PluginProbe
UpStream: a Project Management Plugin for WordPress / 1.39.3
UpStream: a Project Management Plugin for WordPress v1.39.3
trunk 1.39.0 1.39.1 1.39.2 1.39.3 2.0.7 2.1.0
upstream / includes / model / UpStream_Model_Project.php

UpStream_Model_Project.php in UpStream: a Project Management Plugin for WordPress 1.39.3, at includes/model/UpStream_Model_Project.php

545 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 // Exit if accessed directly
5 if ( ! defined('ABSPATH')) {
6 exit;
7 }
8
9 class UpStream_Model_Project extends UpStream_Model_Post_Object
10 {
11 protected $tasks = [];
12
13 protected $bugs = [];
14
15 protected $files = [];
16
17 protected $startDate = null;
18
19 protected $endDate = null;
20
21 protected $clientUserIds = [];
22
23 protected $memberUserIds = [];
24
25 protected $clientId = 0;
26
27 protected $progress = 0;
28
29 protected $statusCode = null;
30
31 protected $postType = 'project';
32
33 protected $type = UPSTREAM_ITEM_TYPE_PROJECT;
34
35 /**
36 * UpStream_Model_Project constructor.
37 */
38 public function __construct($id)
39 {
40 if ($id > 0) {
41 parent::__construct($id, [
42 'clientUserIds' => function ($m) {
43 $arr = isset($m['_upstream_project_client_users'][0]) ? unserialize($m['_upstream_project_client_users'][0]) : null;
44 $arr = is_array($arr) ? $arr : [];
45 $arr = array_filter($arr);
46 return $arr;
47 },
48 'memberUserIds' => function ($m) {
49 $arr = isset($m['_upstream_project_members'][0]) ? unserialize($m['_upstream_project_members'][0]) : null;
50 $arr = is_array($arr) ? $arr : [];
51 $arr = array_filter($arr);
52 return $arr;
53 },
54 'clientId' => '_upstream_project_client',
55 'progress' => '_upstream_project_progress',
56 'statusCode' => '_upstream_project_status',
57 'description' => '_upstream_project_description',
58 'startDate' => function ($m) {
59 if (!empty($m['_upstream_project_start.YMD'][0]))
60 return $m['_upstream_project_start.YMD'][0];
61 elseif (!empty($m['_upstream_project_start'][0]))
62 return UpStream_Model_Object::timestampToYMD($m['_upstream_project_start'][0]);
63 },
64 'endDate' => function ($m) {
65 if (!empty($m['_upstream_project_end.YMD'][0]))
66 return $m['_upstream_project_end.YMD'][0];
67 elseif (!empty($m['_upstream_project_end'][0]))
68 return UpStream_Model_Object::timestampToYMD($m['_upstream_project_end'][0]);
69 },
70 'assignedTo' => function ($m) {
71 return !empty($m['_upstream_project_owner'][0]) ? $m['_upstream_project_owner'] : [];
72 },
73 ]);
74
75 $this->loadChildren();
76 $this->loadCategories();
77 } else {
78 parent::__construct(0, []);
79 }
80
81 $this->type = UPSTREAM_ITEM_TYPE_PROJECT;
82 }
83
84 protected function loadChildren()
85 {
86 // TODO: check if these are disabled
87 $itemset = get_post_meta($this->id, '_upstream_project_tasks');
88 if ($itemset && count($itemset) == 1 && is_array($itemset[0])) {
89 foreach ($itemset[0] as $item) {
90 $this->tasks[] = new UpStream_Model_Task($this, $item);
91 }
92 }
93
94 $itemset = get_post_meta($this->id, '_upstream_project_bugs');
95 if ($itemset && count($itemset) == 1 && is_array($itemset[0])) {
96 foreach ($itemset[0] as $item) {
97 $this->bugs[] = new UpStream_Model_Bug($this, $item);
98 }
99 }
100
101 $itemset = get_post_meta($this->id, '_upstream_project_files');
102 if ($itemset && count($itemset) == 1 && is_array($itemset[0])) {
103 foreach ($itemset[0] as $item) {
104 $this->files[] = new UpStream_Model_File($this, $item);
105 }
106 }
107 }
108
109
110 protected function loadCategories()
111 {
112 if (is_project_categorization_disabled()) {
113 return [];
114 }
115
116 $categories = wp_get_object_terms($this->id, 'project_category');
117
118 $categoryIds = [];
119 if (!isset($categories->errors)) {
120 foreach ($categories as $category) {
121 $categoryIds[] = $category->term_id;
122 }
123 }
124
125 $this->categoryIds = $categoryIds;
126 }
127
128 public function calculateElapsedTime()
129 {
130 $total = 0;
131
132 foreach ($this->tasks as $task) {
133 $total += $task->calculateElapsedTime();
134 }
135
136 foreach ($this->bugs as $bug) {
137 $total += $bug->calculateElapsedTime();
138 }
139
140 return $total;
141 }
142
143 public function calculateBudgeted()
144 {
145 $total = 0;
146
147 foreach ($this->tasks as $task) {
148 $total += $task->calculateBudgeted();
149 }
150
151 foreach ($this->bugs as $bug) {
152 $total += $bug->calculateBudgeted();
153 }
154
155 return $total;
156 }
157
158 public function calculateSpent()
159 {
160 $total = 0;
161
162 foreach ($this->tasks as $task) {
163 $total += $task->calculateSpent();
164 }
165
166 foreach ($this->bugs as $bug) {
167 $total += $bug->calculateSpent();
168 }
169
170 return $total;
171 }
172
173 protected function storeCategories()
174 {
175 if (is_project_categorization_disabled()) {
176 return;
177 }
178
179 $res = wp_set_object_terms($this->id, $this->categoryIds, 'project_category');
180
181 if ($res instanceof \WP_Error) {
182 // TODO: throw
183 }
184
185 }
186
187 public function store()
188 {
189 parent::store();
190
191 if ($this->clientId > 0) update_post_meta($this->id, '_upstream_project_client', $this->clientId);
192 if ($this->clientUserIds != null) update_post_meta($this->id, '_upstream_project_client_users', $this->clientUserIds);
193 if ($this->statusCode != null) update_post_meta($this->id, '_upstream_project_status', $this->statusCode);
194 if ($this->description != null) update_post_meta($this->id, '_upstream_project_description', $this->description);
195 if (count($this->assignedTo) > 0) update_post_meta($this->id, '_upstream_project_owner', $this->assignedTo[0]);
196 if ($this->startDate != null) update_post_meta($this->id, '_upstream_project_start.YMD', $this->startDate);
197 if ($this->endDate != null) update_post_meta($this->id, '_upstream_project_end.YMD', $this->endDate);
198 if ($this->startDate != null) update_post_meta($this->id, '_upstream_project_start', UpStream_Model_Object::ymdToTimestamp($this->startDate));
199 if ($this->endDate != null) update_post_meta($this->id, '_upstream_project_end', UpStream_Model_Object::ymdToTimestamp($this->endDate));
200 if ($this->progress != null) update_post_meta($this->id, '_upstream_project_progress', $this->progress);
201
202 $items = [];
203 foreach ($this->tasks as $item) {
204 $r = [];
205 $item->storeToArray($r);
206 $items[] = $r;
207 }
208 update_post_meta($this->id, '_upstream_project_tasks', $items);
209
210 $items = [];
211 foreach ($this->bugs as $item) {
212 $r = [];
213 $item->storeToArray($r);
214 $items[] = $r;
215 }
216 update_post_meta($this->id, '_upstream_project_bugs', $items);
217
218 $items = [];
219 foreach ($this->files as $item) {
220 $r = [];
221 $item->storeToArray($r);
222 $items[] = $r;
223 }
224 update_post_meta($this->id, '_upstream_project_files', $items);
225
226 $this->storeCategories();
227
228 $projectObject = new UpStream_Project($this->id);
229 $projectObject->update_project_meta();
230 }
231
232 public function hasMetaObject($item)
233 {
234 if (!($item instanceof \UpStream_Model_Meta_Object))
235 throw new UpStream_Model_ArgumentException(__('Argument must be of type UpStream_Model_Meta_Object', 'upstream'));
236 elseif ($item instanceof UpStream_Model_Task) {
237 foreach ($this->tasks() as $task) {
238 if ($task->id === $item->id)
239 return true;
240 }
241 }
242 elseif ($item instanceof UpStream_Model_File) {
243 foreach ($this->files() as $file) {
244 if ($file->id === $item->id)
245 return true;
246 }
247 }
248 elseif ($item instanceof UpStream_Model_Bug) {
249 foreach ($this->bugs() as $bug) {
250 if ($bug->id === $item->id)
251 return true;
252 }
253 }
254
255 return false;
256 }
257
258 public function addMetaObject($item)
259 {
260 if (!($item instanceof \UpStream_Model_Meta_Object))
261 throw new UpStream_Model_ArgumentException(__('Can only add objects of type UpStream_Model_Meta_Object', 'upstream'));
262 elseif ($item instanceof UpStream_Model_Task)
263 $this->tasks[] = $item;
264 elseif ($item instanceof UpStream_Model_File)
265 $this->files[] = $item;
266 elseif ($item instanceof UpStream_Model_Bug)
267 $this->bugs[] = $item;
268 }
269
270 public function &tasks() {
271 return $this->tasks;
272 }
273
274 public function &bugs() {
275 return $this->bugs;
276 }
277
278 public function &files() {
279 return $this->files;
280 }
281
282 public function &addTask($title, $createdBy)
283 {
284 $item = \UpStream_Model_Task::create($this, $title, $createdBy);
285 $this->tasks[] = $item;
286
287 return $item;
288 }
289
290 public function &addBug($title, $createdBy)
291 {
292 $item = \UpStream_Model_Bug::create($this, $title, $createdBy);
293 $this->bugs[] = $item;
294
295 return $item;
296 }
297
298 public function &addFile($title, $createdBy)
299 {
300 $item = \UpStream_Model_File::create($this, $title, $createdBy);
301 $this->files[] = $item;
302
303 return $item;
304 }
305
306 public function __get($property)
307 {
308 switch ($property) {
309
310 case 'status':
311 $s = $this->getStatuses();
312
313 foreach ($s as $sKey => $sValue) {
314 if ($this->statusCode === $sKey)
315 return $sValue;
316 }
317 return '';
318
319 case 'elapsedTime':
320 return $this->calculateElapsedTime();
321 case 'budgeted':
322 return $this->calculateBudgeted();
323 case 'spent':
324 return $this->calculateSpent();
325
326 case 'statusCode':
327 case 'clientId':
328 case 'clientUserIds':
329 case 'memberUserIds':
330 case 'startDate':
331 case 'endDate':
332 case 'categoryIds':
333 return $this->{$property};
334
335 case 'progress':
336 return round($this->{$property});
337
338 case 'categories':
339 $categories = [];
340 foreach ($this->categoryIds as $tid) {
341 $term = get_term_by('id', $tid, 'project_category');
342 $categories[] = $term;
343 }
344 return $categories;
345
346 case 'tasks':
347 case 'bugs':
348 case 'files':
349 throw new UpStream_Model_ArgumentException(__('Not implemented. Use &tasks(), &files(), or &bugs().', 'upstream'));
350 default:
351 return parent::__get($property);
352
353 }
354 }
355
356 public function __set($property, $value)
357 {
358 switch ($property) {
359
360 case 'categoryIds':
361 if (!is_array($value))
362 $value = [$value];
363
364 $categoryIds = [];
365 foreach ($value as $tid) {
366 $term = get_term_by('id', $tid, 'project_category');
367 if ($term === false)
368 throw new UpStream_Model_ArgumentException(sprintf(__('Term ID %s is invalid.', 'upstream'), $tid));
369 $categoryIds[] = $term->term_id;
370 }
371
372 $this->categoryIds = $categoryIds;
373
374 break;
375
376 case 'status':
377 $s = $this->getStatuses();
378 $sc = null;
379
380 foreach ($s as $sKey => $sValue) {
381 if ($value === $sValue) {
382 $sc = $sKey;
383 break;
384 }
385 }
386
387 if ($sc == null)
388 throw new UpStream_Model_ArgumentException(sprintf(__('Status %s is invalid.', 'upstream'), $value));
389
390 $this->statusCode = $sc;
391
392 break;
393
394 case 'statusCode':
395 $s = $this->getStatuses();
396 $sc = null;
397
398 foreach ($s as $sKey => $sValue) {
399 if ($value === $sKey) {
400 $sc = $sKey;
401 break;
402 }
403 }
404
405 if ($sc == null)
406 throw new UpStream_Model_ArgumentException(sprintf(__('Status code %s is invalid.', 'upstream'), $value));
407
408 $this->statusCode = $sc;
409
410 break;
411
412 case 'assignedTo':
413 case 'assignedTo:byUsername':
414 case 'assignedTo:byEmail':
415 if (is_array($value) && count($value) != 1)
416 throw new UpStream_Model_ArgumentException(__('For projects, assignedTo must be an array of length 1.', 'upstream'));
417
418 parent::__set($property, $value);
419 break;
420
421 case 'clientId':
422 // this will throw a model exception if the client doesn't exist
423 $client = \UpStream_Model_Manager::get_instance()->getByID(UPSTREAM_ITEM_TYPE_CLIENT, $value);
424 $this->clientId = $client->id;
425 break;
426
427 case 'clientUserIds':
428 if ($this->clientId == 0)
429 throw new UpStream_Model_ArgumentException(__('Cannot assign client users if the project has no client.', 'upstream'));
430
431 if (!is_array($value))
432 throw new UpStream_Model_ArgumentException(__('Client user IDs must be an array.', 'upstream'));
433
434 if (count(array_unique($value)) != count($value))
435 throw new UpStream_Model_ArgumentException(__('Input cannot contain duplicates.', 'upstream'));
436
437 $client = \UpStream_Model_Manager::get_instance()->getByID(UPSTREAM_ITEM_TYPE_CLIENT, $this->clientId);
438 for ($i = 0; $i < count($value); $i++) {
439 if (!$client->includesUser($value[$i]))
440 throw new UpStream_Model_ArgumentException(sprintf(__('User ID %s does not exist in this client.', 'upstream'), $value[$i]));
441 }
442 $this->clientUserIds = $value;
443
444 break;
445
446 case 'startDate':
447 case 'endDate':
448 if (!self::isValidDate($value))
449 throw new UpStream_Model_ArgumentException(__('Argument is not a valid date of the form YYYY-MM-DD.', 'upstream'));
450
451 $this->{$property} = $value;
452 break;
453
454 default:
455 parent::__set($property, $value);
456 break;
457
458 }
459 }
460
461 public static function fields()
462 {
463 $fields = parent::fields();
464
465 $fields['statusCode'] = [ 'type' => 'select', 'title' => __('Status'), 'search' => true, 'display' => true, 'options_cb' => 'UpStream_Model_Project::getStatuses' ];
466 $fields['categoryIds'] = [ 'type' => 'select', 'title' => __('Categories'), 'search' => true, 'display' => true, 'options_cb' => 'UpStream_Model_Project::getCategories', 'is_array' => 'true' ];
467 $fields['clientUserIds'] = [ 'type' => 'user_id', 'is_array' => true, 'title' => __('Selected Client Users'), 'search' => true, 'display' => true ];
468 $fields['memberUserIds'] = [ 'type' => 'user_id', 'is_array' => true, 'title' => __('Members'), 'search' => true, 'display' => true ];
469 $fields['startDate'] = [ 'type' => 'date', 'title' => __('Start Date'), 'search' => true, 'display' => true ];
470 $fields['endDate'] = [ 'type' => 'date', 'title' => __('End Date'), 'search' => true, 'display' => true ];
471 $fields['progress'] = [ 'type' => 'number', 'title' => __('Progress (%)'), 'search' => true, 'display' => true ];
472
473 $fields = self::customFields($fields, UPSTREAM_ITEM_TYPE_PROJECT);
474
475 return $fields;
476 }
477
478
479 public function findMilestones()
480 {
481 $posts = get_posts(
482 [
483 'post_type' => 'upst_milestone',
484 'post_status' => 'publish',
485 'posts_per_page' => -1,
486 'meta_key' => 'upst_project_id',
487 'meta_value' => $this->id,
488 'orderby' => 'menu_order',
489 'order' => 'ASC',
490 ]
491 );
492
493 $milestones = [];
494
495 foreach ($posts as $post) {
496 $milestone = \UpStream_Model_Manager::get_instance()->getByID(UPSTREAM_ITEM_TYPE_MILESTONE,
497 $post->ID, UPSTREAM_ITEM_TYPE_PROJECT, $this->id);
498 $milestones[] = $milestone;
499 }
500
501 return $milestones;
502 }
503
504 public static function getCategories()
505 {
506 $tid_to_term = [];
507 $terms = get_terms(array('taxonomy' => 'project_category', 'hide_empty' => true));
508
509 foreach ($terms as $term) {
510 $tid_to_term[$term->term_id] = $term->name;
511 }
512
513 return $tid_to_term;
514 }
515
516 public static function getStatuses()
517 {
518 $option = get_option('upstream_projects');
519 $statuses = isset($option['statuses']) ? $option['statuses'] : '';
520 $array = [];
521 if ($statuses) {
522 foreach ($statuses as $status) {
523 if (isset($status['type'])) {
524 $array[$status['id']] = $status['name'];
525 }
526 }
527 }
528
529 return $array;
530 }
531
532 public static function create($title, $createdBy)
533 {
534 if (get_userdata($createdBy) === false)
535 throw new UpStream_Model_ArgumentException(__('User ID does not exist.', 'upstream'));
536
537 $item = new \UpStream_Model_Project(0);
538
539 $item->title = sanitize_text_field($title);
540 $item->createdBy = $createdBy;
541
542 return $item;
543 }
544
545 }