|
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
$irelandis not defined by the methodfinale(...).The most likely cause is that the parameter was changed, but the annotation was not.