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 +173 -20 1.12 → 2.2.0 View file →
@@ -1,14 +1,13 @@
1 1 <?php
2 2
3 3 namespace FluentBoards\App\Models;
4 4
5 -use FluentBoards\App\Services\BoardService;
6 5 use FluentBoards\App\Services\Constant;
7 6 use FluentBoards\App\Services\PermissionManager;
8 7 use FluentBoards\App\Services\UserService;
9 8 use FluentBoards\Framework\Database\Orm\Builder;
10 -use FluentBoards\Framework\Support\Arr;
9 +use FluentBoards\App\Models\CustomField;
11 10
12 11 class Board extends Model
13 12 {
14 13 protected $table = 'fbs_boards';
@@ -17,8 +16,10 @@
17 16
18 17 protected $hidden = [
19 18 'created_at',
20 19 'updated_at',
20 + 'boardMeta',
21 + 'viewerMembership',
21 22 ];
22 23
23 24 protected $fillable = [
24 25 'parent_id',
@@ -31,20 +32,24 @@
31 32 'created_by',
32 33 'archived_at',
33 34 ];
34 35
36 + protected $appends = ['meta', 'isUserOnlyViewer'];
37 +
38 + /**
39 + * Boot board defaults while preserving empty backgrounds for newly created boards.
40 + */
35 41 public static function boot()
36 42 {
37 43 static::creating(function ($model) {
38 44 $model->created_by = $model->created_by ?: get_current_user_id();
39 45 $model->type = $model->type ?: 'to-do'; // default board type is to-do
40 - $model->background = $model->background ?: $model->randomBackground();
46 + $model->background = $model->background ?: '';
41 47 });
42 48 /* global scope for board type which means only type = to-do will be fetched from everywhere */
43 49 parent::boot();
44 50 static::addGlobalScope('type', function (Builder $builder) {
45 - $builder = $builder->whereNull('archived_at');
46 - $builder->where('type', '=', 'to-do')
51 + $builder = $builder->where('type', '=', 'to-do')
47 52 ->orWhere('type', '=', 'roadmap');
48 53 });
49 54 }
50 55
@@ -76,8 +81,36 @@
76 81 {
77 82 return \maybe_unserialize($settings);
78 83 }
79 84
85 + public function getMetaAttribute()
86 + {
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;
96 + }
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 +
80 113 public function setBackgroundAttribute($background)
81 114 {
82 115 $this->attributes['background'] = \maybe_serialize($background);
83 116 }
@@ -95,16 +128,12 @@
95 128 public function completedTasks()
96 129 {
97 130 return $this->hasMany(Task::class, 'board_id')
98 131 ->whereNull('archived_at')
132 + ->where('parent_id', null)
99 133 ->where('status', 'closed');
100 134 }
101 135
102 - public function boardTerm()
103 - {
104 - return $this->hasMany(BoardTerm::class, 'board_id');
105 - }
106 -
107 136 public function stages()
108 137 {
109 138 return $this->hasMany(Stage::class, 'board_id')
110 139 ->whereNull('archived_at')
@@ -124,9 +153,9 @@
124 153 User::class,
125 154 'fbs_relations',
126 155 'object_id',
127 156 'foreign_id'
128 - )->withPivot('settings')
157 + )->withPivot('settings','preferences')
129 158 ->wherePivot('object_type', Constant::OBJECT_TYPE_BOARD_USER)
130 159 ->withTimestamps();
131 160 }
132 161
@@ -215,23 +244,147 @@
215 244 }
216 245
217 246 return $query->whereIn('id', $boardIds);
218 247 }
219 - 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)
220 255 {
221 - $solids = Constant::BOARD_BACKGROUND_DEFAULT_SOLID_COLORS;
222 - $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 + }
223 264
224 - return [
225 - 'id' => $solid['id'],
226 - 'is_image' => false,
227 - 'image_url' => null,
228 - 'color' => $solid['value']
229 - ];
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;
230 275 }
231 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 +
232 289 public function getUsers()
233 290 {
234 291 return (new UserService())->allFluentBoardsUsers($this->id);
292 + }
293 +
294 + public function customFields()
295 + {
296 + return $this->hasMany(CustomField::class, 'board_id')
297 + ->whereNull('archived_at')
298 + ->orderBy('position', 'asc');
299 + }
300 +
301 + public function getMeta() // get Board Meta only
302 + {
303 + $meta = Meta::where('object_id', $this->id)
304 + ->where('object_type', Constant::OBJECT_TYPE_BOARD)
305 + ->get();
306 +
307 + $formattedMeta = [];
308 +
309 + foreach ($meta as $m) {
310 + $formattedMeta[$m->key] = $m->value;
311 + }
312 +
313 + return $formattedMeta;
314 + }
315 +
316 +
317 +
318 + public function getMetaByKey($key) // get Board Meta only
319 + {
320 + $meta = Meta::where('object_id', $this->id)
321 + ->where('object_type', Constant::OBJECT_TYPE_BOARD)
322 + ->where('key', $key)
323 + ->first();
324 +
325 + return $meta->value ?? null;
326 + }
327 +
328 + public function updateMeta($key, $value)
329 + {
330 + $this->unsetRelation('boardMeta');
331 + $meta = Meta::where('object_id', $this->id)
332 + ->where('object_type', Constant::OBJECT_TYPE_BOARD)
333 + ->where('key', $key)
334 + ->first();
335 +
336 + if ($meta) {
337 + $meta->value = $value;
338 + $meta->save();
339 + } else {
340 + $meta = Meta::create([
341 + 'object_id' => $this->id,
342 + 'object_type' => Constant::OBJECT_TYPE_BOARD,
343 + 'key' => $key,
344 + 'value' => $value,
345 + ]);
346 + }
347 +
348 + return $meta;
349 +
350 + }
351 +
352 +
353 + public function activities()
354 + {
355 + return $this->hasMany(Activity::class, 'object_id')
356 + ->where('object_type', Constant::ACTIVITY_BOARD);
357 + }
358 +
359 + public function getisUserOnlyViewerAttribute()
360 + {
361 + $userId = get_current_user_id();
362 + if(PermissionManager::isAdmin()) {
363 + return false;
364 + }
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 + }
373 +
374 + return $boardPermissions->settings['is_viewer_only'] ?? false;
375 + }
376 +
377 + public function removeBoardFromFolder()
378 + {
379 + $relation = Relation::where('object_type', Constant::OBJECT_TYPE_FOLDER_BOARD)
380 + ->where('foreign_id', $this->id)
381 + ->first();
382 +
383 + if (!$relation) {
384 + return;
385 + }
386 + $relation->delete();
387 +
235 388 }
236 389
237 390 }