PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.2.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.2.0
2.2.0 2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 All 43 releases
← All changes | app/Models/Board.php +78 -17 1.95.2 → 2.2.0 View file →
@@ -5,9 +5,9 @@
5 5 use FluentBoards\App\Services\Constant;
6 6 use FluentBoards\App\Services\PermissionManager;
7 7 use FluentBoards\App\Services\UserService;
8 8 use FluentBoards\Framework\Database\Orm\Builder;
9 -use FluentBoardsPro\App\Models\CustomField;
9 +use FluentBoards\App\Models\CustomField;
10 10
11 11 class Board extends Model
12 12 {
13 13 protected $table = 'fbs_boards';
@@ -16,8 +16,10 @@
16 16
17 17 protected $hidden = [
18 18 'created_at',
19 19 'updated_at',
20 + 'boardMeta',
21 + 'viewerMembership',
20 22 ];
21 23
22 24 protected $fillable = [
23 25 'parent_id',
@@ -32,14 +34,17 @@
32 34 ];
33 35
34 36 protected $appends = ['meta', 'isUserOnlyViewer'];
35 37
38 + /**
39 + * Boot board defaults while preserving empty backgrounds for newly created boards.
40 + */
36 41 public static function boot()
37 42 {
38 43 static::creating(function ($model) {
39 44 $model->created_by = $model->created_by ?: get_current_user_id();
40 45 $model->type = $model->type ?: 'to-do'; // default board type is to-do
41 - $model->background = $model->background ?: $model->randomBackground();
46 + $model->background = $model->background ?: '';
42 47 });
43 48 /* global scope for board type which means only type = to-do will be fetched from everywhere */
44 49 parent::boot();
45 50 static::addGlobalScope('type', function (Builder $builder) {
@@ -78,11 +83,34 @@
78 83 }
79 84
80 85 public function getMetaAttribute()
81 86 {
82 - return $this->getMeta();
87 + if (!$this->relationLoaded('boardMeta')) {
88 + return $this->getMeta();
89 + }
90 +
91 + $meta = [];
92 + foreach ($this->getRelation('boardMeta') as $item) {
93 + $meta[$item->key] = $item->value;
94 + }
95 + return $meta;
83 96 }
84 97
98 + /** Internal eager-loaded metadata for board-list serialization. */
99 + public function boardMeta()
100 + {
101 + return $this->hasMany(Meta::class, 'object_id')
102 + ->where('object_type', Constant::OBJECT_TYPE_BOARD);
103 + }
104 +
105 + /** Internal membership snapshot for the current board-list reader. */
106 + public function viewerMembership()
107 + {
108 + return $this->hasOne(Relation::class, 'object_id')
109 + ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
110 + ->where('foreign_id', get_current_user_id());
111 + }
112 +
85 113 public function setBackgroundAttribute($background)
86 114 {
87 115 $this->attributes['background'] = \maybe_serialize($background);
88 116 }
@@ -216,21 +244,49 @@
216 244 }
217 245
218 246 return $query->whereIn('id', $boardIds);
219 247 }
220 - private function randomBackground()
248 +
249 + /**
250 + * Exclude template boards (settings['is_template'] === true).
251 + * The settings column is serialized text, so both the PHP-serialized
252 + * and JSON representations of the flag are matched.
253 + */
254 + public function scopeExcludeTemplates($query)
221 255 {
222 - $solids = Constant::BOARD_BACKGROUND_DEFAULT_SOLID_COLORS;
223 - $solid = $solids[wp_rand(0, count($solids) - 1)];
256 + return $query->where(function ($query) {
257 + $query->whereNull('settings')
258 + ->orWhere(function ($subQuery) {
259 + $subQuery->where('settings', 'NOT LIKE', '%"is_template";b:1%')
260 + ->where('settings', 'NOT LIKE', '%"is_template":true%');
261 + });
262 + });
263 + }
224 264
225 - return [
226 - 'id' => $solid['id'],
227 - 'is_image' => false,
228 - 'image_url' => null,
229 - 'color' => $solid['value']
230 - ];
265 + /**
266 + * Limit boards to types supported by the currently loaded plugins.
267 + */
268 + public function scopeAvailableInCurrentInstall($query)
269 + {
270 + if (!defined('FLUENT_ROADMAP')) {
271 + return $query->where('type', 'to-do');
272 + }
273 +
274 + return $query;
231 275 }
232 276
277 + /**
278 + * Only template boards (inverse of scopeExcludeTemplates).
279 + */
280 + public function scopeOnlyTemplates($query)
281 + {
282 + return $query->whereNotNull('settings')
283 + ->where(function ($subQuery) {
284 + $subQuery->where('settings', 'LIKE', '%"is_template";b:1%')
285 + ->orWhere('settings', 'LIKE', '%"is_template":true%');
286 + });
287 + }
288 +
233 289 public function getUsers()
234 290 {
235 291 return (new UserService())->allFluentBoardsUsers($this->id);
236 292 }
@@ -270,8 +326,9 @@
270 326 }
271 327
272 328 public function updateMeta($key, $value)
273 329 {
330 + $this->unsetRelation('boardMeta');
274 331 $meta = Meta::where('object_id', $this->id)
275 332 ->where('object_type', Constant::OBJECT_TYPE_BOARD)
276 333 ->where('key', $key)
277 334 ->first();
@@ -304,12 +361,16 @@
304 361 $userId = get_current_user_id();
305 362 if(PermissionManager::isAdmin()) {
306 363 return false;
307 364 }
308 - $boardPermissions = Relation::where('object_id', $this->id)
309 - ->where('foreign_id', $userId)
310 - ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
311 - ->first();
365 + $boardPermissions = $this->relationLoaded('viewerMembership') ? $this->getRelation('viewerMembership') : null;
366 + // Never reuse another user's eagerly loaded membership.
367 + if (!$boardPermissions || (int) $boardPermissions->foreign_id !== (int) $userId) {
368 + $boardPermissions = Relation::where('object_id', $this->id)
369 + ->where('foreign_id', $userId)
370 + ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
371 + ->first();
372 + }
312 373
313 374 return $boardPermissions->settings['is_viewer_only'] ?? false;
314 375 }
315 376
@@ -314,9 +375,9 @@
314 375 }
315 376
316 377 public function removeBoardFromFolder()
317 378 {
318 - $relation = Relation::where('object_type', 'FluentBoardsPro\App\Models\Folder')
379 + $relation = Relation::where('object_type', Constant::OBJECT_TYPE_FOLDER_BOARD)
319 380 ->where('foreign_id', $this->id)
320 381 ->first();
321 382
322 383 if (!$relation) {