Complex classes like Tracker_Artifact_Changeset often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Tracker_Artifact_Changeset, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
26 | class Tracker_Artifact_Changeset extends Tracker_Artifact_Followup_Item { |
||
27 | const DEFAULT_MAIL_SENDER = 'forge__artifacts'; |
||
28 | |||
29 | const FIELDS_ALL = 'all'; |
||
30 | const FIELDS_COMMENTS = 'comments'; |
||
31 | |||
32 | public $id; |
||
33 | public $artifact; |
||
34 | public $submitted_by; |
||
35 | public $submitted_on; |
||
36 | public $email; |
||
37 | |||
38 | protected $values; |
||
39 | |||
40 | /** |
||
41 | * @var Tracker_Artifact_Changeset_Comment |
||
42 | */ |
||
43 | private $latest_comment; |
||
44 | |||
45 | /** |
||
46 | * Constructor |
||
47 | * |
||
48 | * @param int $id The changeset Id |
||
49 | * @param Tracker_Artifact $artifact The artifact |
||
50 | * @param int $submitted_by The id of the owner of this changeset |
||
51 | * @param int $submitted_on The timestamp |
||
52 | * @param string $email The email of the submitter if anonymous mode |
||
53 | */ |
||
54 | public function __construct($id, $artifact, $submitted_by, $submitted_on, $email) { |
||
55 | $this->id = $id; |
||
56 | $this->artifact = $artifact; |
||
57 | $this->submitted_by = $submitted_by; |
||
58 | $this->submitted_on = $submitted_on; |
||
59 | $this->email = $email; |
||
60 | } |
||
61 | |||
62 | /** |
||
63 | * Return the value of a field in the current changeset |
||
64 | * |
||
65 | * @param Tracker_FormElement_Field $field The field |
||
66 | * |
||
67 | * @return Tracker_Artifact_ChangesetValue, or null if not found |
||
|
|||
68 | */ |
||
69 | public function getValue(Tracker_FormElement_Field $field) { |
||
70 | if (! isset($this->values[$field->getId()])) { |
||
71 | $this->values[$field->getId()] = $this->getChangesetValueFromDB($field); |
||
72 | } |
||
73 | return $this->values[$field->getId()]; |
||
74 | } |
||
75 | |||
76 | private function getChangesetValueFromDB(Tracker_FormElement_Field $field) { |
||
77 | $dar = $this->getValueDao()->searchByFieldId($this->id, $field->getId()); |
||
78 | if ($dar && count($dar)) { |
||
79 | $row = $dar->getRow(); |
||
80 | return $field->getChangesetValue($this, $row['id'], $row['has_changed']); |
||
81 | } |
||
82 | return null; |
||
83 | } |
||
84 | |||
85 | public function setFieldValue(Tracker_FormElement_Field $field, Tracker_Artifact_ChangesetValue $value = null) { |
||
88 | |||
89 | /** |
||
90 | * Returns the submission date of this changeset (timestamp) |
||
91 | * |
||
92 | * @return int The submission date of this changeset (timestamp) |
||
93 | */ |
||
94 | public function getSubmittedOn() { |
||
97 | |||
98 | /** |
||
99 | * Returns the author of this changeset |
||
100 | * |
||
101 | * @return int The user id or 0/null if anonymous |
||
102 | */ |
||
103 | public function getSubmittedBy() { |
||
106 | |||
107 | /** |
||
108 | * Returns the author's email of this changeset |
||
109 | * |
||
110 | * @return string an email |
||
111 | */ |
||
112 | public function getEmail() { |
||
115 | |||
116 | /** |
||
117 | * Return the changeset values of this changeset |
||
118 | * |
||
119 | * @return Tracker_Artifact_ChangesetValue[] or empty array if not found |
||
120 | */ |
||
121 | public function getValues() { |
||
122 | if (! $this->values) { |
||
123 | $this->forceFetchAllValues(); |
||
124 | } |
||
125 | return $this->values; |
||
126 | } |
||
127 | |||
128 | public function forceFetchAllValues() { |
||
129 | $this->values = array(); |
||
130 | $factory = $this->getFormElementFactory(); |
||
131 | foreach ($this->getValueDao()->searchById($this->id) as $row) { |
||
132 | if ($field = $factory->getFieldById($row['field_id'])) { |
||
133 | $this->values[$field->getId()] = $field->getChangesetValue($this, $row['id'], $row['has_changed']); |
||
134 | } |
||
135 | } |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Delete the changeset |
||
140 | * |
||
141 | * @param PFUser $user the user who wants to delete the changeset |
||
142 | * |
||
143 | * @return void |
||
144 | */ |
||
145 | public function delete(PFUser $user) { |
||
146 | if ($this->userCanDeletePermanently($user)) { |
||
147 | $this->getChangesetDao()->delete($this->id); |
||
148 | $this->getCommentDao()->delete($this->id); |
||
149 | $this->deleteValues(); |
||
150 | } |
||
151 | } |
||
152 | |||
153 | protected function deleteValues() { |
||
154 | $value_dao = $this->getValueDao(); |
||
155 | $factory = $this->getFormElementFactory(); |
||
156 | foreach ($value_dao->searchById($this->id) as $row) { |
||
157 | if ($field = $factory->getFieldById($row['field_id'])) { |
||
158 | $field->deleteChangesetValue($row['id']); |
||
159 | } |
||
160 | } |
||
161 | $value_dao->delete($this->id); |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Returns the ValueDao |
||
166 | * |
||
167 | * @return Tracker_Artifact_Changeset_ValueDao The dao |
||
168 | */ |
||
169 | protected function getValueDao() { |
||
172 | |||
173 | /** |
||
174 | * Returns the Form Element Factory |
||
175 | * |
||
176 | * @return Tracker_FormElementFactory The factory |
||
177 | */ |
||
178 | protected function getFormElementFactory() { |
||
181 | |||
182 | public function getFollowUpDate() { |
||
185 | |||
186 | public function getFollowupContent() { |
||
187 | $html = ''; |
||
188 | |||
189 | //The comment |
||
190 | if ($comment = $this->getComment()) { |
||
191 | $html .= '<div class="tracker_artifact_followup_comment">'; |
||
192 | $html .= $comment->fetchFollowUp(); |
||
193 | $html .= '</div>'; |
||
194 | |||
195 | if ($comment->fetchFollowUp() && $this->diffToPrevious()) { |
||
196 | $html .= '<hr size="1" />'; |
||
197 | } |
||
198 | } |
||
199 | |||
200 | //The changes |
||
201 | if ($changes = $this->diffToPrevious()) { |
||
202 | $html .= '<ul class="tracker_artifact_followup_changes">'; |
||
203 | $html .= $changes; |
||
204 | $html .= '</ul>'; |
||
205 | } |
||
206 | |||
207 | return $html; |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * Fetch followup |
||
212 | * |
||
213 | * @return string html |
||
214 | */ |
||
215 | public function fetchFollowUp() { |
||
216 | $html = ''; |
||
217 | |||
218 | $html .= $this->getAvatarIfEnabled(); |
||
219 | |||
220 | $html .= '<div class="tracker_artifact_followup_header">'; |
||
221 | $html .= $this->getPermalink(); |
||
222 | $html .= $this->fetchChangesetActionButtons(); |
||
223 | $html .= $this->getUserLink(); |
||
224 | $html .= $this->getTimeAgo(); |
||
225 | $html .= '</div>'; |
||
226 | |||
227 | // The content |
||
228 | $html .= '<div class="tracker_artifact_followup_content">'; |
||
229 | $html .= $this->getFollowupContent(); |
||
230 | $html .= '</div>'; |
||
231 | |||
232 | $html .= '<div style="clear:both;"></div>'; |
||
233 | return $html; |
||
234 | } |
||
235 | |||
236 | private function fetchChangesetActionButtons() { |
||
237 | $html = ''; |
||
238 | $edit_button = $this->fetchEditButton(); |
||
239 | $mail_button = $this->fetchIncomingMailButton(); |
||
240 | |||
241 | if ($edit_button || $mail_button) { |
||
242 | $html .= '<div class="tracker_artifact_followup_comment_controls">'; |
||
243 | $html .= $mail_button; |
||
244 | $html .= ' '; |
||
245 | $html .= $edit_button; |
||
246 | $html .= '</div>'; |
||
247 | } |
||
248 | |||
249 | return $html; |
||
250 | } |
||
251 | |||
252 | private function fetchEditButton() { |
||
253 | if (! $this->userCanEdit()) { |
||
254 | return ''; |
||
255 | } |
||
256 | |||
257 | $html = ''; |
||
258 | $html .= '<a href="#" class="tracker_artifact_followup_comment_controls_edit">'; |
||
259 | $html .= '<button class="btn btn-mini"><i class="icon-edit"></i> ' . $GLOBALS['Language']->getText('plugin_tracker_fieldeditor', 'edit') . '</button>'; |
||
260 | $html .= '</a>'; |
||
261 | |||
262 | return $html; |
||
263 | } |
||
264 | |||
265 | private function fetchIncomingMailButton() { |
||
266 | if (! $this->getUserManager()->getCurrentUser()->isSuperUser()) { |
||
267 | return ''; |
||
268 | } |
||
269 | |||
270 | $retriever = Tracker_Artifact_Changeset_IncomingMailGoldenRetriever::instance(); |
||
271 | $raw_mail = $retriever->getRawMailThatCreatedChangeset($this); |
||
272 | if (! $raw_mail) { |
||
273 | return ''; |
||
274 | } |
||
275 | |||
276 | $raw_email_button_title = $GLOBALS['Language']->getText('plugin_tracker', 'raw_email_button_title'); |
||
277 | $raw_mail = Codendi_HTMLPurifier::instance()->purify($raw_mail); |
||
278 | |||
279 | $html = '<button type="button" class="btn btn-mini tracker_artifact_followup_comment_controls_raw_email" data-raw-email="'. $raw_mail .'"> |
||
280 | <i class="icon-envelope"></i> '. $raw_email_button_title .' |
||
281 | </button>'; |
||
282 | |||
283 | return $html; |
||
284 | } |
||
285 | |||
286 | public function getImage() { |
||
287 | return $GLOBALS['HTML']->getImage( |
||
288 | 'ic/comment.png', |
||
289 | array( |
||
290 | 'border' => 0, |
||
291 | 'alt' => 'permalink', |
||
292 | 'class' => 'tracker_artifact_followup_permalink', |
||
293 | 'style' => 'vertical-align:middle', |
||
294 | 'title' => 'Link to this followup - #'. (int) $this->id |
||
295 | ) |
||
296 | ); |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * @return PFUser |
||
301 | */ |
||
302 | public function getSubmitter() { |
||
303 | if ($this->submitted_by) { |
||
304 | return UserManager::instance()->getUserById($this->submitted_by); |
||
305 | } else { |
||
306 | $submitter = UserManager::instance()->getUserAnonymous(); |
||
307 | $submitter->setEmail($this->email); |
||
308 | |||
309 | return $submitter; |
||
310 | } |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * @return string html |
||
315 | */ |
||
316 | public function getSubmitterUrl() { |
||
317 | if ($this->submitted_by) { |
||
318 | $submitter = $this->getSubmitter(); |
||
319 | $uh = UserHelper::instance(); |
||
320 | $submitter_url = $uh->getLinkOnUser($submitter); |
||
321 | } else { |
||
322 | $hp = Codendi_HTMLPurifier::instance(); |
||
323 | $submitter_url = $hp->purify($this->email, CODENDI_PURIFIER_BASIC); |
||
324 | } |
||
325 | |||
326 | return $submitter_url; |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * @return string |
||
331 | */ |
||
332 | public function getHTMLAvatar() { |
||
335 | |||
336 | /** |
||
337 | * @return string |
||
338 | */ |
||
339 | public function getAvatarUrl() { |
||
342 | |||
343 | /** |
||
344 | * @return string html |
||
345 | */ |
||
346 | public function getDateSubmittedOn() { |
||
349 | |||
350 | /** |
||
351 | * @return string |
||
352 | */ |
||
353 | public function getFollowUpClassnames() { |
||
354 | $classnames = ''; |
||
355 | |||
356 | $comment = $this->getComment(); |
||
357 | $changes = $this->diffToPrevious(); |
||
358 | |||
359 | if ($changes || $this->shouldBeDisplayedAsChange($changes, $comment)) { |
||
360 | $classnames .= ' tracker_artifact_followup-with_changes '; |
||
361 | } |
||
362 | |||
363 | if ($comment && ! $comment->hasEmptyBody()) { |
||
364 | $classnames .= ' tracker_artifact_followup-with_comment '; |
||
365 | } |
||
366 | |||
367 | if ($this->submitted_by && $this->submitted_by < 100) { |
||
368 | $classnames .= ' tracker_artifact_followup-by_system_user '; |
||
369 | } |
||
370 | |||
371 | return $classnames; |
||
372 | } |
||
373 | |||
374 | |||
375 | // This function is used to cover a bug previously introduced where |
||
376 | // artifacts can be updated without changes nor comment. We want to |
||
377 | // display such changesets as if they were only containing changes, |
||
378 | // so we introduced this function to determine wether we're in this |
||
379 | // case or not. |
||
380 | private function shouldBeDisplayedAsChange($changes, $comment) { |
||
381 | if ($comment) { |
||
382 | // Not comment AND no changes |
||
383 | return $comment->hasEmptyBody() && ! $changes; |
||
384 | } |
||
385 | |||
386 | return true; |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Say if a user can permanently (no restore) delete a changeset |
||
391 | * |
||
392 | * @param PFUser $user The user who does the delete |
||
393 | * |
||
394 | * @return boolean true if the user can delete |
||
395 | */ |
||
396 | protected function userCanDeletePermanently(PFUser $user) { |
||
400 | |||
401 | /** |
||
402 | * Say if a user can delete a changeset |
||
403 | * |
||
404 | * @param PFUser $user The user. If null, the current logged in user will be used. |
||
405 | * |
||
406 | * @return boolean true if the user can delete |
||
407 | */ |
||
408 | protected function userCanDelete(PFUser $user = null) { |
||
409 | if (!$user) { |
||
410 | $user = $this->getUserManager()->getCurrentUser(); |
||
411 | } |
||
412 | // Only tracker admin can edit a comment |
||
413 | return $user->isSuperUser(); |
||
414 | } |
||
415 | |||
416 | /** |
||
417 | * Say if a user can edit a comment |
||
418 | * |
||
419 | * @param PFUser $user The user. If null, the current logged in user will be used. |
||
420 | * |
||
421 | * @return boolean true if the user can edit |
||
422 | */ |
||
423 | public function userCanEdit(PFUser $user = null) { |
||
430 | |||
431 | /** |
||
432 | * Update the content |
||
433 | * |
||
434 | * @param string $body The new content |
||
435 | * @param PFUser $user The user |
||
436 | * @param String $comment_format Format of the comment |
||
437 | * |
||
438 | * @return void |
||
439 | */ |
||
440 | public function updateComment($body, $user, $comment_format, $timestamp) { |
||
441 | if ($this->updateCommentWithoutNotification($body, $user, $comment_format, $timestamp)) { |
||
442 | $this->notify(); |
||
443 | } |
||
444 | } |
||
445 | |||
446 | public function updateCommentWithoutNotification($body, $user, $comment_format, $timestamp) { |
||
447 | if ($this->userCanEdit($user)) { |
||
448 | $commentUpdated = $this->getCommentDao()->createNewVersion( |
||
449 | $this->id, |
||
450 | $body, |
||
451 | $user->getId(), |
||
452 | $timestamp, |
||
453 | $this->getComment()->id, |
||
454 | $comment_format |
||
455 | ); |
||
456 | |||
457 | unset($this->latest_comment); |
||
458 | |||
459 | if ($commentUpdated) { |
||
460 | $reference_manager = $this->getReferenceManager(); |
||
461 | $reference_manager->extractCrossRef( |
||
462 | $body, |
||
463 | $this->artifact->getId(), |
||
464 | Tracker_Artifact::REFERENCE_NATURE, |
||
465 | $this->artifact->getTracker()->getGroupID(), |
||
466 | $user->getId(), |
||
467 | $this->artifact->getTracker()->getItemName() |
||
468 | ); |
||
469 | |||
470 | $params = array('group_id' => $this->getArtifact()->getTracker()->getGroupId(), |
||
471 | 'artifact' => $this->getArtifact(), |
||
472 | 'changeset_id' => $this->getId(), |
||
473 | 'text' => $body); |
||
474 | |||
475 | EventManager::instance()->processEvent('tracker_followup_event_update', $params); |
||
476 | |||
477 | return true; |
||
478 | } |
||
479 | } |
||
480 | |||
481 | return false; |
||
482 | } |
||
483 | |||
484 | /** |
||
485 | * @return ReferenceManager |
||
486 | */ |
||
487 | protected function getReferenceManager() { |
||
490 | |||
491 | /** |
||
492 | * Get the comment (latest version) |
||
493 | * |
||
494 | * @return Tracker_Artifact_Changeset_Comment The comment of this changeset, or null if no comments |
||
495 | */ |
||
496 | public function getComment() { |
||
497 | if (isset($this->latest_comment)) { |
||
498 | return $this->latest_comment; |
||
499 | } |
||
500 | |||
501 | if ($row = $this->getCommentDao()->searchLastVersion($this->id)->getRow()) { |
||
502 | $this->latest_comment = new Tracker_Artifact_Changeset_Comment($row['id'], |
||
503 | $this, |
||
504 | $row['comment_type_id'], |
||
505 | $row['canned_response_id'], |
||
506 | $row['submitted_by'], |
||
507 | $row['submitted_on'], |
||
508 | $row['body'], |
||
509 | $row['body_format'], |
||
510 | $row['parent_id']); |
||
511 | } |
||
512 | return $this->latest_comment; |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * |
||
517 | * @param Tracker_Artifact_Changeset_Comment|-1 $comment |
||
518 | */ |
||
519 | public function setLatestComment($comment) { |
||
522 | |||
523 | /** |
||
524 | * Return the ChangesetDao |
||
525 | * |
||
526 | * @return Tracker_Artifact_ChangesetDao The Dao |
||
527 | */ |
||
528 | protected function getChangesetDao() { |
||
531 | |||
532 | /** |
||
533 | * Returns the comment dao |
||
534 | * |
||
535 | * @return Tracker_Artifact_ChangesetCommentDao The dao |
||
536 | */ |
||
537 | protected function getCommentDao() { |
||
540 | |||
541 | /** |
||
542 | * Returns true if there are changes in fields_data regarding this changeset, false if nothing has changed |
||
543 | * |
||
544 | * @param array $fields_data The data submitted (array of 'field_id' => 'value') |
||
545 | * |
||
546 | * @return boolean true if there are changes in fields_data regarding this changeset, false if nothing has changed |
||
547 | */ |
||
548 | public function hasChanges($fields_data) { |
||
549 | $has_changes = false; |
||
550 | $used_fields = $this->getFormElementFactory()->getUsedFields($this->artifact->getTracker()); |
||
551 | reset($used_fields); |
||
552 | while (!$has_changes && (list(,$field) = each($used_fields))) { |
||
553 | if (!is_a($field, 'Tracker_FormElement_Field_ReadOnly')) { |
||
554 | if (array_key_exists($field->id, $fields_data)) { |
||
555 | $current_value = $this->getValue($field); |
||
556 | if ($current_value) { |
||
557 | $has_changes = $field->hasChanges($current_value, $fields_data[$field->id]); |
||
558 | } else { |
||
559 | //There is no current value in the changeset for the submitted field |
||
560 | //It means that the field has been added afterwards. |
||
561 | //Then consider that there is at least one change (the value of the new field). |
||
562 | $has_changes = true; |
||
563 | } |
||
564 | } |
||
565 | } |
||
566 | } |
||
567 | return $has_changes; |
||
568 | } |
||
569 | |||
570 | /** |
||
571 | * Return mail format diff between this changeset and previous one (HTML code) |
||
572 | * |
||
573 | * @return string The field difference between the previous changeset. or false if no changes |
||
574 | */ |
||
575 | public function mailDiffToPrevious($format = 'html', $user = null, $ignore_perms = false) { |
||
578 | |||
579 | /** |
||
580 | * Return modal format diff between this changeset and previous one (HTML code) |
||
581 | * |
||
582 | * @return string The field difference between the previous changeset. or false if no changes |
||
583 | */ |
||
584 | public function modalDiffToPrevious($format = 'html', $user = null, $ignore_perms = false) { |
||
587 | |||
588 | /** |
||
589 | * Return diff between this changeset and previous one (HTML code) |
||
590 | * |
||
591 | * @return string The field difference between the previous changeset. or false if no changes |
||
592 | */ |
||
593 | public function diffToPrevious($format = 'html', $user = null, $ignore_perms = false, $for_mail = false, $for_modal = false) { |
||
594 | $result = ''; |
||
595 | $factory = $this->getFormElementFactory(); |
||
596 | $previous_changeset = $this->getArtifact()->getPreviousChangeset($this->getId()); |
||
597 | |||
598 | if (! $previous_changeset) { |
||
599 | return $result; |
||
600 | } |
||
601 | |||
602 | foreach ($this->getValues() as $field_id => $current_changeset_value) { |
||
603 | $field = $factory->getFieldById($field_id); |
||
604 | if (! $field) { |
||
605 | continue; |
||
606 | } |
||
607 | |||
608 | if ( (! $ignore_perms && ! $field->userCanRead($user) ) || ! $current_changeset_value) { |
||
609 | continue; |
||
610 | } |
||
611 | |||
612 | if (! $current_changeset_value->hasChanged()) { |
||
613 | continue; |
||
614 | } |
||
615 | |||
616 | $previous_changeset_value = $previous_changeset->getValue($field); |
||
617 | |||
618 | if (! $previous_changeset_value) {//Case : field added later (ie : artifact already exists) => no value |
||
619 | $diff = $current_changeset_value->nodiff(); |
||
620 | } elseif ($for_mail) { |
||
621 | $artifact_id = $this->getArtifact()->getId(); |
||
622 | $changeset_id = $this->getId(); |
||
623 | |||
624 | $diff = $current_changeset_value->mailDiff($previous_changeset_value, $format, $user, $artifact_id, $changeset_id); |
||
625 | } elseif ($for_modal) { |
||
626 | $diff = $current_changeset_value->modalDiff($previous_changeset_value, $format, $user); |
||
627 | } else { |
||
628 | $diff = $current_changeset_value->diff($previous_changeset_value, $format, $user); |
||
629 | } |
||
630 | |||
631 | if ($diff) { |
||
632 | $result .= $this->displayDiff($diff, $format, $field); |
||
633 | } |
||
634 | } |
||
635 | return $result; |
||
636 | } |
||
637 | |||
638 | /** |
||
639 | * Display diff messsage |
||
640 | * |
||
641 | * @param String $diff |
||
642 | * |
||
643 | */ |
||
644 | public function displayDiff($diff, $format, $field) { |
||
645 | $result = false; |
||
646 | switch($format) { |
||
647 | case 'html': |
||
648 | $result .= '<li>'; |
||
649 | $result .= '<span class="tracker_artifact_followup_changes_field"><b>'. $field->getLabel() .'</b></span> '; |
||
650 | $result .= '<span class="tracker_artifact_followup_changes_changes">'. $diff .'</span>'; |
||
651 | $result .= '</li>'; |
||
652 | break; |
||
653 | default://text |
||
654 | $result .= ' * '.$field->getLabel().' : '.PHP_EOL; |
||
655 | $result .= $diff . PHP_EOL; |
||
656 | break; |
||
657 | } |
||
658 | return $result; |
||
659 | } |
||
660 | |||
661 | /** |
||
662 | * Get an instance of UserManager |
||
663 | * |
||
664 | * @return UserManager |
||
665 | */ |
||
666 | public function getUserManager() { |
||
669 | |||
670 | public function getTracker() { |
||
673 | |||
674 | /** |
||
675 | * @protected for testing purpose |
||
676 | */ |
||
677 | protected function getTrackerPluginConfig() { |
||
678 | return new TrackerPluginConfig( |
||
679 | new TrackerPluginConfigDao() |
||
680 | ); |
||
681 | } |
||
682 | |||
683 | /** |
||
684 | * @return ConfigNotificationAssignedTo |
||
685 | */ |
||
686 | private function getTrackerConfigNotificationAssignedTo() { |
||
687 | return new ConfigNotificationAssignedTo( |
||
688 | new ConfigNotificationAssignedToDao() |
||
689 | ); |
||
690 | } |
||
691 | |||
692 | /** |
||
693 | * @return bool |
||
694 | */ |
||
695 | protected function isNotificationAssignedToEnabled() { |
||
696 | $config_notification_assignedto = $this->getTrackerConfigNotificationAssignedTo(); |
||
697 | $project = $this->getTracker()->getProject(); |
||
698 | return $config_notification_assignedto->isAssignedToSubjectEnabled($project->getID()); |
||
699 | } |
||
700 | |||
701 | /** |
||
702 | * notify people |
||
703 | * |
||
704 | * @return void |
||
705 | */ |
||
706 | public function notify() { |
||
707 | $tracker = $this->getTracker(); |
||
708 | if ( ! $tracker->isNotificationStopped()) { |
||
709 | $logger = $this->getLogger(); |
||
710 | $logger->debug('Start notification'); |
||
711 | |||
712 | $this->getArtifact()->forceFetchAllChangesets(); |
||
713 | |||
714 | // 0. Is update |
||
715 | $is_update = ! $this->getArtifact()->isFirstChangeset($this); |
||
716 | |||
717 | // 1. Get the recipients list |
||
718 | $recipients = $this->getRecipients($is_update); |
||
719 | $logger->debug('Recipients '.implode(', ', array_keys($recipients))); |
||
720 | |||
721 | // 2. Compute the body of the message + headers |
||
722 | $messages = array(); |
||
723 | |||
724 | $config = $this->getTrackerPluginConfig(); |
||
725 | if ($config->isTokenBasedEmailgatewayEnabled() || $this->isNotificationAssignedToEnabled()) { |
||
726 | $messages = $this->buildAMessagePerRecipient($recipients, $is_update); |
||
727 | } else { |
||
728 | $messages = $this->buildOneMessageForMultipleRecipients($recipients, $is_update); |
||
729 | } |
||
730 | |||
731 | // 3. Send the notification |
||
732 | foreach ($messages as $message) { |
||
733 | $logger->debug('Notify '.implode(', ', $message['recipients'])); |
||
734 | $this->sendNotification( |
||
735 | $message['recipients'], |
||
736 | $message['headers'], |
||
737 | $message['from'], |
||
738 | $message['subject'], |
||
739 | $message['htmlBody'], |
||
740 | $message['txtBody'], |
||
741 | $message['message-id'] |
||
742 | ); |
||
743 | } |
||
744 | $logger->debug('End notification'); |
||
745 | } |
||
746 | } |
||
747 | |||
748 | protected function getLogger() { |
||
749 | return new WrapperLogger( |
||
750 | new TruncateLevelLogger( |
||
751 | new BackendLogger(), |
||
752 | ForgeConfig::get('sys_logger_level') |
||
753 | ), |
||
754 | 'art #'.$this->getArtifact()->getId().' - cs #'.$this->getId() |
||
755 | ); |
||
756 | } |
||
757 | |||
758 | public function buildOneMessageForMultipleRecipients(array $recipients, $is_update) { |
||
759 | $messages = array(); |
||
760 | foreach ($recipients as $recipient => $check_perms) { |
||
761 | $user = $this->getUserFromRecipientName($recipient); |
||
762 | |||
763 | if ($user) { |
||
764 | $ignore_perms = !$check_perms; |
||
765 | $recipient_mail = $user->getEmail(); |
||
766 | $message_content = $this->getMessageContent($user, $is_update, $check_perms); |
||
767 | $headers = array_filter(array($this->getCustomReplyToHeader())); |
||
768 | $hash = md5($message_content['htmlBody'] . $message_content['txtBody'] . serialize($message_content['subject'])); |
||
769 | |||
770 | if (isset($messages[$hash])) { |
||
771 | $messages[$hash]['recipients'][] = $recipient_mail; |
||
772 | } else { |
||
773 | $messages[$hash] = $message_content; |
||
774 | |||
775 | $messages[$hash]['message-id'] = null; |
||
776 | $messages[$hash]['headers'] = $headers; |
||
777 | $messages[$hash]['recipients'] = array($recipient_mail); |
||
778 | } |
||
779 | |||
780 | $messages[$hash]['from'] = $this->getDefaultEmailSenderAddress(); |
||
781 | } |
||
782 | } |
||
783 | |||
784 | return $messages; |
||
785 | } |
||
786 | |||
787 | public function buildAMessagePerRecipient(array $recipients, $is_update) { |
||
788 | $messages = array(); |
||
789 | $anonymous_mail = 0; |
||
790 | |||
791 | foreach ($recipients as $recipient => $check_perms) { |
||
792 | $user = $this->getUserFromRecipientName($recipient); |
||
793 | |||
794 | if (! $user->isAnonymous()) { |
||
795 | $headers = array_filter(array($this->getCustomReplyToHeader())); |
||
796 | $message_id = $this->getMessageId($user); |
||
797 | |||
798 | $messages[$message_id] = $this->getMessageContent($user, $is_update, $check_perms); |
||
799 | $messages[$message_id]['from'] = ForgeConfig::get('sys_name') . '<' .$this->getArtifact()->getTokenBasedEmailAddress() . '>'; |
||
800 | $messages[$message_id]['message-id'] = $message_id; |
||
801 | $messages[$message_id]['headers'] = $headers; |
||
802 | $messages[$message_id]['recipients'] = array($user->getEmail()); |
||
803 | |||
804 | } else { |
||
805 | $headers = array($this->getAnonymousHeaders()); |
||
806 | |||
807 | $messages[$anonymous_mail] = $this->getMessageContent($user, $is_update, $check_perms); |
||
808 | $messages[$anonymous_mail]['from'] = $this->getDefaultEmailSenderAddress(); |
||
809 | $messages[$anonymous_mail]['message-id'] = null; |
||
810 | $messages[$anonymous_mail]['headers'] = $headers; |
||
811 | $messages[$anonymous_mail]['recipients'] = array($user->getEmail()); |
||
812 | |||
813 | $anonymous_mail += 1; |
||
814 | } |
||
815 | } |
||
816 | |||
817 | return $messages; |
||
818 | } |
||
819 | |||
820 | /** |
||
821 | * @return array |
||
822 | */ |
||
823 | private function getAnonymousHeaders() { |
||
824 | return array( |
||
825 | "name" => "Reply-to", |
||
826 | "value" => ForgeConfig::get('sys_noreply') |
||
827 | ); |
||
828 | } |
||
829 | |||
830 | private function getDefaultEmailSenderAddress() { |
||
831 | $email_domain = ForgeConfig::get('sys_default_mail_domain'); |
||
832 | |||
833 | if (! $email_domain) { |
||
834 | $email_domain = ForgeConfig::get('sys_default_domain'); |
||
835 | } |
||
836 | |||
837 | return ForgeConfig::get('sys_name') . '<' . self::DEFAULT_MAIL_SENDER . '@' . $email_domain . '>'; |
||
838 | } |
||
839 | |||
840 | private function getMessageId(PFUser $user) { |
||
841 | $recipient_factory = $this->getRecipientFactory(); |
||
842 | $recipient = $recipient_factory->getFromUserAndChangeset($user, $this); |
||
843 | |||
844 | return $recipient->getEmail(); |
||
845 | } |
||
846 | |||
847 | /** |
||
848 | * @return Tracker_Artifact_MailGateway_RecipientFactory |
||
849 | */ |
||
850 | protected function getRecipientFactory() { |
||
853 | |||
854 | private function getCustomReplyToHeader() { |
||
855 | $config = $this->getTrackerPluginConfig(); |
||
856 | $artifactbymail = new Tracker_ArtifactByEmailStatus($config); |
||
857 | |||
858 | if ($config->isTokenBasedEmailgatewayEnabled()) { |
||
859 | return array( |
||
860 | "name" => "Reply-to", |
||
861 | "value" => $this->getArtifact()->getTokenBasedEmailAddress() |
||
862 | ); |
||
863 | } else if ($artifactbymail->canUpdateArtifactInInsecureMode($this->getArtifact()->getTracker())) { |
||
864 | return array( |
||
865 | "name" => "Reply-to", |
||
866 | "value" => $this->getArtifact()->getInsecureEmailAddress() |
||
867 | ); |
||
868 | } |
||
869 | } |
||
870 | |||
871 | private function getMessageContent($user, $is_update, $check_perms) { |
||
872 | $ignore_perms = !$check_perms; |
||
873 | |||
874 | $lang = $user->getLanguage(); |
||
875 | |||
876 | $mailManager = new MailManager(); |
||
877 | $format = $mailManager->getMailPreferencesByUser($user); |
||
878 | |||
879 | |||
880 | $htmlBody = ''; |
||
881 | if ($format == Codendi_Mail_Interface::FORMAT_HTML) { |
||
882 | $htmlBody .= $this->getBodyHtml($is_update, $user, $lang, $ignore_perms); |
||
883 | $htmlBody .= $this->getHTMLAssignedToFilter($user); |
||
884 | } |
||
885 | |||
886 | $txtBody = $this->getBodyText($is_update, $user, $lang, $ignore_perms); |
||
887 | $txtBody .= $this->getTextAssignedToFilter($user); |
||
888 | $subject = $this->getSubject($user, $ignore_perms); |
||
889 | |||
890 | $message = array(); |
||
891 | |||
892 | $message['htmlBody'] = $htmlBody; |
||
893 | $message['txtBody'] = $txtBody; |
||
894 | $message['subject'] = $subject; |
||
895 | |||
896 | return $message; |
||
897 | |||
898 | } |
||
899 | |||
900 | protected function getUserFromRecipientName($recipient_name) { |
||
901 | $um = $this->getUserManager(); |
||
902 | $user = null; |
||
903 | if ( strpos($recipient_name, '@') !== false ) { |
||
904 | //check for registered |
||
905 | $user = $um->getUserByEmail($recipient_name); |
||
906 | |||
907 | //user does not exist (not registered/mailing list) then it is considered as an anonymous |
||
908 | if ( ! $user ) { |
||
909 | // don't call $um->getUserAnonymous() as it will always return the same instance |
||
910 | // we don't want to override previous emails |
||
911 | // So create new anonymous instance by hand |
||
912 | $user = $um->getUserInstanceFromRow( |
||
913 | array( |
||
914 | 'user_id' => 0, |
||
915 | 'email' => $recipient_name, |
||
916 | ) |
||
917 | ); |
||
918 | } |
||
919 | } else { |
||
920 | //is a login |
||
921 | $user = $um->getUserByUserName($recipient_name); |
||
922 | } |
||
923 | |||
924 | return $user; |
||
925 | } |
||
926 | |||
927 | /** |
||
928 | * Send a notification |
||
929 | * |
||
930 | * @param array $recipients the list of recipients |
||
931 | * @param array $headers the additional headers |
||
932 | * @param string $from the mail of the sender |
||
933 | * @param string $subject the subject of the message |
||
934 | * @param string $htmlBody the html content of the message |
||
935 | * @param string $txtBody the text content of the message |
||
936 | * @param string $message_id the id of the message |
||
937 | * |
||
938 | * @return void |
||
939 | */ |
||
940 | protected function sendNotification($recipients, $headers, $from, $subject, $htmlBody, $txtBody, $message_id) { |
||
941 | $hp = Codendi_HTMLPurifier::instance(); |
||
942 | $breadcrumbs = array(); |
||
943 | $tracker = $this->getTracker(); |
||
944 | $project = $tracker->getProject(); |
||
945 | $artifactId = $this->getArtifact()->getID(); |
||
946 | $project_unix_name = $project->getUnixName(true); |
||
947 | $tracker_name = $tracker->getItemName(); |
||
948 | $mail_enhancer = new MailEnhancer(); |
||
949 | |||
950 | if($message_id) { |
||
951 | $mail_enhancer->setMessageId($message_id); |
||
952 | } |
||
953 | |||
954 | $breadcrumbs[] = '<a href="'. get_server_url() .'/projects/'. $project_unix_name .'" />'. $project->getPublicName() .'</a>'; |
||
955 | $breadcrumbs[] = '<a href="'. get_server_url() .'/plugins/tracker/?tracker='. (int)$tracker->getId() .'" />'. $hp->purify($this->getTracker()->getName()) .'</a>'; |
||
956 | $breadcrumbs[] = '<a href="'. get_server_url().'/plugins/tracker/?aid='.(int)$artifactId.'" />'. $hp->purify($this->getTracker()->getName().' #'.$artifactId) .'</a>'; |
||
957 | |||
958 | $mail_enhancer->addPropertiesToLookAndFeel('breadcrumbs', $breadcrumbs); |
||
959 | $mail_enhancer->addPropertiesToLookAndFeel('unsubscribe_link', $this->getUnsubscribeLink()); |
||
960 | $mail_enhancer->addPropertiesToLookAndFeel('title', $hp->purify($subject)); |
||
961 | $mail_enhancer->addHeader("X-Codendi-Project", $project->getUnixName()); |
||
962 | $mail_enhancer->addHeader("X-Codendi-Tracker", $tracker_name); |
||
963 | $mail_enhancer->addHeader("X-Codendi-Artifact-ID", $this->artifact->getId()); |
||
964 | $mail_enhancer->addHeader('From', $from); |
||
965 | |||
966 | foreach($headers as $header) { |
||
967 | $mail_enhancer->addHeader($header['name'], $header['value']); |
||
968 | } |
||
969 | |||
970 | if ($htmlBody) { |
||
971 | $htmlBody .= $this->getHTMLBodyFilter($project_unix_name, $tracker_name); |
||
972 | } |
||
973 | |||
974 | $txtBody .= $this->getTextBodyFilter($project_unix_name, $tracker_name); |
||
975 | |||
976 | $mail_notification_builder = new MailNotificationBuilder(new MailBuilder(TemplateRendererFactory::build())); |
||
977 | $mail_notification_builder->buildAndSendEmail( |
||
978 | $project, |
||
979 | $recipients, |
||
980 | $subject, |
||
981 | $htmlBody, |
||
982 | $txtBody, |
||
983 | get_server_url().$this->getUri(), |
||
984 | trackerPlugin::TRUNCATED_SERVICE_NAME, |
||
985 | $mail_enhancer |
||
986 | ); |
||
987 | } |
||
988 | |||
989 | private function getTextBodyFilter($project_name, $tracker_name) { |
||
990 | $project_filter = '=PROJECT='.$project_name; |
||
991 | $tracker_filter = '=TRACKER='.$tracker_name; |
||
992 | |||
993 | return PHP_EOL . $project_filter . PHP_EOL . $tracker_filter . PHP_EOL; |
||
994 | } |
||
995 | |||
996 | private function getHTMLBodyFilter($project_name, $tracker_name) { |
||
997 | $filter = '<div style="display: none !important;">'; |
||
998 | $filter .= '=PROJECT=' . $project_name . '<br>'; |
||
999 | $filter .= '=TRACKER=' . $tracker_name . '<br>'; |
||
1000 | $filter .= '</div>'; |
||
1001 | |||
1002 | return $filter; |
||
1003 | } |
||
1004 | |||
1005 | /** |
||
1006 | * @return string |
||
1007 | */ |
||
1008 | private function getTextAssignedToFilter(PFUser $recipient) { |
||
1023 | |||
1024 | /** |
||
1025 | * @return string |
||
1026 | */ |
||
1027 | private function getHTMLAssignedToFilter(PFUser $recipient) { |
||
1028 | $filter = ''; |
||
1029 | |||
1030 | if ($this->isNotificationAssignedToEnabled()) { |
||
1031 | $filter = '<div style="display: none !important;">'; |
||
1032 | $users = $this->getArtifact()->getAssignedTo($recipient); |
||
1033 | foreach ($users as $user) { |
||
1034 | $filter .= '=ASSIGNED_TO=' . $user->getUserName() . '<br>'; |
||
1035 | } |
||
1036 | $filter .= '</div>'; |
||
1037 | } |
||
1038 | |||
1039 | return $filter; |
||
1040 | } |
||
1041 | |||
1042 | public function removeRecipientsThatMayReceiveAnEmptyNotification(array &$recipients) { |
||
1043 | if ($this->getComment() && ! $this->getComment()->hasEmptyBody()) { |
||
1044 | return; |
||
1045 | } |
||
1046 | |||
1047 | foreach ($recipients as $recipient => $check_perms) { |
||
1048 | if ( ! $check_perms) { |
||
1049 | continue; |
||
1050 | } |
||
1051 | |||
1052 | $user = $this->getUserFromRecipientName($recipient); |
||
1053 | if ( ! $user || ! $this->userCanReadAtLeastOneChangedField($user)) { |
||
1054 | unset($recipients[$recipient]); |
||
1055 | } |
||
1056 | } |
||
1057 | } |
||
1058 | |||
1059 | public function removeRecipientsThatHaveUnsubscribedArtifactNotification(array &$recipients) { |
||
1070 | |||
1071 | private function userCanReadAtLeastOneChangedField(PFUser $user) { |
||
1072 | $factory = $this->getFormElementFactory(); |
||
1073 | |||
1074 | foreach ($this->getValues() as $field_id => $current_changeset_value) { |
||
1075 | $field = $factory->getFieldById($field_id); |
||
1076 | $field_is_readable = $field && $field->userCanRead($user); |
||
1077 | $field_has_changed = $current_changeset_value && $current_changeset_value->hasChanged(); |
||
1078 | if ($field_is_readable && $field_has_changed) { |
||
1079 | return true; |
||
1080 | } |
||
1081 | } |
||
1082 | return false; |
||
1083 | } |
||
1084 | |||
1085 | /** |
||
1086 | * Get the recipients for notification |
||
1087 | * |
||
1088 | * @param bool $is_update It is an update, not a new artifact |
||
1089 | * |
||
1090 | * @return array of [$recipient => $checkPermissions] where $recipient is a usenrame or an email and $checkPermissions is bool. |
||
1091 | */ |
||
1092 | public function getRecipients($is_update) { |
||
1093 | $factory = $this->getFormElementFactory(); |
||
1094 | |||
1095 | // 0 Is update |
||
1096 | $is_update = ! $this->getArtifact()->isFirstChangeset($this); |
||
1097 | |||
1098 | // 1 Get from the fields |
||
1099 | $recipients = array(); |
||
1100 | $this->forceFetchAllValues(); |
||
1101 | foreach ($this->getValues() as $field_id => $current_changeset_value) { |
||
1102 | if ($field = $factory->getFieldById($field_id)) { |
||
1103 | if ($field->isNotificationsSupported() && $field->hasNotifications() && ($r = $field->getRecipients($current_changeset_value))) { |
||
1104 | $recipients = array_merge($recipients, $r); |
||
1105 | } |
||
1106 | } |
||
1107 | } |
||
1108 | // 2 Get from the commentators |
||
1109 | $recipients = array_merge($recipients, $this->getArtifact()->getCommentators()); |
||
1110 | $recipients = array_values(array_unique($recipients)); |
||
1111 | |||
1112 | |||
1113 | //now force check perms for all this people |
||
1114 | $tablo = array(); |
||
1115 | foreach($recipients as $r) { |
||
1116 | $tablo[$r] = true; |
||
1117 | } |
||
1118 | |||
1119 | // 3 Get from the global notif |
||
1120 | foreach ($this->getArtifact()->getTracker()->getRecipients() as $r) { |
||
1121 | if ( $r['on_updates'] == 1 || !$is_update ) { |
||
1122 | foreach($r['recipients'] as $recipient) { |
||
1123 | $tablo[$recipient] = $r['check_permissions']; |
||
1124 | } |
||
1125 | } |
||
1126 | } |
||
1127 | $this->removeRecipientsThatMayReceiveAnEmptyNotification($tablo); |
||
1128 | $this->removeRecipientsThatHaveUnsubscribedArtifactNotification($tablo); |
||
1129 | |||
1130 | return $tablo; |
||
1131 | } |
||
1132 | |||
1133 | /** |
||
1134 | * Get the text body for notification |
||
1135 | * |
||
1136 | * @param Boolean $is_update It is an update, not a new artifact |
||
1137 | * @param String $recipient The recipient who will receive the notification |
||
1138 | * @param BaseLanguage $language The language of the message |
||
1139 | * @param Boolean $ignore_perms indicates if permissions have to be ignored |
||
1140 | * |
||
1141 | * @return String |
||
1142 | */ |
||
1143 | public function getBodyText($is_update, $recipient_user, BaseLanguage $language, $ignore_perms) { |
||
1144 | $format = 'text'; |
||
1145 | $art = $this->getArtifact(); |
||
1146 | $um = $this->getUserManager(); |
||
1147 | $user = $um->getUserById($this->submitted_by); |
||
1148 | |||
1149 | $output = '+============== '.'['.$art->getTracker()->getItemName() .' #'. $art->getId().'] '.$art->fetchMailTitle($recipient_user, $format, $ignore_perms).' ==============+'; |
||
1150 | $output .= PHP_EOL; |
||
1151 | $output .= PHP_EOL; |
||
1152 | $proto = ($GLOBALS['sys_force_ssl']) ? 'https' : 'http'; |
||
1153 | $output .= ' <'. $proto .'://'. $GLOBALS['sys_default_domain'] .TRACKER_BASE_URL.'/?aid='. $art->getId() .'>'; |
||
1154 | $output .= PHP_EOL; |
||
1155 | $output .= $language->getText('plugin_tracker_include_artifact', 'last_edited'); |
||
1156 | $output .= ' '. $this->getUserHelper()->getDisplayNameFromUserId($this->submitted_by); |
||
1157 | $output .= ' on '.DateHelper::formatForLanguage($language, $this->submitted_on); |
||
1158 | if ( $comment = $this->getComment() ) { |
||
1159 | $output .= PHP_EOL; |
||
1160 | $output .= $comment->fetchMailFollowUp($format); |
||
1161 | } |
||
1162 | $output .= PHP_EOL; |
||
1163 | $output .= ' -------------- ' . $language->getText('plugin_tracker_artifact_changeset', 'header_changeset') . ' ---------------- ' ; |
||
1164 | $output .= PHP_EOL; |
||
1165 | $output .= $this->diffToPrevious($format, $recipient_user, $ignore_perms); |
||
1166 | $output .= PHP_EOL; |
||
1167 | $output .= ' -------------- ' . $language->getText('plugin_tracker_artifact_changeset', 'header_artifact') . ' ---------------- '; |
||
1168 | $output .= PHP_EOL; |
||
1169 | $output .= $art->fetchMail($recipient_user, $format, $ignore_perms); |
||
1170 | $output .= PHP_EOL; |
||
1171 | return $output; |
||
1172 | } |
||
1173 | /** |
||
1174 | * Get the html body for notification |
||
1175 | * |
||
1176 | * @param Boolean $is_update It is an update, not a new artifact |
||
1177 | * @param String $recipient The recipient who will receive the notification |
||
1178 | * @param BaseLanguage $language The language of the message |
||
1179 | * @param Boolean $ignore_perms ??? |
||
1180 | * |
||
1181 | * @return String |
||
1182 | */ |
||
1183 | public function getBodyHtml($is_update, $recipient_user, BaseLanguage $language, $ignore_perms) { |
||
1184 | $format = 'html'; |
||
1185 | $art = $this->getArtifact(); |
||
1186 | $hp = Codendi_HTMLPurifier::instance(); |
||
1187 | $followup = ''; |
||
1188 | $changes = $this->mailDiffToPrevious($format, $recipient_user, $ignore_perms); |
||
1189 | // Display latest changes (diff) |
||
1190 | if ($comment = $this->getComment()) { |
||
1191 | $followup = $comment->fetchMailFollowUp($format); |
||
1192 | } |
||
1193 | |||
1194 | $output = |
||
1195 | '<table style="width:100%"> |
||
1196 | <tr> |
||
1197 | <td align="left" colspan="2"> |
||
1198 | <h1>'.$hp->purify($art->fetchMailTitle($recipient_user, $format, $ignore_perms)).' |
||
1199 | </h1> |
||
1200 | </td> |
||
1201 | </tr>'; |
||
1202 | |||
1203 | if ($followup || $changes) { |
||
1204 | |||
1205 | $output .= |
||
1206 | '<tr> |
||
1207 | <td colspan="2" align="left"> |
||
1208 | <h2>'.$language->getText('plugin_tracker_artifact_changeset', 'header_html_changeset').' |
||
1209 | </h2> |
||
1210 | </td> |
||
1211 | </tr>'; |
||
1212 | // Last comment |
||
1213 | if ($followup) { |
||
1214 | $output .= $followup; |
||
1215 | } |
||
1216 | // Last changes |
||
1217 | if ($changes) { |
||
1218 | //TODO check that the following is PHP compliant (what if I made a changes without a comment? -- comment is null) |
||
1219 | if (!empty($comment->body)) { |
||
1220 | $output .= ' |
||
1221 | <tr> |
||
1222 | <td colspan="2"> |
||
1223 | <hr size="1" /> |
||
1224 | </td> |
||
1225 | </tr>'; |
||
1226 | } |
||
1227 | $output .= |
||
1228 | '<tr> |
||
1229 | <td> </td> |
||
1230 | <td align="left"> |
||
1231 | <ul>'. |
||
1232 | $changes.' |
||
1233 | </ul> |
||
1234 | </td> |
||
1235 | </tr>'; |
||
1236 | } |
||
1237 | |||
1238 | $artifact_link = get_server_url().'/plugins/tracker/?aid='.(int)$art->getId(); |
||
1239 | |||
1240 | $output .= |
||
1241 | '<tr> |
||
1242 | <td> </td> |
||
1243 | <td align="right">'. |
||
1244 | $this->fetchHtmlAnswerButton($artifact_link). |
||
1245 | '</span> |
||
1246 | </td> |
||
1247 | </tr>'; |
||
1248 | } |
||
1249 | $output .= '</table>'; |
||
1250 | |||
1251 | //Display of snapshot |
||
1252 | $snapshot = $art->fetchMail($recipient_user, $format, $ignore_perms); |
||
1253 | if ($snapshot) { |
||
1254 | $output .= $snapshot; |
||
1255 | } |
||
1256 | return $output; |
||
1257 | } |
||
1258 | |||
1259 | /** |
||
1260 | * @return string html call to action button to include in an html mail |
||
1261 | */ |
||
1262 | private function fetchHtmlAnswerButton($artifact_link) { |
||
1263 | return '<span class="cta"> |
||
1264 | <a href="'. $artifact_link .'" target="_blank">' . |
||
1265 | $GLOBALS['Language']->getText('tracker_include_artifact','mail_answer_now') . |
||
1266 | '</a> |
||
1267 | </span>'; |
||
1268 | } |
||
1269 | |||
1270 | /** |
||
1271 | * @return string html call to action button to include in an html mail |
||
1272 | */ |
||
1273 | private function getUnsubscribeLink() { |
||
1274 | $link = get_server_url().'/plugins/tracker/?aid='.(int)$this->getArtifact()->getId().'&func=manage-subscription'; |
||
1275 | |||
1276 | return '<a href="'. $link .'" target="_blank">' . |
||
1277 | $GLOBALS['Language']->getText('plugin_tracker_artifact','mail_unsubscribe') . |
||
1278 | '</a>'; |
||
1279 | } |
||
1280 | |||
1281 | /** |
||
1282 | * Wrapper for UserHelper |
||
1283 | * |
||
1284 | * @return UserHelper |
||
1285 | */ |
||
1286 | protected function getUserHelper() { |
||
1289 | |||
1290 | /** |
||
1291 | * Get the subject for notification |
||
1292 | * |
||
1293 | * @return string |
||
1294 | */ |
||
1295 | public function getSubject(PFUser $recipient, $ignore_perms=false) { |
||
1296 | $subject = '['. $this->getArtifact()->getTracker()->getItemName() .' #'. $this->getArtifact()->getId() .'] '; |
||
1297 | $subject .= $this->getSubjectAssignedTo($recipient); |
||
1298 | $subject .= $this->getArtifact()->fetchMailTitle($recipient, 'text' ,$ignore_perms); |
||
1299 | return $subject; |
||
1300 | } |
||
1301 | |||
1302 | /** |
||
1303 | * @return string |
||
1304 | */ |
||
1305 | private function getSubjectAssignedTo(PFUser $recipient) { |
||
1306 | if ($this->isNotificationAssignedToEnabled()) { |
||
1307 | $users = $this->getArtifact()->getAssignedTo($recipient); |
||
1308 | if (in_array($recipient, $users, true)) { |
||
1309 | return '[Assigned to me] '; |
||
1310 | } |
||
1311 | } |
||
1312 | return ''; |
||
1313 | } |
||
1314 | |||
1315 | /** |
||
1316 | * Return the Tracker_Artifact of this changeset |
||
1317 | * |
||
1318 | * @return Tracker_Artifact The artifact of this changeset |
||
1319 | */ |
||
1320 | function getArtifact() { |
||
1323 | |||
1324 | /** |
||
1325 | * Returns the Id of this changeset |
||
1326 | * |
||
1327 | * @return int The Id of this changeset |
||
1328 | */ |
||
1329 | public function getId() { |
||
1332 | |||
1333 | public function exportCommentToSOAP() { |
||
1334 | $comment = $this->getComment(); |
||
1335 | if ($comment) { |
||
1336 | $soap = $this->getSoapMetadata(); |
||
1337 | return $comment->exportToSOAP($soap); |
||
1338 | } |
||
1339 | } |
||
1340 | |||
1341 | private function getSoapMetadata() { |
||
1342 | $soap = array( |
||
1343 | 'submitted_by' => $this->getSubmittedBy(), |
||
1344 | 'email' => $this->getEmailForUndefinedSubmitter(), |
||
1345 | 'submitted_on' => $this->getSubmittedOn(), |
||
1346 | ); |
||
1347 | return $soap; |
||
1348 | } |
||
1349 | |||
1350 | public function getSoapValue(PFUser $user) { |
||
1351 | $soap = $this->getSoapMetadata(); |
||
1352 | $comment = $this->getComment(); |
||
1353 | if (! $comment) { |
||
1354 | $comment = new Tracker_Artifact_Changeset_CommentNull($this); |
||
1355 | } |
||
1356 | $soap['last_comment'] = $comment->getSoapValue(); |
||
1357 | $factory = $this->getFormElementFactory(); |
||
1358 | foreach ($this->getValueDao()->searchById($this->id) as $row) { |
||
1359 | $field = $factory->getFieldById($row['field_id']); |
||
1360 | if ($field && $field->isCompatibleWithSoap()) { |
||
1361 | $soap['fields'][] = $field->getSoapValue($user, $this); |
||
1362 | } |
||
1363 | } |
||
1364 | return $soap; |
||
1365 | } |
||
1366 | |||
1367 | public function getRESTValue(PFUser $user, $fields) { |
||
1368 | $comment = $this->getComment(); |
||
1369 | if (! $comment) { |
||
1370 | $comment = new Tracker_Artifact_Changeset_CommentNull($this); |
||
1371 | } |
||
1372 | if ($fields == self::FIELDS_COMMENTS && $comment->hasEmptyBody()) { |
||
1373 | return null; |
||
1374 | } |
||
1375 | $classname_with_namespace = 'Tuleap\Tracker\REST\ChangesetRepresentation'; |
||
1376 | $changeset_representation = new $classname_with_namespace; |
||
1377 | $changeset_representation->build( |
||
1378 | $this, |
||
1379 | $comment, |
||
1380 | $fields == self::FIELDS_COMMENTS ? array() : $this->getRESTFieldValues($user) |
||
1381 | ); |
||
1382 | return $changeset_representation; |
||
1383 | } |
||
1384 | |||
1385 | private function getRESTFieldValues(PFUser $user) { |
||
1396 | |||
1397 | private function getEmailForUndefinedSubmitter() { |
||
1398 | if (! $this->getSubmittedBy()) { |
||
1399 | return $this->getEmail(); |
||
1400 | } |
||
1401 | } |
||
1402 | |||
1403 | /** |
||
1404 | * Link to changeset in interface |
||
1405 | * |
||
1406 | * @return String |
||
1407 | */ |
||
1408 | public function getUri() { |
||
1411 | } |
||
1412 |
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.