1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved |
4
|
|
|
* |
5
|
|
|
* This file is a part of Codendi. |
6
|
|
|
* |
7
|
|
|
* Codendi is free software; you can redistribute it and/or modify |
8
|
|
|
* it under the terms of the GNU General Public License as published by |
9
|
|
|
* the Free Software Foundation; either version 2 of the License, or |
10
|
|
|
* (at your option) any later version. |
11
|
|
|
* |
12
|
|
|
* Codendi is distributed in the hope that it will be useful, |
13
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15
|
|
|
* GNU General Public License for more details. |
16
|
|
|
* |
17
|
|
|
* You should have received a copy of the GNU General Public License |
18
|
|
|
* along with Codendi. If not, see <http://www.gnu.org/licenses/>. |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
require_once(dirname(__FILE__).'/../../constants.php'); |
22
|
|
|
|
23
|
|
|
class Tracker_Artifact implements Recent_Element_Interface, Tracker_Dispatchable_Interface { |
24
|
|
|
const REST_ROUTE = 'artifacts'; |
25
|
|
|
const NO_PARENT = -1; |
26
|
|
|
const PERMISSION_ACCESS = 'PLUGIN_TRACKER_ARTIFACT_ACCESS'; |
27
|
|
|
const REFERENCE_NATURE = 'plugin_tracker_artifact'; |
28
|
|
|
const STATUS_OPEN = 'open'; |
29
|
|
|
const STATUS_CLOSED = 'closed'; |
30
|
|
|
|
31
|
|
|
public $id; |
32
|
|
|
public $tracker_id; |
33
|
|
|
public $use_artifact_permissions; |
34
|
|
|
protected $per_tracker_id; |
35
|
|
|
protected $submitted_by; |
36
|
|
|
protected $submitted_on; |
37
|
|
|
|
38
|
|
|
protected $changesets; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var array of Tracker_Artifact |
42
|
|
|
*/ |
43
|
|
|
private $ancestors; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var Tracker |
47
|
|
|
*/ |
48
|
|
|
private $tracker; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @var Tracker_FormElementFactory |
52
|
|
|
*/ |
53
|
|
|
private $formElementFactory; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @var Tracker_HierarchyFactory |
57
|
|
|
*/ |
58
|
|
|
private $hierarchy_factory; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @var String |
62
|
|
|
*/ |
63
|
|
|
private $title; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* @var String |
67
|
|
|
*/ |
68
|
|
|
private $status; |
69
|
|
|
|
70
|
|
|
/** @var Tracker_ArtifactFactory */ |
71
|
|
|
private $artifact_factory; |
72
|
|
|
|
73
|
|
|
/** @var Tracker_Artifact[] */ |
74
|
|
|
private $siblings; |
75
|
|
|
|
76
|
|
|
/** @var Tracker_Artifact[] */ |
77
|
|
|
private $siblings_without_permission_checking; |
78
|
|
|
|
79
|
|
|
/** @var Tracker_Artifact */ |
80
|
|
|
private $parent_without_permission_checking; |
81
|
|
|
|
82
|
|
|
/** @var Boolean[] */ |
83
|
|
|
private $can_view_cache = array(); |
84
|
|
|
|
85
|
|
|
/** @var PFUser*/ |
86
|
|
|
private $submitted_by_user; |
87
|
|
|
|
88
|
|
|
/** @var array */ |
89
|
|
|
private $authorized_ugroups; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Constructor |
93
|
|
|
* |
94
|
|
|
* @param int $id The Id of the artifact |
95
|
|
|
* @param int $tracker_id The tracker Id the artifact belongs to |
96
|
|
|
* @param int $submitted_by The id of the user who's submitted the artifact |
97
|
|
|
* @param int $submitted_on The timestamp of artifact submission |
98
|
|
|
* |
99
|
|
|
* @param boolean $use_artifact_permissions True if this artifact uses permission, false otherwise |
100
|
|
|
*/ |
101
|
|
|
public function __construct($id, $tracker_id, $submitted_by, $submitted_on, $use_artifact_permissions) { |
102
|
|
|
$this->id = $id; |
103
|
|
|
$this->tracker_id = $tracker_id; |
104
|
|
|
$this->submitted_by = $submitted_by; |
105
|
|
|
$this->submitted_on = $submitted_on; |
106
|
|
|
$this->use_artifact_permissions = $use_artifact_permissions; |
107
|
|
|
$this->per_tracker_id = null; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Obtain event manager instance |
112
|
|
|
* |
113
|
|
|
* @return EventManager |
114
|
|
|
*/ |
115
|
|
|
private function getEventManager() { |
116
|
|
|
return EventManager::instance(); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Return true if given given artifact refer to the same DB object (basically same id). |
121
|
|
|
* |
122
|
|
|
* @param Tracker_Artifact $artifact |
123
|
|
|
* |
124
|
|
|
* @return Boolean |
125
|
|
|
*/ |
126
|
|
|
public function equals(Tracker_Artifact $artifact = null) { |
127
|
|
|
return $artifact && $this->id == $artifact->getId(); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Set the value of use_artifact_permissions |
132
|
|
|
* |
133
|
|
|
* @param bool $use_artifact_permissions |
134
|
|
|
* |
135
|
|
|
* @return bool true if the artifact has individual permissions set |
136
|
|
|
*/ |
137
|
|
|
public function setUseArtifactPermissions($use_artifact_permissions) { |
138
|
|
|
$this->use_artifact_permissions = $use_artifact_permissions; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* useArtifactPermissions |
143
|
|
|
* @return bool true if the artifact has individual permissions set |
144
|
|
|
*/ |
145
|
|
|
public function useArtifactPermissions() { |
146
|
|
|
return $this->use_artifact_permissions; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* userCanView - determine if the user can view this artifact. |
151
|
|
|
* |
152
|
|
|
* @param PFUser $user if not specified, use the current user |
153
|
|
|
* |
154
|
|
|
* @return boolean user can view the artifact |
155
|
|
|
*/ |
156
|
|
|
public function userCanView(PFUser $user = null) { |
157
|
|
|
$um = $this->getUserManager(); |
158
|
|
|
$project_manager = $this->getProjectManager(); |
159
|
|
|
|
160
|
|
|
if (! $user) { |
161
|
|
|
$user = $um->getCurrentUser(); |
162
|
|
|
} |
163
|
|
|
if ($user instanceof Tracker_UserWithReadAllPermission) { |
164
|
|
|
return true; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
if (! isset($this->can_view_cache[$user->getId()])) { |
168
|
|
|
if ($this->getTracker()->userIsAdmin() || $user->isSuperUser()) { |
169
|
|
|
$this->setUserCanView($user, true); |
170
|
|
|
} else { |
171
|
|
|
$permission_checker = new Tracker_Permission_PermissionChecker($um, $project_manager); |
172
|
|
|
$this->setUserCanView($user, $permission_checker->userCanView($user, $this)); |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
return $this->can_view_cache[$user->getId()]; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
public function setUserCanView(PFUser $user, $can_view) { |
179
|
|
|
$this->can_view_cache[$user->getId()] = $can_view; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
public function userCanUpdate(PFUser $user) { |
183
|
|
|
if ($user->isAnonymous() || !$this->userCanView($user)) { |
184
|
|
|
return false; |
185
|
|
|
} |
186
|
|
|
return true; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* @deprecated |
191
|
|
|
*/ |
192
|
|
|
public function permission_db_authorized_ugroups( $permission_type ) { |
193
|
|
|
include_once 'www/project/admin/permissions.php'; |
194
|
|
|
$result = array(); |
195
|
|
|
$res = permission_db_authorized_ugroups($permission_type, $this->getId()); |
196
|
|
|
if ( db_numrows($res) > 0 ) { |
197
|
|
|
while ( $row = db_fetch_array($res) ) { |
198
|
|
|
$result[] = $row; |
199
|
|
|
} |
200
|
|
|
return $result; |
201
|
|
|
} else { |
202
|
|
|
return false; |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
public function getAuthorizedUGroups() { |
207
|
|
|
if (! isset($this->authorized_ugroups)) { |
208
|
|
|
$this->authorized_ugroups = array(); |
209
|
|
|
if ($this->useArtifactPermissions()) { |
210
|
|
|
$this->authorized_ugroups = PermissionsManager::instance()->getAuthorizedUgroupIds( |
211
|
|
|
$this->id, |
212
|
|
|
self::PERMISSION_ACCESS |
213
|
|
|
); |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
return $this->authorized_ugroups; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
public function setAuthorizedUGroups(array $ugroups) { |
221
|
|
|
$this->authorized_ugroups = $ugroups; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* This method returns the artifact mail rendering |
226
|
|
|
* |
227
|
|
|
* @param array $recipient |
228
|
|
|
* @param string $format, the mail format text or html |
|
|
|
|
229
|
|
|
* @param bool $ignore_perms, indicates if we ignore various permissions |
|
|
|
|
230
|
|
|
* |
231
|
|
|
* @return string |
232
|
|
|
*/ |
233
|
|
|
public function fetchMail($recipient, $format, $ignore_perms=false) { |
234
|
|
|
$output = ''; |
235
|
|
|
switch($format) { |
236
|
|
|
case 'html': |
237
|
|
|
$content = $this->fetchMailFormElements($recipient, $format, $ignore_perms); |
238
|
|
|
if ($content) { |
239
|
|
|
$output .= |
240
|
|
|
'<table style="width:100%"> |
241
|
|
|
<tr> |
242
|
|
|
<td colspan="3" align="left"> |
243
|
|
|
<h2>'. |
244
|
|
|
$GLOBALS['Language']->getText('plugin_tracker_artifact_changeset', 'header_html_snapshot').' |
245
|
|
|
</h2> |
246
|
|
|
</td> |
247
|
|
|
</tr> |
248
|
|
|
</table>'; |
249
|
|
|
$output .= $content; |
250
|
|
|
} |
251
|
|
|
$output .= |
252
|
|
|
'<table style="width:100%">'. |
253
|
|
|
$this->fetchMailFollowUp($recipient, $format, $ignore_perms). |
254
|
|
|
'</table>'; |
255
|
|
|
break; |
256
|
|
|
default: |
257
|
|
|
$output .= PHP_EOL; |
258
|
|
|
//fields formelements |
259
|
|
|
$output .= $this->fetchMailFormElements($recipient, $format, $ignore_perms); |
260
|
|
|
$output .= $this->fetchMailFollowUp($recipient, $format, $ignore_perms); |
261
|
|
|
break; |
262
|
|
|
} |
263
|
|
|
return $output; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Returns the artifact field for mail rendering |
268
|
|
|
* |
269
|
|
|
* @param array $recipient |
270
|
|
|
* @param string $format, the mail format text or html |
|
|
|
|
271
|
|
|
* @param bool $ignore_perms, indicates if we ignore various permissions |
|
|
|
|
272
|
|
|
* |
273
|
|
|
* @return String |
274
|
|
|
*/ |
275
|
|
|
public function fetchMailFormElements($recipient, $format, $ignore_perms = false) { |
276
|
|
|
$output = ''; |
277
|
|
|
$toplevel_form_elements = $this->getTracker()->getFormElements(); |
278
|
|
|
$this->prepareElementsForDisplay($toplevel_form_elements); |
279
|
|
|
|
280
|
|
|
foreach ($toplevel_form_elements as $formElement) { |
|
|
|
|
281
|
|
|
$output .= $formElement->fetchMailArtifact($recipient, $this, $format, $ignore_perms); |
282
|
|
|
if ($format == 'text' && $output) { |
283
|
|
|
$output .= PHP_EOL; |
284
|
|
|
} |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
if ($format == 'html') { |
288
|
|
|
$output = '<table width="100%">'.$output.'</table>'; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
return $output; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** @param Tracker_FormElement[] */ |
295
|
|
|
private function prepareElementsForDisplay($toplevel_form_elements) { |
296
|
|
|
foreach ($toplevel_form_elements as $formElement) { |
297
|
|
|
$formElement->prepareForDisplay(); |
298
|
|
|
} |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Returns the artifact followup for mail rendering |
303
|
|
|
* |
304
|
|
|
* @param array $recipient |
305
|
|
|
* @param string $format, the mail format text or html |
|
|
|
|
306
|
|
|
* @param bool $ignore_perms, indicates if we ignore various permissions |
|
|
|
|
307
|
|
|
* |
308
|
|
|
* @return String |
309
|
|
|
*/ |
310
|
|
|
public function fetchMailFollowUp($recipient, $format, $ignore_perms=false) { |
311
|
|
|
$uh = UserHelper::instance(); |
312
|
|
|
$um = UserManager::instance(); |
313
|
|
|
$cs = $this->getChangesets(); |
314
|
|
|
$hp = Codendi_HTMLPurifier::instance(); |
315
|
|
|
$output = ''; |
316
|
|
|
|
317
|
|
|
if($format == 'html'){ |
318
|
|
|
$output .= |
319
|
|
|
'<tr> |
320
|
|
|
<td colspan="3" align="left"> |
321
|
|
|
<h2>'. |
322
|
|
|
$GLOBALS['Language']->getText('plugin_tracker_include_artifact','follow_ups').' |
323
|
|
|
</h2> |
324
|
|
|
</td> |
325
|
|
|
</tr>'; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
foreach ( $cs as $changeset ) { |
329
|
|
|
$comment = $changeset->getComment(); |
330
|
|
|
/* @var $comment Tracker_Artifact_Changeset_Comment */ |
331
|
|
|
if (empty($comment) || $comment->hasEmptyBody()) { |
332
|
|
|
//do not display empty comment |
333
|
|
|
continue; |
334
|
|
|
} |
335
|
|
|
switch ($format) { |
336
|
|
|
case 'html': |
337
|
|
|
$followup = $comment->fetchMailFollowUp($format); |
338
|
|
|
$output .= $followup; |
339
|
|
|
break; |
340
|
|
|
case 'text': |
341
|
|
|
$user = $um->getUserById($comment->submitted_by); |
342
|
|
|
$output .= PHP_EOL; |
343
|
|
|
$output .= '----------------------------- '; |
344
|
|
|
$output .= PHP_EOL; |
345
|
|
|
$output .= $GLOBALS['Language']->getText('plugin_tracker_artifact','mail_followup_date') . util_timestamp_to_userdateformat($comment->submitted_on); |
|
|
|
|
346
|
|
|
$output .= "\t" . $GLOBALS['Language']->getText('plugin_tracker_artifact','mail_followup_by') . $uh->getDisplayNameFromUser($user); |
347
|
|
|
$output .= PHP_EOL; |
348
|
|
|
$output .= $comment->getPurifiedBodyForText(); |
349
|
|
|
$output .= PHP_EOL; |
350
|
|
|
$output .= PHP_EOL; |
351
|
|
|
break; |
352
|
|
|
default: |
353
|
|
|
$output .= '<!-- TODO -->'; |
354
|
|
|
break; |
355
|
|
|
} |
356
|
|
|
} |
357
|
|
|
return $output; |
358
|
|
|
} |
359
|
|
|
/** |
360
|
|
|
* Fetch the tooltip displayed on an artifact reference |
361
|
|
|
* |
362
|
|
|
* @param PFUser $user The user who fetch the tooltip |
363
|
|
|
* |
364
|
|
|
* @return string html |
365
|
|
|
*/ |
366
|
|
|
public function fetchTooltip($user) { |
367
|
|
|
$tooltip = $this->getTracker()->getTooltip(); |
368
|
|
|
$html = ''; |
369
|
|
|
if ($this->userCanView($user)) { |
370
|
|
|
$fields = $tooltip->getFields(); |
371
|
|
|
if (!empty($fields)) { |
372
|
|
|
$html .= '<table>'; |
373
|
|
|
foreach ($fields as $f) { |
374
|
|
|
//TODO: check field permissions |
375
|
|
|
$html .= $f->fetchTooltip($this); |
376
|
|
|
} |
377
|
|
|
$html .= '</table>'; |
378
|
|
|
} |
379
|
|
|
} |
380
|
|
|
return $html; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* Fetch the artifact for the MyArtifact widget |
385
|
|
|
* |
386
|
|
|
* @param string $item_name The short name of the tracker this artifact belongs to |
387
|
|
|
* @param string $title The title of this artifact |
388
|
|
|
* |
389
|
|
|
* @return string html |
390
|
|
|
*/ |
391
|
|
|
public function fetchWidget($item_name, $title) { |
392
|
|
|
$hp = Codendi_HTMLPurifier::instance(); |
393
|
|
|
$html = ''; |
394
|
|
|
$html .= ' <a class="direct-link-to-artifact" href="'.TRACKER_BASE_URL.'/?aid='. $this->id .'">'; |
395
|
|
|
$html .= $hp->purify($item_name, CODENDI_PURIFIER_CONVERT_HTML); |
396
|
|
|
$html .= ' #'; |
397
|
|
|
$html .= $this->id; |
398
|
|
|
if ($title) { |
399
|
|
|
$html .= ' - '; |
400
|
|
|
$html .= $hp->purify($title, CODENDI_PURIFIER_CONVERT_HTML); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
$html .= '</a>'; |
404
|
|
|
return $html; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
public function fetchTitleWithoutUnsubscribeButton($prefix) { |
408
|
|
|
return $this->fetchTitleContent($prefix, false); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Returns HTML code to display the artifact title |
413
|
|
|
* |
414
|
|
|
* @param string $prefix The prefix to display before the title of the artifact. Default is blank. |
415
|
|
|
* |
416
|
|
|
* @return string The HTML code for artifact title |
417
|
|
|
*/ |
418
|
|
|
public function fetchTitle($prefix = '') { |
419
|
|
|
return $this->fetchTitleContent($prefix, true); |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
private function fetchTitleContent($prefix = '', $unsubscribe_button) { |
423
|
|
|
$html = ''; |
424
|
|
|
$html .= $this->fetchHiddenTrackerId(); |
425
|
|
|
$html .= '<div class="tracker_artifact_title">'; |
426
|
|
|
$html .= $prefix; |
427
|
|
|
$html .= $this->getXRefAndTitle(); |
428
|
|
|
if ($unsubscribe_button) { |
429
|
|
|
$html .= $this->fetchEmailActionButtons(); |
430
|
|
|
} |
431
|
|
|
$html .= '</div>'; |
432
|
|
|
return $html; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
public function fetchEmailActionButtons() { |
436
|
|
|
$html = '<div class="tracker-artifact-email-actions">'; |
437
|
|
|
$html .= $this->fetchIncomingMailButton() . ' '; |
438
|
|
|
$html .= $this->fetchNotificationButton(); |
439
|
|
|
$html .= '</div>'; |
440
|
|
|
|
441
|
|
|
return $html; |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
private function fetchNotificationButton() { |
445
|
|
|
$alternate_text = $this->getUnsubscribeButtonAlternateText(); |
446
|
|
|
|
447
|
|
|
$html = '<button class="btn btn-default tracker-artifact-notification" title="' . $alternate_text . '">'; |
448
|
|
|
$html .= '<i class="icon-bell-alt"></i> ' . $this->getUnsubscribeButtonLabel(); |
449
|
|
|
$html .= '</button>'; |
450
|
|
|
|
451
|
|
|
return $html; |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
private function getUnsubscribeButtonLabel() { |
455
|
|
|
$user = $this->getCurrentUser(); |
456
|
|
|
|
457
|
|
|
if ($this->doesUserHaveUnsubscribedFromNotification($user)) { |
458
|
|
|
return $GLOBALS['Language']->getText('plugin_tracker', 'enable_notifications'); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
return $GLOBALS['Language']->getText('plugin_tracker', 'disable_notifications'); |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
private function fetchIncomingMailButton() { |
465
|
|
|
if (! $this->getCurrentUser()->isSuperUser()) { |
466
|
|
|
return ''; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
$retriever = Tracker_Artifact_Changeset_IncomingMailGoldenRetriever::instance(); |
470
|
|
|
$raw_mail = $retriever->getRawMailThatCreatedArtifact($this); |
471
|
|
|
if (! $raw_mail) { |
472
|
|
|
return ''; |
473
|
|
|
} |
474
|
|
|
|
475
|
|
|
$raw_email_button_title = $GLOBALS['Language']->getText('plugin_tracker', 'raw_email_button_title'); |
476
|
|
|
$raw_mail = Codendi_HTMLPurifier::instance()->purify($raw_mail); |
477
|
|
|
|
478
|
|
|
$html = '<button type="button" class="btn btn-default artifact-incoming-mail-button" data-raw-email="'. $raw_mail .'"> |
479
|
|
|
<i class="icon-envelope"></i> '. $raw_email_button_title .' |
480
|
|
|
</button>'; |
481
|
|
|
|
482
|
|
|
return $html; |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
private function getUnsubscribeButtonAlternateText() { |
486
|
|
|
$user = $this->getCurrentUser(); |
487
|
|
|
|
488
|
|
|
if ($this->doesUserHaveUnsubscribedFromNotification($user)) { |
489
|
|
|
return $GLOBALS['Language']->getText('plugin_tracker', 'enable_notifications_alternate_text'); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
return $GLOBALS['Language']->getText('plugin_tracker', 'disable_notifications_alternate_text'); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
private function doesUserHaveUnsubscribedFromNotification(PFUser $user) { |
496
|
|
|
return $this->getDao()->doesUserHaveUnsubscribedFromNotifications($this->id, $user->getId()); |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
public function fetchHiddenTrackerId() { |
500
|
|
|
return '<input type="hidden" id="tracker_id" name="tracker_id" value="'.$this->getTrackerId().'"/>'; |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
public function getXRefAndTitle() { |
504
|
|
|
$hp = Codendi_HTMLPurifier::instance(); |
505
|
|
|
return '<span class="'. $this->getTracker()->getColor() .' xref-in-title">' . |
506
|
|
|
$this->getXRef() . |
507
|
|
|
'<span> -</span>'. |
508
|
|
|
'</span> '. |
509
|
|
|
$hp->purify($this->getTitle()); |
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
public function fetchColoredXRef() { |
513
|
|
|
return '<span class="colored-xref '. $this->getTracker()->getColor() .'"><a class="cross-reference" href="' . $this->getUri() . '">'. $this->getXRef() .'</a></span>'; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* Get the artifact title, or null if no title defined in semantics |
518
|
|
|
* |
519
|
|
|
* @return string the title of the artifact, or null if no title defined in semantics |
520
|
|
|
*/ |
521
|
|
|
public function getTitle() { |
522
|
|
|
if ( ! isset($this->title)) { |
523
|
|
|
$this->title = null; |
524
|
|
|
if ($title_field = Tracker_Semantic_Title::load($this->getTracker())->getField()) { |
525
|
|
|
if ($title_field->userCanRead()) { |
526
|
|
|
if ($last_changeset = $this->getLastChangeset()) { |
527
|
|
|
if ($title_field_value = $last_changeset->getValue($title_field)) { |
528
|
|
|
$this->title = $title_field_value->getText(); |
529
|
|
|
} |
530
|
|
|
} |
531
|
|
|
} |
532
|
|
|
} |
533
|
|
|
} |
534
|
|
|
return $this->title; |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
public function getCachedTitle() { |
538
|
|
|
return $this->title; |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
/** |
542
|
|
|
* @return PFUser[] |
543
|
|
|
*/ |
544
|
|
|
public function getAssignedTo(PFUser $user) { |
545
|
|
|
$assigned_to_field = Tracker_Semantic_Contributor::load($this->getTracker())->getField(); |
546
|
|
|
if ($assigned_to_field && $assigned_to_field->userCanRead($user)) { |
547
|
|
|
$field_value = $this->getLastChangeset()->getValue($assigned_to_field); |
548
|
|
|
if ($field_value) { |
549
|
|
|
$user_manager = $this->getUserManager(); |
550
|
|
|
$user_ids = $field_value->getValue(); |
551
|
|
|
$assigned_users = array(); |
552
|
|
|
foreach($user_ids as $user_id) { |
553
|
|
|
if ($user_id != 100) { |
554
|
|
|
$assigned_user = $user_manager->getUserById($user_id); |
555
|
|
|
$assigned_users[] = $assigned_user; |
556
|
|
|
} |
557
|
|
|
} |
558
|
|
|
return $assigned_users; |
559
|
|
|
} |
560
|
|
|
} |
561
|
|
|
return array(); |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* @param string $title |
566
|
|
|
*/ |
567
|
|
|
public function setTitle($title) { |
568
|
|
|
$this->title = $title; |
569
|
|
|
} |
570
|
|
|
|
571
|
|
|
/** |
572
|
|
|
* Get the artifact status, or null if no status defined in semantics |
573
|
|
|
* |
574
|
|
|
* @return string the status of the artifact, or null if no status defined in semantics |
575
|
|
|
*/ |
576
|
|
|
public function getStatus() { |
577
|
|
|
if ( ! isset($this->status)) { |
578
|
|
|
$last_changeset = $this->getLastChangeset(); |
579
|
|
|
if ($last_changeset) { |
580
|
|
|
$this->status = $this->getStatusForChangeset($last_changeset); |
581
|
|
|
} |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
return $this->status; |
585
|
|
|
} |
586
|
|
|
|
587
|
|
|
/** |
588
|
|
|
* Get the artifact status, or null if no status defined in semantics |
589
|
|
|
* |
590
|
|
|
* @return string the status of the artifact, or null if no status defined in semantics |
591
|
|
|
*/ |
592
|
|
|
public function getStatusForChangeset(Tracker_Artifact_Changeset $changeset) { |
593
|
|
|
$status_field = Tracker_Semantic_Status::load($this->getTracker())->getField(); |
594
|
|
|
if (! $status_field) { |
595
|
|
|
return null; |
596
|
|
|
} |
597
|
|
|
if (! $status_field->userCanRead()) { |
598
|
|
|
return null; |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
return $status_field->getFirstValueFor($changeset); |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
|
605
|
|
|
/** |
606
|
|
|
* @param String $status |
607
|
|
|
*/ |
608
|
|
|
public function setStatus($status) { |
609
|
|
|
$this->status = $status; |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
public function getSemanticStatusValue() { |
613
|
|
|
return $this->isOpen() ? self::STATUS_OPEN : self::STATUS_CLOSED; |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
public function isOpen() { |
617
|
|
|
return Tracker_Semantic_Status::load($this->getTracker())->isOpen($this); |
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
/** |
621
|
|
|
* |
622
|
|
|
* @param <type> $recipient |
|
|
|
|
623
|
|
|
* @param <type> $ignore_perms |
|
|
|
|
624
|
|
|
* @return <type> |
|
|
|
|
625
|
|
|
*/ |
626
|
|
|
public function fetchMailTitle($recipient, $format = 'text', $ignore_perms = false) { |
627
|
|
|
$output = ''; |
628
|
|
|
if ( $title_field = Tracker_Semantic_Title::load($this->getTracker())->getField() ) { |
629
|
|
|
if ( $ignore_perms || $title_field->userCanRead($recipient) ) { |
630
|
|
|
if ($value = $this->getLastChangeset()->getValue($title_field)) { |
631
|
|
|
if ($title = $value->getText() ) { |
632
|
|
|
$output .= $title; |
633
|
|
|
} |
634
|
|
|
} |
635
|
|
|
} |
636
|
|
|
} |
637
|
|
|
return $output; |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
/** |
641
|
|
|
* Returns HTML code to display the artifact history |
642
|
|
|
* |
643
|
|
|
* @return string The HTML code for artifact history |
644
|
|
|
*/ |
645
|
|
|
protected function fetchHistory() { |
646
|
|
|
$html = ''; |
647
|
|
|
$html .= '<h4 class="tracker_artifact_tab">History</h4>'; |
648
|
|
|
$h = new Tracker_History($this); |
649
|
|
|
$html .= $h->fetch(); |
650
|
|
|
return $html; |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
/** |
654
|
|
|
* Returns HTML code to display the artifact history |
655
|
|
|
* |
656
|
|
|
* @param Codendi_Request $request The data from the user |
657
|
|
|
* |
658
|
|
|
* @return String The valid followup comment format |
659
|
|
|
*/ |
660
|
|
|
public function validateCommentFormat($request, $comment_format_field_name) { |
661
|
|
|
$comment_format = $request->get($comment_format_field_name); |
662
|
|
|
return Tracker_Artifact_Changeset_Comment::checkCommentFormat($comment_format); |
663
|
|
|
} |
664
|
|
|
|
665
|
|
|
/** |
666
|
|
|
* Process the artifact functions |
667
|
|
|
* |
668
|
|
|
* @param Tracker_IDisplayTrackerLayout $layout Displays the page header and footer |
669
|
|
|
* @param Codendi_Request $request The data from the user |
670
|
|
|
* @param PFUser $current_user The current user |
671
|
|
|
* |
672
|
|
|
* @return void |
673
|
|
|
*/ |
674
|
|
|
public function process(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) { |
675
|
|
|
switch ($request->get('func')) { |
676
|
|
|
case 'get-children': |
677
|
|
|
$children = $this->getChildPresenterCollection($current_user); |
678
|
|
|
$GLOBALS['Response']->sendJSON($children); |
679
|
|
|
exit; |
|
|
|
|
680
|
|
|
break; |
|
|
|
|
681
|
|
|
case 'update-comment': |
682
|
|
|
if ((int)$request->get('changeset_id') && $request->exist('content')) { |
683
|
|
|
if ($changeset = $this->getChangeset($request->get('changeset_id'))) { |
684
|
|
|
$comment_format = $this->validateCommentFormat($request, 'comment_format'); |
685
|
|
|
$changeset->updateComment($request->get('content'), $current_user, $comment_format, $_SERVER['REQUEST_TIME']); |
686
|
|
|
if ($request->isAjax()) { |
687
|
|
|
//We assume that we can only change a comment from a followUp |
688
|
|
|
echo $changeset->getComment()->fetchFollowUp(); |
689
|
|
|
} |
690
|
|
|
} |
691
|
|
|
} |
692
|
|
|
break; |
693
|
|
|
case 'preview-attachment': |
694
|
|
|
case 'show-attachment': |
695
|
|
|
if ((int)$request->get('field') && (int)$request->get('attachment')) { |
696
|
|
|
$ff = Tracker_FormElementFactory::instance(); |
697
|
|
|
//TODO: check that the user can read the field |
698
|
|
|
if ($field = $ff->getFormElementByid($request->get('field'))) { |
699
|
|
|
$method = explode('-', $request->get('func')); |
700
|
|
|
$method = $method[0]; |
701
|
|
|
$method .= 'Attachment'; |
702
|
|
|
if (method_exists($field, $method)) { |
703
|
|
|
$field->$method($request->get('attachment')); |
704
|
|
|
} |
705
|
|
|
} |
706
|
|
|
} |
707
|
|
|
break; |
708
|
|
|
case 'artifact-delete-changeset': |
709
|
|
|
// @see comment in Tracker_Artifact_Changeset::fetchFollowUp() |
710
|
|
|
//if ($changeset = $this->getChangeset($request->get('changeset'))) { |
711
|
|
|
// $changeset->delete($current_user); |
712
|
|
|
//} |
713
|
|
|
$GLOBALS['Response']->redirect('?aid='. $this->id); |
714
|
|
|
break; |
715
|
|
|
case 'artifact-update': |
716
|
|
|
$action = new Tracker_Action_UpdateArtifact( |
717
|
|
|
$this, |
718
|
|
|
$this->getFormElementFactory(), |
719
|
|
|
$this->getEventManager() |
720
|
|
|
); |
721
|
|
|
$action->process($layout, $request, $current_user); |
722
|
|
|
break; |
723
|
|
|
case 'check-user-can-link-and-unlink': |
724
|
|
|
$source_artifact = (int)$request->get('from-artifact'); |
725
|
|
|
$destination_artifact = (int)$request->get('to-artifact'); |
726
|
|
|
|
727
|
|
|
if (! ($this->userHasRankingPermissions($source_artifact) && $this->userHasRankingPermissions($destination_artifact))) { |
728
|
|
|
$this->sendUserDoesNotHavePermissionsErrorCode(); |
729
|
|
|
} |
730
|
|
|
break; |
731
|
|
|
case 'unassociate-artifact-to': |
732
|
|
|
$artlink_fields = $this->getFormElementFactory()->getUsedArtifactLinkFields($this->getTracker()); |
733
|
|
|
$linked_artifact_id = $request->get('linked-artifact-id'); |
734
|
|
|
|
735
|
|
|
if (! $this->userHasRankingPermissions($this->getId())) { |
736
|
|
|
$this->sendUserDoesNotHavePermissionsErrorCode(); |
737
|
|
|
break; |
738
|
|
|
} |
739
|
|
|
|
740
|
|
|
if (count($artlink_fields)) { |
741
|
|
|
$this->unlinkArtifact($artlink_fields, $linked_artifact_id, $current_user); |
742
|
|
|
$this->summonArtifactAssociators($request, $current_user, $linked_artifact_id); |
743
|
|
|
} else { |
744
|
|
|
$GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker', 'must_have_artifact_link_field')); |
745
|
|
|
$GLOBALS['Response']->sendStatusCode(400); |
746
|
|
|
} |
747
|
|
|
break; |
748
|
|
|
case 'associate-artifact-to': |
749
|
|
|
$linked_artifact_id = $request->get('linked-artifact-id'); |
750
|
|
|
|
751
|
|
|
if (! $this->userHasRankingPermissions($this->getId())) { |
752
|
|
|
$this->sendUserDoesNotHavePermissionsErrorCode(); |
753
|
|
|
break; |
754
|
|
|
} |
755
|
|
|
|
756
|
|
|
if (!$this->linkArtifact($linked_artifact_id, $current_user)) { |
|
|
|
|
757
|
|
|
$GLOBALS['Response']->sendStatusCode(400); |
758
|
|
|
} else { |
759
|
|
|
$this->summonArtifactAssociators($request, $current_user, $linked_artifact_id); |
760
|
|
|
} |
761
|
|
|
break; |
762
|
|
|
case 'higher-priority-than': |
763
|
|
|
$target_id = (int)$request->get('target-id'); |
764
|
|
|
$milestone_id = (int)$request->get('milestone-id'); |
765
|
|
|
$project_id = $this->getProjectId(); |
766
|
|
|
|
767
|
|
|
$user_is_authorized = $this->userHasRankingPermissions($milestone_id); |
768
|
|
|
|
769
|
|
|
if (! $this->userHasRankingPermissions($milestone_id)) { |
770
|
|
|
$this->sendUserDoesNotHavePermissionsErrorCode(); |
771
|
|
|
break; |
772
|
|
|
} |
773
|
|
|
|
774
|
|
|
$this->getPriorityManager()->moveArtifactBeforeWithHistoryChangeLogging($this->getId(), $target_id, $milestone_id, $project_id); |
775
|
|
|
break; |
776
|
|
|
case 'lesser-priority-than': |
777
|
|
|
$target_id = (int)$request->get('target-id'); |
778
|
|
|
$milestone_id = (int)$request->get('milestone-id'); |
779
|
|
|
$project_id = $this->getProjectId(); |
780
|
|
|
|
781
|
|
|
if (! $this->userHasRankingPermissions($milestone_id)) { |
782
|
|
|
$this->sendUserDoesNotHavePermissionsErrorCode(); |
783
|
|
|
break; |
784
|
|
|
} |
785
|
|
|
|
786
|
|
|
$this->getPriorityManager()->moveArtifactAfterWithHistoryChangeLogging($this->getId(), $target_id, $milestone_id, $project_id); |
787
|
|
|
break; |
788
|
|
|
case 'show-in-overlay': |
789
|
|
|
$renderer = new Tracker_Artifact_EditOverlayRenderer($this, $this->getEventManager()); |
790
|
|
|
$renderer->display($request, $current_user); |
791
|
|
|
break; |
792
|
|
|
case 'get-new-changesets': |
793
|
|
|
$changeset_id = $request->getValidated('changeset_id', 'uint', 0); |
794
|
|
|
$changeset_factory = $this->getChangesetFactory(); |
795
|
|
|
$GLOBALS['Response']->sendJSON($changeset_factory->getNewChangesetsFormattedForJson($this, $changeset_id)); |
796
|
|
|
break; |
797
|
|
|
case 'edit': |
798
|
|
|
$GLOBALS['Response']->redirect($this->getUri()); |
799
|
|
|
break; |
800
|
|
|
case 'get-edit-in-place': |
801
|
|
|
$renderer = new Tracker_Artifact_Renderer_EditInPlaceRenderer($this, $this->getMustacheRenderer()); |
802
|
|
|
$renderer->display($current_user, $request); |
803
|
|
|
break; |
804
|
|
|
case 'update-in-place': |
805
|
|
|
$renderer = new Tracker_Artifact_Renderer_EditInPlaceRenderer($this, $this->getMustacheRenderer()); |
806
|
|
|
$renderer->updateArtifact($request, $current_user); |
807
|
|
|
break; |
808
|
|
|
case 'copy-artifact': |
809
|
|
|
$art_link = $this->fetchDirectLinkToArtifact(); |
810
|
|
|
$GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_artifact', 'copy_mode_info', array($art_link)), CODENDI_PURIFIER_LIGHT); |
811
|
|
|
|
812
|
|
|
$renderer = new Tracker_Artifact_CopyRenderer($this->getEventManager(), $this, $this->getFormElementFactory(), $layout); |
813
|
|
|
$renderer->display($request, $current_user); |
814
|
|
|
break; |
815
|
|
|
case 'manage-subscription': |
816
|
|
|
$artifact_subscriber = new Tracker_ArtifactNotificationSubscriber($this, $this->getDao()); |
817
|
|
|
|
818
|
|
|
if ($this->doesUserHaveUnsubscribedFromNotification($current_user)) { |
819
|
|
|
$artifact_subscriber->subscribeUser($current_user, $request); |
820
|
|
|
break; |
821
|
|
|
} |
822
|
|
|
|
823
|
|
|
$artifact_subscriber->unsubscribeUser($current_user, $request); |
824
|
|
|
break; |
825
|
|
|
|
826
|
|
|
default: |
827
|
|
|
if ($request->isAjax()) { |
828
|
|
|
echo $this->fetchTooltip($current_user); |
829
|
|
|
} else { |
830
|
|
|
$renderer = new Tracker_Artifact_ReadOnlyRenderer($this->getEventManager(), $this, $this->getFormElementFactory(), $layout); |
831
|
|
|
$renderer->display($request, $current_user); |
832
|
|
|
} |
833
|
|
|
break; |
834
|
|
|
} |
835
|
|
|
} |
836
|
|
|
|
837
|
|
|
private function sendUserDoesNotHavePermissionsErrorCode() { |
838
|
|
|
$GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker', 'unsufficient_permissions_for_ranking')); |
839
|
|
|
$GLOBALS['Response']->sendStatusCode(403); |
840
|
|
|
} |
841
|
|
|
|
842
|
|
|
private function userHasRankingPermissions($milestone_id) { |
843
|
|
|
$user_is_authorized = true; |
844
|
|
|
|
845
|
|
|
$this->getEventManager()->processEvent( |
846
|
|
|
ITEM_PRIORITY_CHANGE, |
847
|
|
|
array( |
848
|
|
|
'user_is_authorized' => &$user_is_authorized, |
849
|
|
|
'group_id' => $this->getProjectId(), |
850
|
|
|
'milestone_id' => $milestone_id, |
851
|
|
|
'user' => $this->getCurrentUser() |
852
|
|
|
) |
853
|
|
|
); |
854
|
|
|
|
855
|
|
|
return $user_is_authorized; |
856
|
|
|
} |
857
|
|
|
|
858
|
|
|
private function getProjectId() { |
859
|
|
|
return $this->getTracker()->getGroupId(); |
860
|
|
|
} |
861
|
|
|
|
862
|
|
|
/** @return Tracker_Artifact_PriorityManager */ |
863
|
|
|
protected function getPriorityManager() { |
864
|
|
|
return new Tracker_Artifact_PriorityManager( |
865
|
|
|
new Tracker_Artifact_PriorityDao(), |
866
|
|
|
new Tracker_Artifact_PriorityHistoryDao(), |
867
|
|
|
UserManager::instance(), |
868
|
|
|
Tracker_ArtifactFactory::instance() |
869
|
|
|
); |
870
|
|
|
} |
871
|
|
|
|
872
|
|
|
/** @return Tracker_Artifact[] */ |
873
|
|
|
public function getChildrenForUser(PFUser $current_user) { |
874
|
|
|
$children = array(); |
875
|
|
|
foreach ($this->getArtifactFactory()->getChildren($this) as $child) { |
876
|
|
|
if ($child->userCanView($current_user)) { |
877
|
|
|
$children[] = $child; |
878
|
|
|
} |
879
|
|
|
} |
880
|
|
|
return $children; |
881
|
|
|
} |
882
|
|
|
|
883
|
|
|
/** @return Tracker_ArtifactChildPresenter[] */ |
884
|
|
|
private function getChildPresenterCollection(PFUser $current_user) { |
885
|
|
|
$presenters = array(); |
886
|
|
|
foreach ($this->getChildrenForUser($current_user) as $child) { |
887
|
|
|
$tracker = $child->getTracker(); |
888
|
|
|
$semantics = Tracker_Semantic_Status::load($tracker); |
889
|
|
|
|
890
|
|
|
$presenters[] = new Tracker_ArtifactChildPresenter($child, $this, $semantics); |
891
|
|
|
} |
892
|
|
|
return $presenters; |
893
|
|
|
} |
894
|
|
|
|
895
|
|
|
public function hasChildren() { |
896
|
|
|
return $this->getArtifactFactory()->hasChildren($this); |
897
|
|
|
} |
898
|
|
|
|
899
|
|
|
/** |
900
|
|
|
* @see Tracker_CardPresenter::getAccentColor() |
901
|
|
|
* |
902
|
|
|
* @return string |
903
|
|
|
*/ |
904
|
|
|
public function getCardAccentColor(PFUser $current_user) { |
905
|
|
|
$selectbox = $this->getFormElementFactory()->getSelectboxFieldByNameForUser( |
906
|
|
|
$this->getTrackerId(), |
907
|
|
|
Tracker::TYPE_FIELD_NAME, |
908
|
|
|
$current_user |
909
|
|
|
); |
910
|
|
|
if (! $selectbox) { |
911
|
|
|
return ''; |
912
|
|
|
} |
913
|
|
|
|
914
|
|
|
return $selectbox->getCurrentDecoratorColor($this); |
915
|
|
|
} |
916
|
|
|
|
917
|
|
|
/** |
918
|
|
|
* @return string html |
919
|
|
|
*/ |
920
|
|
|
public function fetchDirectLinkToArtifact() { |
921
|
|
|
return '<a class="direct-link-to-artifact" href="'. $this->getUri() . '">' . $this->getXRef() . '</a>'; |
922
|
|
|
} |
923
|
|
|
|
924
|
|
|
/** |
925
|
|
|
* @return string html |
926
|
|
|
*/ |
927
|
|
|
public function fetchDirectLinkToArtifactWithTitle() { |
928
|
|
|
return '<a class="direct-link-to-artifact" href="'. $this->getUri() . '">' . $this->getXRefAndTitle() . '</a>'; |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
/** |
932
|
|
|
* @return string html |
933
|
|
|
*/ |
934
|
|
|
public function fetchDirectLinkToArtifactWithoutXRef() { |
935
|
|
|
$hp = Codendi_HTMLPurifier::instance(); |
936
|
|
|
return '<a class="direct-link-to-artifact" href="'. $this->getUri() . '">' . $hp->purify($this->getTitle()) . '</a>'; |
937
|
|
|
} |
938
|
|
|
|
939
|
|
|
public function getRestUri() { |
940
|
|
|
return self::REST_ROUTE . '/' . $this->getId(); |
941
|
|
|
} |
942
|
|
|
|
943
|
|
|
/** |
944
|
|
|
* @return string |
945
|
|
|
*/ |
946
|
|
|
public function getUri() { |
947
|
|
|
return TRACKER_BASE_URL .'/?aid=' . $this->getId(); |
948
|
|
|
} |
949
|
|
|
|
950
|
|
|
/** |
951
|
|
|
* @return string the cross reference text: bug #42 |
952
|
|
|
*/ |
953
|
|
|
public function getXRef() { |
954
|
|
|
return $this->getTracker()->getItemName() . ' #' . $this->getId(); |
955
|
|
|
} |
956
|
|
|
|
957
|
|
|
/** |
958
|
|
|
* Fetch the html xref link to the artifact |
959
|
|
|
* |
960
|
|
|
* @return string html |
961
|
|
|
*/ |
962
|
|
|
public function fetchXRefLink() { |
963
|
|
|
return '<a class="cross-reference" href="/goto?'. http_build_query(array( |
964
|
|
|
'key' => $this->getTracker()->getItemName(), |
965
|
|
|
'val' => $this->getId(), |
966
|
|
|
'group_id' => $this->getTracker()->getGroupId(), |
967
|
|
|
)) .'">'. $this->getXRef() .'</a>'; |
968
|
|
|
} |
969
|
|
|
|
970
|
|
|
/** |
971
|
|
|
* Return the URL to use when you want to create a new artifact of $target_tracker type linked to current artifact |
972
|
|
|
* |
973
|
|
|
* @param Tracker $target_tracker |
974
|
|
|
* @return String |
975
|
|
|
*/ |
976
|
|
|
public function getSubmitNewArtifactLinkedToMeUri(Tracker $target_tracker) { |
977
|
|
|
return TRACKER_BASE_URL . '/?'.http_build_query(array( |
978
|
|
|
'tracker' => $target_tracker->getId(), |
979
|
|
|
'func' => 'new-artifact-link', |
980
|
|
|
'id' => $this->getId(), |
981
|
|
|
'immediate' => 1, |
982
|
|
|
)); |
983
|
|
|
} |
984
|
|
|
|
985
|
|
|
/** |
986
|
|
|
* Returns a Tracker_FormElementFactory instance |
987
|
|
|
* |
988
|
|
|
* @return Tracker_FormElementFactory |
989
|
|
|
*/ |
990
|
|
|
protected function getFormElementFactory() { |
991
|
|
|
if (empty($this->formElementFactory)) { |
992
|
|
|
$this->formElementFactory = Tracker_FormElementFactory::instance(); |
993
|
|
|
} |
994
|
|
|
return $this->formElementFactory; |
995
|
|
|
} |
996
|
|
|
|
997
|
|
|
public function setFormElementFactory(Tracker_FormElementFactory $factory) { |
998
|
|
|
$this->formElementFactory = $factory; |
999
|
|
|
} |
1000
|
|
|
|
1001
|
|
|
/** |
1002
|
|
|
* Returns a Tracker_ArtifactFactory instance |
1003
|
|
|
* |
1004
|
|
|
* @return Tracker_ArtifactFactory |
1005
|
|
|
*/ |
1006
|
|
|
protected function getArtifactFactory() { |
1007
|
|
|
if ($this->artifact_factory) { |
1008
|
|
|
return $this->artifact_factory; |
1009
|
|
|
} |
1010
|
|
|
return Tracker_ArtifactFactory::instance(); |
1011
|
|
|
} |
1012
|
|
|
|
1013
|
|
|
public function setArtifactFactory(Tracker_ArtifactFactory $artifact_factory) { |
1014
|
|
|
$this->artifact_factory = $artifact_factory; |
1015
|
|
|
} |
1016
|
|
|
|
1017
|
|
|
/** |
1018
|
|
|
* Create the initial changeset of this artifact |
1019
|
|
|
* |
1020
|
|
|
* @param array $fields_data The artifact fields values |
1021
|
|
|
* @param PFUser $submitter The user who did the artifact submission |
1022
|
|
|
* @param integer $submitted_on When the changeset is created |
1023
|
|
|
* |
1024
|
|
|
* @return int The Id of the initial changeset, or null if fields were not valid |
1025
|
|
|
*/ |
1026
|
|
|
public function createInitialChangeset($fields_data, $submitter, $submitted_on) { |
1027
|
|
|
$creator = new Tracker_Artifact_Changeset_InitialChangesetCreator( |
1028
|
|
|
new Tracker_Artifact_Changeset_InitialChangesetFieldsValidator($this->getFormElementFactory()), |
1029
|
|
|
$this->getFormElementFactory(), |
1030
|
|
|
$this->getChangesetDao(), |
1031
|
|
|
$this->getArtifactFactory(), |
1032
|
|
|
$this->getEventManager() |
1033
|
|
|
); |
1034
|
|
|
|
1035
|
|
|
return $creator->create($this, $fields_data, $submitter, $submitted_on); |
1036
|
|
|
} |
1037
|
|
|
|
1038
|
|
|
public function getErrors() { |
1039
|
|
|
$list_errors = array(); |
1040
|
|
|
$is_valid = true; |
1041
|
|
|
$used_fields = $this->getFormElementFactory()->getUsedFields($this->getTracker()); |
1042
|
|
|
foreach ($used_fields as $field) { |
1043
|
|
|
if ($field->hasErrors()) { |
1044
|
|
|
$list_errors[] = $field->getId(); |
1045
|
|
|
} |
1046
|
|
|
} |
1047
|
|
|
return $list_errors; |
1048
|
|
|
} |
1049
|
|
|
|
1050
|
|
|
/** |
1051
|
|
|
* Update an artifact (means create a new changeset) |
1052
|
|
|
* |
1053
|
|
|
* @param array $fields_data Artifact fields values |
1054
|
|
|
* @param string $comment The comment (follow-up) associated with the artifact update |
1055
|
|
|
* @param PFUser $submitter The user who is doing the update |
1056
|
|
|
* @param boolean $send_notification true if a notification must be sent, false otherwise |
1057
|
|
|
* @param string $comment_format The comment (follow-up) type ("text" | "html") |
1058
|
|
|
* |
1059
|
|
|
* @throws Tracker_Exception In the validation |
1060
|
|
|
* @throws Tracker_NoChangeException In the validation |
1061
|
|
|
* @return Tracker_Artifact_Changeset|Boolean The new changeset if update is done without error, false otherwise |
1062
|
|
|
*/ |
1063
|
|
|
public function createNewChangeset($fields_data, $comment, PFUser $submitter, $send_notification = true, $comment_format = Tracker_Artifact_Changeset_Comment::TEXT_COMMENT) { |
1064
|
|
|
$submitted_on = $_SERVER['REQUEST_TIME']; |
1065
|
|
|
|
1066
|
|
|
$creator = new Tracker_Artifact_Changeset_NewChangesetCreator( |
1067
|
|
|
new Tracker_Artifact_Changeset_NewChangesetFieldsValidator($this->getFormElementFactory()), |
1068
|
|
|
$this->getFormElementFactory(), |
1069
|
|
|
$this->getChangesetDao(), |
1070
|
|
|
$this->getChangesetCommentDao(), |
1071
|
|
|
$this->getArtifactFactory(), |
1072
|
|
|
$this->getEventManager(), |
1073
|
|
|
$this->getReferenceManager() |
1074
|
|
|
); |
1075
|
|
|
return $creator->create($this, $fields_data, $comment, $submitter, $submitted_on, $send_notification, $comment_format); |
1076
|
|
|
} |
1077
|
|
|
|
1078
|
|
|
/** |
1079
|
|
|
* @return ReferenceManager |
1080
|
|
|
*/ |
1081
|
|
|
public function getReferenceManager() { |
1082
|
|
|
return ReferenceManager::instance(); |
1083
|
|
|
} |
1084
|
|
|
|
1085
|
|
|
/** |
1086
|
|
|
* Returns the tracker Id this artifact belongs to |
1087
|
|
|
* |
1088
|
|
|
* @return int The tracker Id this artifact belongs to |
1089
|
|
|
*/ |
1090
|
|
|
public function getTrackerId() { |
1091
|
|
|
return $this->tracker_id; |
1092
|
|
|
} |
1093
|
|
|
|
1094
|
|
|
/** |
1095
|
|
|
* Returns the tracker this artifact belongs to |
1096
|
|
|
* |
1097
|
|
|
* @return Tracker The tracker this artifact belongs to |
1098
|
|
|
*/ |
1099
|
|
|
public function getTracker() { |
1100
|
|
|
if (!isset($this->tracker)) { |
1101
|
|
|
$this->tracker = TrackerFactory::instance()->getTrackerByid($this->tracker_id); |
1102
|
|
|
} |
1103
|
|
|
return $this->tracker; |
1104
|
|
|
} |
1105
|
|
|
|
1106
|
|
|
public function setTracker(Tracker $tracker) { |
1107
|
|
|
$this->tracker = $tracker; |
1108
|
|
|
$this->tracker_id = $tracker->getId(); |
1109
|
|
|
} |
1110
|
|
|
|
1111
|
|
|
/** |
1112
|
|
|
* Returns the last modified date of the artifact |
1113
|
|
|
* |
1114
|
|
|
* @return Integer The timestamp (-1 if no date) |
1115
|
|
|
*/ |
1116
|
|
|
public function getLastUpdateDate() { |
1117
|
|
|
$last_changeset = $this->getLastChangeset(); |
1118
|
|
|
if ($last_changeset) { |
1119
|
|
|
return $last_changeset->getSubmittedOn(); |
1120
|
|
|
} |
1121
|
|
|
return -1; |
1122
|
|
|
} |
1123
|
|
|
|
1124
|
|
|
public function wasLastModifiedByAnonymous() { |
1125
|
|
|
$last_changeset = $this->getLastChangeset(); |
1126
|
|
|
if ($last_changeset) { |
1127
|
|
|
if ($last_changeset->getSubmittedBy()) { |
1128
|
|
|
return false; |
1129
|
|
|
} |
1130
|
|
|
return true; |
1131
|
|
|
} |
1132
|
|
|
return false; |
1133
|
|
|
} |
1134
|
|
|
|
1135
|
|
|
public function getLastModifiedBy() { |
1136
|
|
|
$last_changeset = $this->getLastChangeset(); |
1137
|
|
|
if ($last_changeset) { |
1138
|
|
|
if ($last_changeset->getSubmittedBy()) { |
1139
|
|
|
return $last_changeset->getSubmittedBy(); |
1140
|
|
|
} |
1141
|
|
|
return $last_changeset->getEmail(); |
1142
|
|
|
} |
1143
|
|
|
return $this->getSubmittedBy(); |
1144
|
|
|
} |
1145
|
|
|
|
1146
|
|
|
/** |
1147
|
|
|
* @return Integer |
1148
|
|
|
*/ |
1149
|
|
|
public function getVersionIdentifier() { |
1150
|
|
|
return $this->getLastUpdateDate(); |
1151
|
|
|
} |
1152
|
|
|
|
1153
|
|
|
/** |
1154
|
|
|
* Returns the latest changeset of this artifact |
1155
|
|
|
* |
1156
|
|
|
* @return Tracker_Artifact_Changeset The latest changeset of this artifact, or null if no latest changeset |
1157
|
|
|
*/ |
1158
|
|
|
public function getLastChangeset() { |
1159
|
|
|
if ($this->changesets === null) { |
1160
|
|
|
return $this->getChangesetFactory()->getLastChangeset($this); |
1161
|
|
|
} else { |
1162
|
|
|
$changesets = $this->getChangesets(); |
1163
|
|
|
return end($changesets); |
1164
|
|
|
} |
1165
|
|
|
} |
1166
|
|
|
|
1167
|
|
|
/** |
1168
|
|
|
* @return Tracker_Artifact_Changeset|null |
1169
|
|
|
*/ |
1170
|
|
|
public function getLastChangesetWithFieldValue(Tracker_FormElement_Field $field) { |
1171
|
|
|
return $this->getChangesetFactory()->getLastChangesetWithFieldValue($this, $field); |
1172
|
|
|
} |
1173
|
|
|
|
1174
|
|
|
/** |
1175
|
|
|
* Returns the first changeset of this artifact |
1176
|
|
|
* |
1177
|
|
|
* @return Tracker_Artifact_Changeset The first changeset of this artifact |
1178
|
|
|
*/ |
1179
|
|
|
public function getFirstChangeset() { |
1180
|
|
|
$changesets = $this->getChangesets(); |
1181
|
|
|
reset($changesets); |
1182
|
|
|
list(,$c) = each($changesets); |
1183
|
|
|
return $c; |
1184
|
|
|
} |
1185
|
|
|
|
1186
|
|
|
/** |
1187
|
|
|
* say if the changeset is the first one for this artifact |
1188
|
|
|
* |
1189
|
|
|
* @return bool |
1190
|
|
|
*/ |
1191
|
|
|
public function isFirstChangeset(Tracker_Artifact_Changeset $changeset) { |
1192
|
|
|
$c = $this->getFirstChangeset(); |
1193
|
|
|
return $c->getId() == $changeset->getId(); |
1194
|
|
|
} |
1195
|
|
|
|
1196
|
|
|
private function getPriorityHistory() { |
1197
|
|
|
return $this->getPriorityManager()->getArtifactPriorityHistory($this); |
1198
|
|
|
} |
1199
|
|
|
|
1200
|
|
|
public function cmpFollowups($a, $b) { |
1201
|
|
|
return ($a->getFollowUpDate() < $b->getFollowUpDate()) ? -1 : 1; |
1202
|
|
|
} |
1203
|
|
|
|
1204
|
|
|
public function getFollowupsContent() { |
1205
|
|
|
$followups_content = $this->getChangesets(); |
1206
|
|
|
$followups_content = array_merge($followups_content, $this->getPriorityHistory()); |
1207
|
|
|
|
1208
|
|
|
usort($followups_content, array($this, "cmpFollowups")); |
1209
|
|
|
|
1210
|
|
|
return $followups_content; |
1211
|
|
|
} |
1212
|
|
|
|
1213
|
|
|
/** |
1214
|
|
|
* Returns all the changesets of this artifact |
1215
|
|
|
* |
1216
|
|
|
* @return Tracker_Artifact_Changeset[] The changesets of this artifact |
1217
|
|
|
*/ |
1218
|
|
|
public function getChangesets() { |
1219
|
|
|
if ($this->changesets === null) { |
1220
|
|
|
$this->forceFetchAllChangesets(); |
1221
|
|
|
} |
1222
|
|
|
return $this->changesets; |
1223
|
|
|
} |
1224
|
|
|
|
1225
|
|
|
public function forceFetchAllChangesets() { |
1226
|
|
|
$this->changesets = $this->getChangesetFactory()->getChangesetsForArtifact($this); |
1227
|
|
|
} |
1228
|
|
|
|
1229
|
|
|
/** |
1230
|
|
|
* @param array $changesets array of Tracker_Artifact_Changeset |
1231
|
|
|
*/ |
1232
|
|
|
public function setChangesets(array $changesets) { |
1233
|
|
|
$this->changesets = $changesets; |
1234
|
|
|
} |
1235
|
|
|
|
1236
|
|
|
public function clearChangesets() { |
1237
|
|
|
$this->changesets = null; |
1238
|
|
|
} |
1239
|
|
|
|
1240
|
|
|
public function addChangeset(Tracker_Artifact_Changeset $changeset) { |
1241
|
|
|
$this->changesets[$changeset->getId()] = $changeset; |
1242
|
|
|
} |
1243
|
|
|
|
1244
|
|
|
/** |
1245
|
|
|
* Get all commentators of this artifact |
1246
|
|
|
* |
1247
|
|
|
* @return array of strings (username or emails) |
1248
|
|
|
*/ |
1249
|
|
|
public function getCommentators() { |
1250
|
|
|
$commentators = array(); |
1251
|
|
|
foreach ($this->getChangesets() as $c) { |
1252
|
|
|
if ($submitted_by = $c->getSubmittedBy()) { |
1253
|
|
|
if ($user = $this->getUserManager()->getUserById($submitted_by)) { |
1254
|
|
|
$commentators[] = $user->getUserName(); |
1255
|
|
|
} |
1256
|
|
|
} else if ($email = $c->getEmail()) { |
1257
|
|
|
$commentators[] = $email; |
1258
|
|
|
} |
1259
|
|
|
} |
1260
|
|
|
return $commentators; |
1261
|
|
|
} |
1262
|
|
|
|
1263
|
|
|
/** |
1264
|
|
|
* Return the ChangesetDao |
1265
|
|
|
* |
1266
|
|
|
* @return Tracker_Artifact_ChangesetDao The Dao |
1267
|
|
|
*/ |
1268
|
|
|
protected function getChangesetDao() { |
1269
|
|
|
return new Tracker_Artifact_ChangesetDao(); |
1270
|
|
|
} |
1271
|
|
|
|
1272
|
|
|
/** |
1273
|
|
|
* @return Tracker_Artifact_ChangesetFactory |
1274
|
|
|
*/ |
1275
|
|
|
protected function getChangesetFactory() { |
1276
|
|
|
return new Tracker_Artifact_ChangesetFactory( |
1277
|
|
|
$this->getChangesetDao(), |
1278
|
|
|
new Tracker_Artifact_ChangesetJsonFormatter( |
1279
|
|
|
$this->getMustacheRenderer() |
1280
|
|
|
) |
1281
|
|
|
); |
1282
|
|
|
} |
1283
|
|
|
|
1284
|
|
|
/** |
1285
|
|
|
* @return MustacheRenderer |
1286
|
|
|
*/ |
1287
|
|
|
private function getMustacheRenderer() { |
1288
|
|
|
return TemplateRendererFactory::build()->getRenderer(dirname(TRACKER_BASE_DIR).'/templates') ; |
1289
|
|
|
} |
1290
|
|
|
|
1291
|
|
|
/** |
1292
|
|
|
* Return the ChangesetCommentDao |
1293
|
|
|
* |
1294
|
|
|
* @return Tracker_Artifact_Changeset_CommentDao The Dao |
1295
|
|
|
*/ |
1296
|
|
|
protected function getChangesetCommentDao() { |
1297
|
|
|
return new Tracker_Artifact_Changeset_CommentDao(); |
1298
|
|
|
} |
1299
|
|
|
|
1300
|
|
|
/** |
1301
|
|
|
* Returns the changeset of this artifact with Id $changeset_id, or null if not found |
1302
|
|
|
* |
1303
|
|
|
* @param int $changeset_id The Id of the changeset to retrieve |
1304
|
|
|
* |
1305
|
|
|
* @return Tracker_Artifact_Changeset The changeset, or null if not found |
1306
|
|
|
*/ |
1307
|
|
|
public function getChangeset($changeset_id) { |
1308
|
|
|
if (! isset($this->changesets[$changeset_id])) { |
1309
|
|
|
$this->changesets[$changeset_id] = $this->getChangesetFactory()->getChangeset($this, $changeset_id); |
1310
|
|
|
} |
1311
|
|
|
return $this->changesets[$changeset_id]; |
1312
|
|
|
} |
1313
|
|
|
|
1314
|
|
|
/** |
1315
|
|
|
* Returns the previous changeset just before the changeset $changeset_id, or null if $changeset_id is the first one |
1316
|
|
|
* |
1317
|
|
|
* @param int $changeset_id The changeset reference |
1318
|
|
|
* |
1319
|
|
|
* @return Tracker_Artifact_Changeset The previous changeset, or null if not found |
1320
|
|
|
*/ |
1321
|
|
|
public function getPreviousChangeset($changeset_id) { |
1322
|
|
|
$previous = null; |
1323
|
|
|
$changesets = $this->getChangesets(); |
1324
|
|
|
reset($changesets); |
1325
|
|
|
while ((list(,$changeset) = each($changesets)) && $changeset->id != $changeset_id) { |
1326
|
|
|
$previous = $changeset; |
1327
|
|
|
} |
1328
|
|
|
return $previous; |
1329
|
|
|
} |
1330
|
|
|
|
1331
|
|
|
public function exportCommentsToSOAP() { |
1332
|
|
|
$soap_comments = array(); |
1333
|
|
|
foreach ($this->getChangesets() as $changeset) { |
1334
|
|
|
$changeset_comment = $changeset->exportCommentToSOAP(); |
1335
|
|
|
if ($changeset_comment) { |
1336
|
|
|
$soap_comments[] = $changeset_comment; |
1337
|
|
|
} |
1338
|
|
|
} |
1339
|
|
|
return $soap_comments; |
1340
|
|
|
} |
1341
|
|
|
|
1342
|
|
|
public function exportHistoryToSOAP(PFUser $user) { |
1343
|
|
|
$soap_comments = array(); |
1344
|
|
|
foreach ($this->getChangesets() as $changeset) { |
1345
|
|
|
$soap_comments[] = $changeset->getSoapValue($user); |
1346
|
|
|
} |
1347
|
|
|
return $soap_comments; |
1348
|
|
|
} |
1349
|
|
|
|
1350
|
|
|
/** |
1351
|
|
|
* Get the Id of this artifact |
1352
|
|
|
* |
1353
|
|
|
* @return int The Id of this artifact |
1354
|
|
|
*/ |
1355
|
|
|
public function getId() { |
1356
|
|
|
return $this->id; |
1357
|
|
|
} |
1358
|
|
|
|
1359
|
|
|
/** |
1360
|
|
|
* Set the Id of this artifact |
1361
|
|
|
* |
1362
|
|
|
* @param int $id the new id of the artifact |
1363
|
|
|
* |
1364
|
|
|
* @return Tracker_Artifact |
1365
|
|
|
*/ |
1366
|
|
|
public function setId($id) { |
1367
|
|
|
$this->id = $id; |
1368
|
|
|
return $this; |
1369
|
|
|
} |
1370
|
|
|
|
1371
|
|
|
/** |
1372
|
|
|
* Get the value for this field in the changeset |
1373
|
|
|
* |
1374
|
|
|
* @param Tracker_FormElement_Field $field The field |
1375
|
|
|
* @param Tracker_Artifact_Changeset $changeset The changeset. if null given take the last changeset of the artifact |
1376
|
|
|
* |
1377
|
|
|
* @return Tracker_Artifact_ChangesetValue | null |
1378
|
|
|
*/ |
1379
|
|
|
function getValue(Tracker_FormElement_Field $field, Tracker_Artifact_Changeset $changeset = null) { |
1380
|
|
|
if (!$changeset) { |
1381
|
|
|
$changeset = $this->getLastChangeset(); |
1382
|
|
|
} |
1383
|
|
|
if ($changeset) { |
1384
|
|
|
return $changeset->getValue($field); |
1385
|
|
|
} |
1386
|
|
|
return null; |
1387
|
|
|
} |
1388
|
|
|
|
1389
|
|
|
/** |
1390
|
|
|
* Returns the date (timestamp) the artifact ha been created |
1391
|
|
|
* |
1392
|
|
|
* @return int the timestamp for the date this aetifact was created |
1393
|
|
|
*/ |
1394
|
|
|
function getSubmittedOn() { |
1395
|
|
|
return $this->submitted_on; |
1396
|
|
|
} |
1397
|
|
|
|
1398
|
|
|
/** |
1399
|
|
|
* Returns the user who submitted the artifact |
1400
|
|
|
* |
1401
|
|
|
* @return int the user id |
1402
|
|
|
*/ |
1403
|
|
|
function getSubmittedBy() { |
1404
|
|
|
return $this->submitted_by; |
1405
|
|
|
} |
1406
|
|
|
|
1407
|
|
|
/** |
1408
|
|
|
* The user who created the artifact |
1409
|
|
|
* |
1410
|
|
|
* @return PFUser |
1411
|
|
|
*/ |
1412
|
|
|
public function getSubmittedByUser() { |
1413
|
|
|
if (! isset($this->submitted_by_user)) { |
1414
|
|
|
$this->submitted_by_user = $this->getUserManager()->getUserById($this->submitted_by); |
1415
|
|
|
} |
1416
|
|
|
return $this->submitted_by_user; |
1417
|
|
|
} |
1418
|
|
|
|
1419
|
|
|
public function setSubmittedByUser(PFUser $user) { |
1420
|
|
|
$this->submitted_by_user = $user; |
1421
|
|
|
$this->submitted_by = $user->getId(); |
1422
|
|
|
} |
1423
|
|
|
|
1424
|
|
|
/** |
1425
|
|
|
* Returns the id of the artifact in this tracker |
1426
|
|
|
* |
1427
|
|
|
* @return int the artifact id |
1428
|
|
|
*/ |
1429
|
|
|
public function getPerTrackerArtifactId() { |
1430
|
|
|
if ($this->per_tracker_id == null) { |
1431
|
|
|
$this->per_tracker_id = $this->getDao()->getPerTrackerArtifactId($this->id); |
1432
|
|
|
} |
1433
|
|
|
return $this->per_tracker_id; |
1434
|
|
|
} |
1435
|
|
|
|
1436
|
|
|
/** |
1437
|
|
|
* Returns ids of user who unsubscribed to artifact notification |
1438
|
|
|
* |
1439
|
|
|
* @return array |
1440
|
|
|
*/ |
1441
|
|
|
public function getUnsubscribersIds() { |
1442
|
|
|
$unsubscribers_ids = array(); |
1443
|
|
|
foreach ($this->getDao()->getUnsubscribersIds($this->id) as $row) { |
|
|
|
|
1444
|
|
|
$unsubscribers_ids[] = $row['user_id']; |
1445
|
|
|
} |
1446
|
|
|
return $unsubscribers_ids; |
1447
|
|
|
} |
1448
|
|
|
|
1449
|
|
|
/** |
1450
|
|
|
* Return Workflow the artifact should respect |
1451
|
|
|
* |
1452
|
|
|
* @return Workflow |
1453
|
|
|
*/ |
1454
|
|
|
public function getWorkflow() { |
1455
|
|
|
$workflow = $this->getTracker()->getWorkflow(); |
1456
|
|
|
$workflow->setArtifact($this); |
1457
|
|
|
return $workflow; |
1458
|
|
|
} |
1459
|
|
|
|
1460
|
|
|
/** |
1461
|
|
|
* Get the UserManager instance |
1462
|
|
|
* |
1463
|
|
|
* @return UserManager |
1464
|
|
|
*/ |
1465
|
|
|
public function getUserManager() { |
1466
|
|
|
return UserManager::instance(); |
1467
|
|
|
} |
1468
|
|
|
|
1469
|
|
|
private function getCurrentUser() { |
1470
|
|
|
return $this->getUserManager()->getCurrentUser(); |
1471
|
|
|
} |
1472
|
|
|
|
1473
|
|
|
/** |
1474
|
|
|
* Get the ProjectManager instance |
1475
|
|
|
* |
1476
|
|
|
* @return ProjectManager |
1477
|
|
|
*/ |
1478
|
|
|
private function getProjectManager() { |
1479
|
|
|
return ProjectManager::instance(); |
1480
|
|
|
} |
1481
|
|
|
|
1482
|
|
|
/** |
1483
|
|
|
* User want to link an artifact to the current one |
1484
|
|
|
* |
1485
|
|
|
* @param int $linked_artifact_id The id of the artifact to link |
1486
|
|
|
* @param PFUser $current_user The user who made the link |
1487
|
|
|
* |
1488
|
|
|
* @return bool true if success false otherwise |
1489
|
|
|
*/ |
1490
|
|
|
public function linkArtifact($linked_artifact_id, PFUser $current_user) { |
1491
|
|
|
$artlink_fields = $this->getFormElementFactory()->getUsedArtifactLinkFields($this->getTracker()); |
1492
|
|
|
if (count($artlink_fields)) { |
1493
|
|
|
$comment = ''; |
1494
|
|
|
$artlink_field = $artlink_fields[0]; |
1495
|
|
|
$fields_data = array(); |
1496
|
|
|
$fields_data[$artlink_field->getId()]['new_values'] = $linked_artifact_id; |
1497
|
|
|
|
1498
|
|
|
try { |
1499
|
|
|
$this->createNewChangeset($fields_data, $comment, $current_user); |
1500
|
|
|
return true; |
1501
|
|
|
} catch (Tracker_NoChangeException $e) { |
1502
|
|
|
$GLOBALS['Response']->addFeedback('info', $e->getMessage(), CODENDI_PURIFIER_LIGHT); |
1503
|
|
|
return false; |
1504
|
|
|
} catch (Tracker_Exception $e) { |
1505
|
|
|
$GLOBALS['Response']->addFeedback('error', $e->getMessage()); |
1506
|
|
|
return false; |
1507
|
|
|
} |
1508
|
|
|
} else { |
1509
|
|
|
$GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker', 'must_have_artifact_link_field')); |
1510
|
|
|
} |
1511
|
|
|
} |
1512
|
|
|
|
1513
|
|
|
/** |
1514
|
|
|
* User want to link an artifact to the current one |
1515
|
|
|
* |
1516
|
|
|
* @param array $linked_artifact_ids The ids of the artifacts to link |
1517
|
|
|
* @param PFUser $current_user The user who made the link |
1518
|
|
|
* |
1519
|
|
|
* @return bool true if success false otherwise |
1520
|
|
|
*/ |
1521
|
|
|
public function linkArtifacts($linked_artifact_ids, PFUser $current_user) { |
1522
|
|
|
$linked_artifact_ids = implode(',', $linked_artifact_ids); |
1523
|
|
|
|
1524
|
|
|
return $this->linkArtifact($linked_artifact_ids, $current_user); |
1525
|
|
|
} |
1526
|
|
|
|
1527
|
|
|
private function unlinkArtifact($artlink_fields, $linked_artifact_id, PFUser $current_user) { |
1528
|
|
|
$comment = ''; |
1529
|
|
|
$artlink_field = $artlink_fields[0]; |
1530
|
|
|
$fields_data = array(); |
1531
|
|
|
$fields_data[$artlink_field->getId()]['new_values'] = ''; |
1532
|
|
|
$fields_data[$artlink_field->getId()]['removed_values'] = array($linked_artifact_id => 1); |
1533
|
|
|
|
1534
|
|
|
try { |
1535
|
|
|
$this->createNewChangeset($fields_data, $comment, $current_user); |
1536
|
|
|
} catch (Tracker_NoChangeException $e) { |
1537
|
|
|
$GLOBALS['Response']->addFeedback('info', $e->getMessage(), CODENDI_PURIFIER_LIGHT); |
1538
|
|
|
} catch (Tracker_Exception $e) { |
1539
|
|
|
$GLOBALS['Response']->addFeedback('error', $e->getMessage()); |
1540
|
|
|
} |
1541
|
|
|
} |
1542
|
|
|
|
1543
|
|
|
/** |
1544
|
|
|
* Get artifacts linked to the current artifact |
1545
|
|
|
* |
1546
|
|
|
* @param PFUser $user The user who should see the artifacts |
1547
|
|
|
* |
1548
|
|
|
* @return Tracker_Artifact[] |
1549
|
|
|
*/ |
1550
|
|
|
public function getLinkedArtifacts(PFUser $user) { |
1551
|
|
|
$artifact_links = array(); |
1552
|
|
|
$artifact_link_field = $this->getAnArtifactLinkField($user); |
1553
|
|
|
if ($artifact_link_field) { |
1554
|
|
|
$artifact_links = $artifact_link_field->getLinkedArtifacts($this->getLastChangeset(), $user); |
|
|
|
|
1555
|
|
|
} |
1556
|
|
|
return $artifact_links; |
1557
|
|
|
} |
1558
|
|
|
|
1559
|
|
|
/** |
1560
|
|
|
* Get artifacts linked to the current artifact |
1561
|
|
|
* |
1562
|
|
|
* @see Tracker_FormElement_Field_ArtifactLink::getSlicedLinkedArtifacts() |
1563
|
|
|
* |
1564
|
|
|
* @param PFUser $user The user who should see the artifacts |
1565
|
|
|
* @param int $limit The number of artifact to fetch |
1566
|
|
|
* @param int $offset The offset |
1567
|
|
|
* |
1568
|
|
|
* @return Tracker_Artifact_PaginatedArtifacts |
1569
|
|
|
*/ |
1570
|
|
|
public function getSlicedLinkedArtifacts(PFUser $user, $limit, $offset) { |
1571
|
|
|
$artifact_link_field = $this->getAnArtifactLinkField($user); |
1572
|
|
|
if (! $artifact_link_field) { |
1573
|
|
|
return new Tracker_Artifact_PaginatedArtifacts(array(), 0); |
1574
|
|
|
} |
1575
|
|
|
|
1576
|
|
|
return $artifact_link_field->getSlicedLinkedArtifacts($this->getLastChangeset(), $user, $limit, $offset); |
|
|
|
|
1577
|
|
|
} |
1578
|
|
|
|
1579
|
|
|
/** |
1580
|
|
|
* Get artifacts linked to the current artifact and sub artifacts |
1581
|
|
|
* |
1582
|
|
|
* @param PFUser $user The user who should see the artifacts |
1583
|
|
|
* |
1584
|
|
|
* @return Array of Tracker_Artifact |
1585
|
|
|
*/ |
1586
|
|
|
public function getLinkedArtifactsOfHierarchy(PFUser $user) { |
1587
|
|
|
$artifact_links = $this->getLinkedArtifacts($user); |
1588
|
|
|
$allowed_trackers = $this->getAllowedChildrenTypes(); |
1589
|
|
|
foreach ($artifact_links as $artifact_link) { |
1590
|
|
|
$tracker = $artifact_link->getTracker(); |
1591
|
|
|
if (in_array($tracker, $allowed_trackers)) { |
1592
|
|
|
$sub_linked_artifacts = $artifact_link->getLinkedArtifactsOfHierarchy($user); |
1593
|
|
|
$artifact_links = array_merge($artifact_links, $sub_linked_artifacts); |
1594
|
|
|
} |
1595
|
|
|
} |
1596
|
|
|
return $artifact_links; |
1597
|
|
|
} |
1598
|
|
|
|
1599
|
|
|
/** |
1600
|
|
|
* @return Tracker[] |
1601
|
|
|
*/ |
1602
|
|
|
public function getAllowedChildrenTypes() { |
1603
|
|
|
return $this->getHierarchyFactory()->getChildren($this->getTrackerId()); |
1604
|
|
|
} |
1605
|
|
|
|
1606
|
|
|
/** |
1607
|
|
|
* @return Tracker[] |
1608
|
|
|
*/ |
1609
|
|
|
public function getAllowedChildrenTypesForUser(PFUser $user) { |
1610
|
|
|
$allowed_children = array(); |
1611
|
|
|
foreach ($this->getAllowedChildrenTypes() as $tracker) { |
1612
|
|
|
if ($tracker->userCanSubmitArtifact($user)) { |
1613
|
|
|
$allowed_children[] = $tracker; |
1614
|
|
|
} |
1615
|
|
|
} |
1616
|
|
|
return $allowed_children; |
1617
|
|
|
} |
1618
|
|
|
|
1619
|
|
|
/** |
1620
|
|
|
* Get artifacts linked to the current artifact if |
1621
|
|
|
* they are not in children. |
1622
|
|
|
* |
1623
|
|
|
* @param PFUser $user The user who should see the artifacts |
1624
|
|
|
* |
1625
|
|
|
* @return Array of Tracker_Artifact |
1626
|
|
|
*/ |
1627
|
|
|
public function getUniqueLinkedArtifacts(PFUser $user) { |
1628
|
|
|
$sub_artifacts = $this->getLinkedArtifacts($user); |
1629
|
|
|
$grandchild_artifacts = array(); |
1630
|
|
|
foreach ($sub_artifacts as $artifact) { |
1631
|
|
|
$grandchild_artifacts = array_merge($grandchild_artifacts, $artifact->getLinkedArtifactsOfHierarchy($user)); |
1632
|
|
|
} |
1633
|
|
|
array_filter($grandchild_artifacts); |
1634
|
|
|
return array_diff($sub_artifacts, $grandchild_artifacts); |
1635
|
|
|
} |
1636
|
|
|
|
1637
|
|
|
public function __toString() { |
1638
|
|
|
return __CLASS__." #$this->id"; |
1639
|
|
|
} |
1640
|
|
|
|
1641
|
|
|
/** |
1642
|
|
|
* Returns all ancestors of current artifact (from direct parent to oldest ancestor) |
1643
|
|
|
* |
1644
|
|
|
* @param PFUser $user |
1645
|
|
|
* |
1646
|
|
|
* @return Tracker_Artifact[] |
1647
|
|
|
*/ |
1648
|
|
|
public function getAllAncestors(PFUser $user) { |
1649
|
|
|
if (!isset($this->ancestors)) { |
1650
|
|
|
$this->ancestors = $this->getHierarchyFactory()->getAllAncestors($user, $this); |
1651
|
|
|
} |
1652
|
|
|
return $this->ancestors; |
1653
|
|
|
} |
1654
|
|
|
|
1655
|
|
|
public function setAllAncestors(array $ancestors) { |
1656
|
|
|
$this->ancestors = $ancestors; |
1657
|
|
|
} |
1658
|
|
|
|
1659
|
|
|
/** |
1660
|
|
|
* Return the parent artifact of current artifact if any |
1661
|
|
|
* |
1662
|
|
|
* @param PFUser $user |
1663
|
|
|
* |
1664
|
|
|
* @return Tracker_Artifact |
1665
|
|
|
*/ |
1666
|
|
|
public function getParent(PFUser $user) { |
1667
|
|
|
return $this->getHierarchyFactory()->getParentArtifact($user, $this); |
1668
|
|
|
} |
1669
|
|
|
|
1670
|
|
|
/** |
1671
|
|
|
* Get parent artifact regartheless if user can access it |
1672
|
|
|
* |
1673
|
|
|
* Note: even if there are several parents, only the first one is returned |
1674
|
|
|
* |
1675
|
|
|
* @return Tracker_Artifact|null |
1676
|
|
|
*/ |
1677
|
|
|
public function getParentWithoutPermissionChecking() { |
1678
|
|
|
if ($this->parent_without_permission_checking !== self::NO_PARENT && ! isset($this->parent_without_permission_checking)) { |
1679
|
|
|
$dar = $this->getDao()->getParents(array($this->getId())); |
1680
|
|
|
if ($dar && count($dar) == 1) { |
1681
|
|
|
$this->parent_without_permission_checking = $this->getArtifactFactory()->getInstanceFromRow($dar->current()); |
1682
|
|
|
} else { |
1683
|
|
|
$this->parent_without_permission_checking = self::NO_PARENT; |
1684
|
|
|
} |
1685
|
|
|
} |
1686
|
|
|
if ($this->parent_without_permission_checking === self::NO_PARENT) { |
1687
|
|
|
return null; |
1688
|
|
|
} |
1689
|
|
|
return $this->parent_without_permission_checking; |
1690
|
|
|
} |
1691
|
|
|
|
1692
|
|
|
public function setParentWithoutPermissionChecking($parent) { |
1693
|
|
|
$this->parent_without_permission_checking = $parent; |
1694
|
|
|
} |
1695
|
|
|
|
1696
|
|
|
/** |
1697
|
|
|
* Get artifacts that share same parent that mine (sista & bro) |
1698
|
|
|
* |
1699
|
|
|
* @param PFUser $user |
1700
|
|
|
* |
1701
|
|
|
* @return Tracker_Artifact[] |
1702
|
|
|
*/ |
1703
|
|
|
public function getSiblings(PFUser $user) { |
1704
|
|
|
if (! isset($this->siblings)) { |
1705
|
|
|
$this->siblings = array(); |
1706
|
|
|
foreach ($this->getSiblingsWithoutPermissionChecking() as $artifact) { |
1707
|
|
|
if ($artifact->userCanView($user)) { |
1708
|
|
|
$this->siblings[] = $artifact; |
1709
|
|
|
} |
1710
|
|
|
} |
1711
|
|
|
} |
1712
|
|
|
return $this->siblings; |
1713
|
|
|
} |
1714
|
|
|
|
1715
|
|
|
public function setSiblings(array $artifacts) { |
1716
|
|
|
$this->siblings = $artifacts; |
1717
|
|
|
} |
1718
|
|
|
|
1719
|
|
|
/** |
1720
|
|
|
* Get all sista & bro regartheless if user can access them |
1721
|
|
|
* |
1722
|
|
|
* @return Tracker_Artifact[] |
1723
|
|
|
*/ |
1724
|
|
|
public function getSiblingsWithoutPermissionChecking() { |
1725
|
|
|
if (! isset($this->siblings_without_permission_checking)) { |
1726
|
|
|
$this->siblings_without_permission_checking = $this->getDao()->getSiblings($this->getId())->instanciateWith(array($this->getArtifactFactory(), 'getInstanceFromRow')); |
1727
|
|
|
} |
1728
|
|
|
return $this->siblings_without_permission_checking; |
1729
|
|
|
} |
1730
|
|
|
|
1731
|
|
|
public function setSiblingsWithoutPermissionChecking($siblings) { |
1732
|
|
|
$this->siblings_without_permission_checking = $siblings; |
1733
|
|
|
} |
1734
|
|
|
|
1735
|
|
|
/** |
1736
|
|
|
* Returns the previously injected factory (e.g. in tests), or a new |
1737
|
|
|
* instance (e.g. in production). |
1738
|
|
|
* |
1739
|
|
|
* @return Tracker_HierarchyFactory |
1740
|
|
|
*/ |
1741
|
|
|
public function getHierarchyFactory() { |
1742
|
|
|
if ($this->hierarchy_factory == null) { |
1743
|
|
|
$this->hierarchy_factory = Tracker_HierarchyFactory::instance(); |
1744
|
|
|
} |
1745
|
|
|
return $this->hierarchy_factory; |
1746
|
|
|
} |
1747
|
|
|
|
1748
|
|
|
|
1749
|
|
|
public function setHierarchyFactory($hierarchy = null) { |
1750
|
|
|
$this->hierarchy_factory = $hierarchy; |
1751
|
|
|
} |
1752
|
|
|
|
1753
|
|
|
/** |
1754
|
|
|
* Returns the ids of the children of the tracker. |
1755
|
|
|
* |
1756
|
|
|
* @return array of int |
1757
|
|
|
*/ |
1758
|
|
|
protected function getChildTrackersIds() { |
1759
|
|
|
$children_trackers_ids = array(); |
1760
|
|
|
$children_hierarchy_tracker = $this->getHierarchyFactory()->getChildren($this->getTrackerId()); |
1761
|
|
|
foreach ($children_hierarchy_tracker as $tracker) { |
1762
|
|
|
$children_trackers_ids[] = $tracker->getId(); |
1763
|
|
|
} |
1764
|
|
|
return $children_trackers_ids; |
1765
|
|
|
} |
1766
|
|
|
|
1767
|
|
|
/** |
1768
|
|
|
* Return the first (and only one) ArtifactLink field (if any) |
1769
|
|
|
* |
1770
|
|
|
* @return Tracker_FormElement_Field_ArtifactLink |
1771
|
|
|
*/ |
1772
|
|
|
public function getAnArtifactLinkField(PFUser $user) { |
1773
|
|
|
return $this->getFormElementFactory()->getAnArtifactLinkField($user, $this->getTracker()); |
1774
|
|
|
} |
1775
|
|
|
|
1776
|
|
|
/** |
1777
|
|
|
* Return the first BurndownField (if any) |
1778
|
|
|
* |
1779
|
|
|
* @return Tracker_FormElement_Field_Burndown |
1780
|
|
|
*/ |
1781
|
|
|
public function getABurndownField(PFUser $user) { |
1782
|
|
|
return $this->getFormElementFactory()->getABurndownField($user, $this->getTracker()); |
1783
|
|
|
} |
1784
|
|
|
|
1785
|
|
|
/** |
1786
|
|
|
* Invoke those we don't speak of which may want to redirect to a |
1787
|
|
|
* specific page after an update/creation of this artifact. |
1788
|
|
|
* If the summoning is not strong enough (or there is no listener) then |
1789
|
|
|
* nothing is done. Else the client is redirected and |
1790
|
|
|
* the script will die in agony! |
1791
|
|
|
* |
1792
|
|
|
* @param Codendi_Request $request The request |
1793
|
|
|
*/ |
1794
|
|
|
public function summonArtifactRedirectors(Codendi_Request $request, Tracker_Artifact_Redirect $redirect) { |
1795
|
|
|
$this->getEventManager()->processEvent( |
1796
|
|
|
TRACKER_EVENT_REDIRECT_AFTER_ARTIFACT_CREATION_OR_UPDATE, |
1797
|
|
|
array( |
1798
|
|
|
'request' => $request, |
1799
|
|
|
'artifact' => $this, |
1800
|
|
|
'redirect' => $redirect |
1801
|
|
|
) |
1802
|
|
|
); |
1803
|
|
|
} |
1804
|
|
|
|
1805
|
|
|
private function summonArtifactAssociators(Codendi_Request $request, PFUser $current_user, $linked_artifact_id) { |
1806
|
|
|
$this->getEventManager()->processEvent( |
1807
|
|
|
TRACKER_EVENT_ARTIFACT_ASSOCIATION_EDITED, |
1808
|
|
|
array( |
1809
|
|
|
'artifact' => $this, |
1810
|
|
|
'linked-artifact-id' => $linked_artifact_id, |
1811
|
|
|
'request' => $request, |
1812
|
|
|
'user' => $current_user, |
1813
|
|
|
'form_element_factory' => $this->getFormElementFactory(), |
1814
|
|
|
) |
1815
|
|
|
); |
1816
|
|
|
} |
1817
|
|
|
|
1818
|
|
|
public function delete(PFUser $user) { |
1819
|
|
|
$this->getDao()->startTransaction(); |
1820
|
|
|
foreach($this->getChangesets() as $changeset) { |
1821
|
|
|
$changeset->delete($user); |
1822
|
|
|
} |
1823
|
|
|
$this->getPermissionsManager()->clearPermission(self::PERMISSION_ACCESS, $this->getId()); |
1824
|
|
|
$this->getCrossReferenceManager()->deleteEntity($this->getId(), self::REFERENCE_NATURE, $this->getTracker()->getGroupId()); |
1825
|
|
|
$this->getDao()->deleteArtifactLinkReference($this->getId()); |
1826
|
|
|
// We do not keep trace of the history change here because it doesn't have any sense |
1827
|
|
|
$this->getPriorityManager()->deletePriority($this); |
1828
|
|
|
$this->getDao()->delete($this->getId()); |
1829
|
|
|
$this->getDao()->commit(); |
1830
|
|
|
|
1831
|
|
|
EventManager::instance()->processEvent( |
1832
|
|
|
TRACKER_EVENT_ARTIFACT_DELETE, |
1833
|
|
|
array( |
1834
|
|
|
'artifact' => $this, |
1835
|
|
|
) |
1836
|
|
|
); |
1837
|
|
|
} |
1838
|
|
|
|
1839
|
|
|
/** |
1840
|
|
|
* Return the authorised ugroups to see the artifact |
1841
|
|
|
* |
1842
|
|
|
* @return Array |
1843
|
|
|
*/ |
1844
|
|
|
private function getAuthorisedUgroups () { |
1845
|
|
|
$ugroups = array(); |
1846
|
|
|
//Individual artifact permission |
1847
|
|
|
if ($this->useArtifactPermissions()) { |
1848
|
|
|
$rows = $this->permission_db_authorized_ugroups('PLUGIN_TRACKER_ARTIFACT_ACCESS'); |
1849
|
|
|
if ( $rows !== false ) { |
1850
|
|
|
foreach ($rows as $row) { |
1851
|
|
|
$ugroups[] = $row['ugroup_id']; |
1852
|
|
|
} |
1853
|
|
|
} |
1854
|
|
|
} else { |
1855
|
|
|
$permissions = $this->getTracker()->getAuthorizedUgroupsByPermissionType(); |
1856
|
|
|
foreach ($permissions as $permission => $ugroups) { |
1857
|
|
|
switch($permission) { |
1858
|
|
|
case Tracker::PERMISSION_FULL: |
1859
|
|
|
case Tracker::PERMISSION_SUBMITTER: |
1860
|
|
|
case Tracker::PERMISSION_ASSIGNEE: |
1861
|
|
|
case Tracker::PERMISSION_SUBMITTER_ONLY: |
1862
|
|
|
foreach ($ugroups as $ugroup) { |
1863
|
|
|
$ugroups[] = $ugroup['ugroup_id']; |
1864
|
|
|
} |
1865
|
|
|
break; |
1866
|
|
|
} |
1867
|
|
|
} |
1868
|
|
|
} |
1869
|
|
|
return $ugroups; |
1870
|
|
|
} |
1871
|
|
|
|
1872
|
|
|
/** |
1873
|
|
|
* Returns ugroups of an artifact in a human readable format |
1874
|
|
|
* |
1875
|
|
|
* @return array |
1876
|
|
|
*/ |
1877
|
|
|
public function exportPermissions() { |
1878
|
|
|
$project = ProjectManager::instance()->getProject($this->getTracker()->getGroupId()); |
1879
|
|
|
$literalizer = new UGroupLiteralizer(); |
1880
|
|
|
$ugroupsId = $this->getAuthorisedUgroups(); |
1881
|
|
|
return $literalizer->ugroupIdsToString($ugroupsId, $project); |
1882
|
|
|
} |
1883
|
|
|
|
1884
|
|
|
protected function getDao() { |
1885
|
|
|
return new Tracker_ArtifactDao(); |
1886
|
|
|
} |
1887
|
|
|
|
1888
|
|
|
protected function getPermissionsManager() { |
1889
|
|
|
return PermissionsManager::instance(); |
1890
|
|
|
} |
1891
|
|
|
|
1892
|
|
|
protected function getCrossReferenceManager() { |
1893
|
|
|
return new CrossReferenceManager(); |
1894
|
|
|
} |
1895
|
|
|
|
1896
|
|
|
protected function getCrossReferenceFactory() { |
1897
|
|
|
return new CrossReferenceFactory($this->getId(), self::REFERENCE_NATURE, $this->getTracker()->getGroupId()); |
1898
|
|
|
} |
1899
|
|
|
|
1900
|
|
|
/** |
1901
|
|
|
* Get the cross references from/to this artifact. |
1902
|
|
|
* |
1903
|
|
|
* Note: the direction of cross references is not returned |
1904
|
|
|
* |
1905
|
|
|
* @return array of references info to be sent in soap format: array('ref' => ..., 'url' => ...) |
1906
|
|
|
*/ |
1907
|
|
|
public function getCrossReferencesSOAPValues() { |
1908
|
|
|
$soap_value = array(); |
1909
|
|
|
$cross_reference_factory = $this->getCrossReferenceFactory(); |
1910
|
|
|
$cross_reference_factory->fetchDatas(); |
1911
|
|
|
|
1912
|
|
|
$cross_references = $cross_reference_factory->getFormattedCrossReferences(); |
1913
|
|
|
foreach ($cross_references as $array_of_references_by_direction) { |
1914
|
|
|
foreach ($array_of_references_by_direction as $reference) { |
1915
|
|
|
$soap_value[] = array( |
1916
|
|
|
'ref' => $reference['ref'], |
1917
|
|
|
'url' => $reference['url'], |
1918
|
|
|
); |
1919
|
|
|
} |
1920
|
|
|
} |
1921
|
|
|
return $soap_value; |
1922
|
|
|
} |
1923
|
|
|
|
1924
|
|
|
public function getSoapValue(PFUser $user) { |
1925
|
|
|
$soap_artifact = array(); |
1926
|
|
|
if ($this->userCanView($user)) { |
1927
|
|
|
$last_changeset = $this->getLastChangeset(); |
1928
|
|
|
|
1929
|
|
|
$soap_artifact['artifact_id'] = $this->getId(); |
1930
|
|
|
$soap_artifact['tracker_id'] = $this->getTrackerId(); |
1931
|
|
|
$soap_artifact['submitted_by'] = $this->getSubmittedBy(); |
1932
|
|
|
$soap_artifact['submitted_on'] = $this->getSubmittedOn(); |
1933
|
|
|
$soap_artifact['cross_references'] = $this->getCrossReferencesSOAPValues(); |
1934
|
|
|
$soap_artifact['last_update_date'] = $last_changeset->getSubmittedOn(); |
1935
|
|
|
|
1936
|
|
|
$soap_artifact['value'] = array(); |
1937
|
|
|
foreach ($this->getFormElementFactory()->getUsedFieldsForSoap($this->getTracker()) as $field) { |
1938
|
|
|
$value = $field->getSoapValue($user, $last_changeset); |
|
|
|
|
1939
|
|
|
if ($value !== null) { |
1940
|
|
|
$soap_artifact['value'][] = $value; |
1941
|
|
|
} |
1942
|
|
|
} |
1943
|
|
|
} |
1944
|
|
|
return $soap_artifact; |
1945
|
|
|
} |
1946
|
|
|
|
1947
|
|
|
/** |
1948
|
|
|
* Adds to $artifacts_node the xml export of the artifact. |
1949
|
|
|
*/ |
1950
|
|
|
public function exportToXML( |
1951
|
|
|
SimpleXMLElement $artifacts_node, |
1952
|
|
|
PFUser $user, |
1953
|
|
|
Tuleap\Project\XML\Export\ArchiveInterface $archive, |
1954
|
|
|
UserXMLExporter $user_xml_exporter |
1955
|
|
|
) { |
1956
|
|
|
$children_collector = new Tracker_XML_Exporter_NullChildrenCollector(); |
1957
|
|
|
$file_path_xml_exporter = new Tracker_XML_Exporter_InArchiveFilePathXMLExporter(); |
1958
|
|
|
|
1959
|
|
|
$artifact_xml_exporter = $this->getArtifactXMLExporter( |
1960
|
|
|
$children_collector, |
1961
|
|
|
$file_path_xml_exporter, |
1962
|
|
|
$user, |
1963
|
|
|
$user_xml_exporter |
1964
|
|
|
); |
1965
|
|
|
|
1966
|
|
|
$artifact_xml_exporter->exportFullHistory($artifacts_node, $this); |
1967
|
|
|
|
1968
|
|
|
$attachment_exporter = $this->getArtifactAttachmentExporter(); |
1969
|
|
|
$attachment_exporter->exportAttachmentsInArchive($this, $archive); |
1970
|
|
|
} |
1971
|
|
|
|
1972
|
|
|
/** |
1973
|
|
|
* @return Tracker_XML_Exporter_ArtifactAttachmentExporter |
1974
|
|
|
*/ |
1975
|
|
|
private function getArtifactAttachmentExporter() { |
1976
|
|
|
return new Tracker_XML_Exporter_ArtifactAttachmentExporter($this->getFormElementFactory()); |
1977
|
|
|
} |
1978
|
|
|
|
1979
|
|
|
private function getArtifactXMLExporter( |
1980
|
|
|
Tracker_XML_ChildrenCollector $children_collector, |
1981
|
|
|
Tracker_XML_Exporter_FilePathXMLExporter $file_path_xml_exporter, |
1982
|
|
|
PFUser $current_user, |
1983
|
|
|
UserXMLExporter $user_xml_exporter |
1984
|
|
|
) { |
1985
|
|
|
$builder = new Tracker_XML_Exporter_ArtifactXMLExporterBuilder(); |
1986
|
|
|
|
1987
|
|
|
return $builder->build($children_collector, $file_path_xml_exporter, $current_user, $user_xml_exporter); |
1988
|
|
|
} |
1989
|
|
|
|
1990
|
|
|
/** @return string */ |
1991
|
|
|
public function getTokenBasedEmailAddress() { |
1992
|
|
|
return trackerPlugin::EMAILGATEWAY_TOKEN_ARTIFACT_UPDATE .'@' . $this->getEmailDomain(); |
1993
|
|
|
} |
1994
|
|
|
|
1995
|
|
|
/** @return string */ |
1996
|
|
|
public function getInsecureEmailAddress() { |
1997
|
|
|
return trackerPlugin::EMAILGATEWAY_INSECURE_ARTIFACT_UPDATE .'+'. $this->getId() .'@' . $this->getEmailDomain(); |
1998
|
|
|
} |
1999
|
|
|
|
2000
|
|
|
private function getEmailDomain() { |
2001
|
|
|
$email_domain = ForgeConfig::get('sys_default_mail_domain'); |
2002
|
|
|
|
2003
|
|
|
if (! $email_domain) { |
2004
|
|
|
$email_domain = ForgeConfig::get('sys_default_domain'); |
2005
|
|
|
} |
2006
|
|
|
|
2007
|
|
|
return $email_domain; |
2008
|
|
|
} |
2009
|
|
|
} |
2010
|
|
|
|
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.
Consider the following example. The parameter
$ireland
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was changed, but the annotation was not.