GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Tracker::displayAReport()   F
last analyzed

Complexity

Conditions 22
Paths 3960

Size

Total Lines 147
Code Lines 94

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 147
rs 2
cc 22
eloc 94
nc 3960
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
4
 * Copyright (c) Enalean, 2011, 2012, 2013, 2014, 2015. All Rights Reserved.
5
 *
6
 * This file is a part of Codendi.
7
 *
8
 * Codendi is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * Codendi is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
require_once('common/date/DateHelper.class.php');
23
require_once('common/widget/Widget_Static.class.php');
24
require_once 'common/include/CSRFSynchronizerToken.class.php';
25
26
require_once('json.php');
27
28
class Tracker implements Tracker_Dispatchable_Interface {
29
    const PERMISSION_ADMIN            = 'PLUGIN_TRACKER_ADMIN';
30
    const PERMISSION_FULL             = 'PLUGIN_TRACKER_ACCESS_FULL';
31
    const PERMISSION_ASSIGNEE         = 'PLUGIN_TRACKER_ACCESS_ASSIGNEE';
32
    const PERMISSION_SUBMITTER        = 'PLUGIN_TRACKER_ACCESS_SUBMITTER';
33
    const PERMISSION_NONE             = 'PLUGIN_TRACKER_NONE';
34
    const PERMISSION_SUBMITTER_ONLY   = 'PLUGIN_TRACKER_ACCESS_SUBMITTER_ONLY';
35
36
    const REMAINING_EFFORT_FIELD_NAME = "remaining_effort";
37
    const ASSIGNED_TO_FIELD_NAME      = "assigned_to";
38
    const IMPEDIMENT_FIELD_NAME       = "impediment";
39
    const TYPE_FIELD_NAME             = "type";
40
    const NO_PARENT                   = -1;
41
    const DEFAULT_COLOR               = 'inca_silver';
42
43
    public $id;
44
    public $group_id;
45
    public $name;
46
    public $description;
47
    public $color;
48
    public $item_name;
49
    public $allow_copy;
50
    public $submit_instructions;
51
    public $browse_instructions;
52
    public $status;
53
    public $deletion_date;
54
    public $instantiate_for_new_projects;
55
    public $log_priority_changes;
56
    public $stop_notification;
57
    private $formElementFactory;
58
    private $sharedFormElementFactory;
59
    private $project;
60
    private $children;
61
    private $parent = false;
62
    private $enable_emailgateway;
63
64
    // attributes necessary to to create an intermediate Tracker Object
65
    // (before Database import) during XML import
66
    // they are not used after the import
67
    public $tooltip;
68
    public $cannedResponses = array();
69
    public $formElements = array();
70
    public $reports = array();
71
    public $workflow;
72
73
    public function __construct($id,
74
            $group_id,
75
            $name,
76
            $description,
77
            $item_name,
78
            $allow_copy,
79
            $submit_instructions,
80
            $browse_instructions,
81
            $status,
82
            $deletion_date,
83
            $instantiate_for_new_projects,
84
            $log_priority_changes,
85
            $stop_notification,
86
            $color,
87
            $enable_emailgateway) {
88
        $this->id                           = $id;
89
        $this->group_id                     = $group_id;
90
        $this->name                         = $name;
91
        $this->description                  = $description;
92
        $this->item_name                    = $item_name;
93
        $this->allow_copy                   = $allow_copy;
94
        $this->submit_instructions          = $submit_instructions;
95
        $this->browse_instructions          = $browse_instructions;
96
        $this->status                       = $status;
97
        $this->deletion_date                = $deletion_date;
98
        $this->instantiate_for_new_projects = $instantiate_for_new_projects;
99
        $this->log_priority_changes         = $log_priority_changes;
100
        $this->stop_notification            = $stop_notification;
101
        $this->enable_emailgateway          = $enable_emailgateway;
102
        $this->formElementFactory           = Tracker_FormElementFactory::instance();
103
        $this->sharedFormElementFactory     = new Tracker_SharedFormElementFactory($this->formElementFactory, new Tracker_FormElement_Field_List_BindFactory());
104
        $this->renderer                     = TemplateRendererFactory::build()->getRenderer(TRACKER_TEMPLATE_DIR);
105
106
        $this->setColor($color);
107
    }
108
109
    private function setColor($color) {
110
        if (! $color) {
111
            $color = self::DEFAULT_COLOR;
112
        }
113
114
        $this->color = $color;
115
    }
116
117
    public function __toString() {
118
        return "Tracker #".$this->id;
119
    }
120
121
    /**
122
     * @return string the url of the form to submit a new artifact
123
     */
124
    public function getSubmitUrl() {
125
        return TRACKER_BASE_URL .'/?tracker='. $this->getId() .'&func=new-artifact';
126
    }
127
128
    /**
129
     * @return string ~ 'Add new bug'
130
     */
131
    public function getAddNewLabel() {
132
        return $GLOBALS['Language']->getText('plugin_tracker', 'add_a', $this->getItemName());
133
    }
134
135
    /**
136
     * getGroupId - get this Tracker Group ID.
137
     *
138
     * @return int The group_id
139
     */
140
    function getGroupId() {
141
        return $this->group_id;
142
    }
143
144
    /**
145
     * Get the project of this tracker.
146
     *
147
     * @return Project
148
     */
149
    function getProject() {
150
        if (!$this->project) {
151
            $this->project = ProjectManager::instance()->getProject($this->group_id);
152
        }
153
        return $this->project;
154
    }
155
156
    function setProject(Project $project) {
157
        $this->project  = $project;
158
        $this->group_id = $project->getID();
159
    }
160
161
    /**
162
     * getId - get this Tracker Id.
163
     *
164
     * @return int The id
165
     */
166
    function getId() {
167
        return $this->id;
168
    }
169
170
    /**
171
     * set this Tracker Id.
172
     *
173
     * @param int $id the id of the tracker
174
     *
175
     * @return int The id
176
     */
177
    function setId($id) {
178
        $this->id = $id;
179
    }
180
181
    /**
182
     * getName - get this Tracker name.
183
     *
184
     * @return string the tracker name
185
     */
186
    function getName() {
187
        return $this->name;
188
    }
189
190
    /**
191
     * getDescription - get this Tracker description.
192
     *
193
     * @return string the tracker description
194
     */
195
    function getDescription() {
196
        return $this->description;
197
    }
198
199
    /**
200
     * getItemName - get this Tracker item name (short name).
201
     *
202
     * @return string the tracker item name (shortname)
203
     */
204
    function getItemName() {
205
        return $this->item_name;
206
    }
207
208
    /**
209
     * Returns the brwose instructions
210
     *
211
     * @return string the browse instructions of the tracker
212
     */
213
    function getBrowseInstructions() {
214
        return $this->browse_instructions;
215
    }
216
217
    /**
218
     * Returns true is this tracker must be instantiated for new project
219
     *
220
     * @return boolean true is this tracker must be instantiated for new project
221
     */
222
    function mustBeInstantiatedForNewProjects() {
223
        return $this->instantiate_for_new_projects == 1;
224
    }
225
226
    public function arePriorityChangesShown() {
227
        return $this->log_priority_changes == 1;
228
    }
229
230
    /**
231
     * Returns true is notifications are stopped for this tracker
232
     *
233
     * @return boolean true is notifications are stopped for this tracker, false otherwise
234
     */
235
    function isNotificationStopped() {
236
        return $this->stop_notification == 1;
237
    }
238
239
    /**
240
     * @return array of formElements used by this tracker
241
     */
242
    public function getFormElements() {
243
        return Tracker_FormElementFactory::instance()->getUsedFormElementForTracker($this);
244
    }
245
246
    /**
247
     * @return Tracker_FormElement_Field[]
248
     */
249
    public function getFormElementFields() {
250
        return Tracker_FormElementFactory::instance()->getUsedFields($this);
251
    }
252
253
    /**
254
     * @param string $name
255
     * @param mixed  $type A field type name, or an array of field type names, e.g. 'float', or array('float', 'int').
256
     *
257
     * @return bool true if the tracker contains an element of the given name and type
258
     */
259
    public function hasFormElementWithNameAndType($name, $type) {
260
        $form_element_factory = Tracker_FormElementFactory::instance();
261
        $element              = $form_element_factory->getUsedFieldByName($this->getId(), $name);
262
263
        return $element !== null && in_array($form_element_factory->getType($element), (array)$type);
264
    }
265
266
    /**
267
     * Should probably be mobified for better efficiency
268
     *
269
     * @return array of all the formElements
270
     */
271
    public function getAllFormElements() {
272
        return array_merge(Tracker_FormElementFactory::instance()->getUsedFormElementForTracker($this),
273
                Tracker_FormElementFactory::instance()->getUnusedFormElementForTracker($this));
274
    }
275
276
    /**
277
     * fetch FormElements
278
     * @param Tracker_Artifact $artifact
279
     * @param array $submitted_values the values already submitted
280
     *
281
     * @return string
282
     */
283
    public function fetchFormElements($artifact, $submitted_values = array()) {
284
        $html = '';
285
        foreach($this->getFormElements() as $formElement) {
0 ignored issues
show
Bug introduced by
The expression $this->getFormElements() of type object<Tracker_FormElement> is not traversable.
Loading history...
286
            $html .= $formElement->fetchArtifact($artifact, $submitted_values);
287
        }
288
        return $html;
289
    }
290
291
    /**
292
     * fetch FormElements
293
     * @param Tracker_Artifact $artifact
294
     * @param array $submitted_values the values already submitted
295
     *
296
     * @return string
297
     */
298
    public function fetchFormElementsForCopy($artifact, $submitted_values = array()) {
299
        $html = '';
300
        foreach($this->getFormElements() as $formElement) {
0 ignored issues
show
Bug introduced by
The expression $this->getFormElements() of type object<Tracker_FormElement> is not traversable.
Loading history...
301
            $html .= $formElement->fetchArtifactCopyMode($artifact, $submitted_values);
302
        }
303
        return $html;
304
    }
305
306
    /**
307
     * Fetch FormElements in HTML without the container and column rendering
308
     *
309
     * @param Tracker_Artifact $artifact
310
     * @param array $submitted_values the values already submitted
311
     *
312
     * @return string
313
     */
314
    public function fetchFormElementsNoColumns($artifact, $submitted_values = array()) {
315
        $html = '';
316
        foreach($this->getFormElements() as $formElement) {
0 ignored issues
show
Bug introduced by
The expression $this->getFormElements() of type object<Tracker_FormElement> is not traversable.
Loading history...
317
            $html .= $formElement->fetchArtifactForOverlay($artifact, $submitted_values);
318
        }
319
        return $html;
320
    }
321
322
    /**
323
     * Fetch Tracker submit form in HTML without the container and column rendering
324
     *
325
     * @param Tracker_Artifact | null  $artifact_to_link  The artifact wich will be linked to the new artifact
326
     *
327
     * @return String
328
     */
329
    public function fetchSubmitNoColumns($artifact_to_link, $submitted_values) {
330
        $html='';
331
332
        if ($artifact_to_link) {
333
            $html .= '<input type="hidden" name="link-artifact-id" value="'. $artifact_to_link->getId() .'" />';
334
        }
335
336
        foreach($this->getFormElements() as $form_element) {
0 ignored issues
show
Bug introduced by
The expression $this->getFormElements() of type object<Tracker_FormElement> is not traversable.
Loading history...
337
            $html .= $form_element->fetchSubmitForOverlay($submitted_values);
338
        }
339
340
        return $html;
341
    }
342
343
    /**
344
     * fetch FormElements in read only mode
345
     *
346
     * @param Tracker_Artifact $artifact
347
     *
348
     * @return string
349
     */
350
    public function fetchFormElementsReadOnly($artifact, $submitted_values = array()) {
351
        $html = '';
352
        foreach($this->getFormElements() as $formElement) {
0 ignored issues
show
Bug introduced by
The expression $this->getFormElements() of type object<Tracker_FormElement> is not traversable.
Loading history...
353
            $html .= $formElement->fetchArtifactReadOnly($artifact, $submitted_values);
354
        }
355
        return $html;
356
    }
357
358
    /**
359
     * fetch FormElements
360
     * @return string
361
     */
362
    public function fetchAdminFormElements() {
363
        $html = '';
364
        $html .= '<div id="tracker-admin-fields" class="tracker-admin-group">';
365
        foreach($this->getFormElements() as $formElement) {
0 ignored issues
show
Bug introduced by
The expression $this->getFormElements() of type object<Tracker_FormElement> is not traversable.
Loading history...
366
            $html .= $formElement->fetchAdmin($this);
367
        }
368
        $html .= '</div>';
369
        return $html;
370
    }
371
372
    public function fetchFormElementsMasschange() {
373
        $html = '';
374
        $html .= '<table width="20%"><tr><td>';
375
        foreach($this->getFormElements() as $formElement) {
0 ignored issues
show
Bug introduced by
The expression $this->getFormElements() of type object<Tracker_FormElement> is not traversable.
Loading history...
376
            $html .= $formElement->fetchSubmitMasschange();
377
        }
378
        $html .= '</td></tr></table>';
379
        return $html;
380
    }
381
382
    /**
383
     * Return an instance of TrackerFactory
384
     *
385
     * @return TrackerFactory an instance of tracker factory
386
     */
387
    public function getTrackerFactory() {
388
        return TrackerFactory::instance();
389
    }
390
391
    /**
392
     * Return self
393
     *
394
     * @see plugins/tracker/include/Tracker/Tracker_Dispatchable_Interface::getTracker()
395
     *
396
     * @return Tracker
397
     */
398
    public function getTracker() {
399
        return $this;
400
    }
401
402
    public function process(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
403
        //TODO: log the admin actions (add a formElement, ...) ?
404
        $hp = Codendi_HTMLPurifier::instance();
405
        $func = (string)$request->get('func');
406
        switch ($func) {
407
            case 'new-artifact':
408
                if ($this->userCanSubmitArtifact($current_user)) {
409
                    $this->displaySubmit($layout, $request, $current_user);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<Tracker_IFetchTrackerSwitcher>. It seems like you assume a child interface of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
410
                } else {
411
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
412
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
413
                }
414
                break;
415
416
            case 'get-create-in-place':
417
                if ($this->userCanSubmitArtifact($current_user)) {
418
                    $artifact_link_id       = $request->get('artifact-link-id');
419
                    $render_with_javascript = ($request->get('fetch-js') == 'false') ? false : true;
420
421
                    $renderer = new Tracker_Artifact_Renderer_CreateInPlaceRenderer(
422
                        $this,
423
                        TemplateRendererFactory::build()->getRenderer(dirname(TRACKER_BASE_DIR).'/templates')
0 ignored issues
show
Compatibility introduced by
\TemplateRendererFactory...SE_DIR) . '/templates') of type object<TemplateRenderer> is not a sub-type of object<MustacheRenderer>. It seems like you assume a child class of the class TemplateRenderer to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
424
                    );
425
426
                    $renderer->display($artifact_link_id, $render_with_javascript);
427
                } else {
428
                    $GLOBALS['Response']->send400JSONErrors();
429
                }
430
            break;
431
432
            case 'new-artifact-link':
433
                $link = $request->get('id');
434
                if ($this->userCanSubmitArtifact($current_user)) {
435
                    $this->displaySubmit($layout, $request, $current_user, $link);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<Tracker_IFetchTrackerSwitcher>. It seems like you assume a child interface of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
436
                } else {
437
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
438
                }
439
                break;
440
            case 'delete':
441
                if ($this->userCanDeleteTracker($current_user)) {
442
                    if ($this->getTrackerFactory()->markAsDeleted($this->id)) {
443
                        $GLOBALS['Response']->addFeedback(
444
                                'info',
445
                                $GLOBALS['Language']->getText(
446
                                'plugin_tracker_admin_index',
447
                                'delete_success',
448
                                $hp->purify($this->name, CODENDI_PURIFIER_CONVERT_HTML)));
449
                        $GLOBALS['Response']->addFeedback(
450
                                'info',
451
                                $GLOBALS['Language']->getText(
452
                                'plugin_tracker_admin_index',
453
                                'tracker_deleted',
454
                                $GLOBALS['sys_email_admin']),
455
                                CODENDI_PURIFIER_FULL
456
                        );
457
                        $reference_manager =  ReferenceManager::instance();
458
                        $ref =  $reference_manager->loadReferenceFromKeywordAndNumArgs(strtolower($this->getItemName()), $this->getGroupId(), 1);
459
                        if ($ref) {
460
                            if ($reference_manager->deleteReference($ref)) {
461
                                $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('project_reference', 't_r_deleted'));
462
                            }
463
                        }
464
465
                        EventManager::instance()->processEvent(
466
                            TRACKER_EVENT_TRACKER_DELETE,
467
                            array(
468
                                'tracker' => $this,
469
                            )
470
                        );
471
                    } else {
472
                        $GLOBALS['Response']->addFeedback(
473
                                'error',
474
                                $GLOBALS['Language']->getText(
475
                                'plugin_tracker_admin_index',
476
                                'deletion_failed',
477
                                $hp->purify($this->name, CODENDI_PURIFIER_CONVERT_HTML)));
478
                    }
479
                } else {
480
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
481
                }
482
                $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?group_id='. $this->group_id);
483
                break;
484
            case 'admin':
485
                if ($this->userIsAdmin($current_user)) {
486
                    $this->displayAdmin($layout, $request, $current_user);
487
                } else {
488
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
489
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
490
                }
491
                break;
492
            case 'admin-editoptions':
493
                if ($this->userIsAdmin($current_user)) {
494
                    if ($request->get('update')) {
495
                        $this->editOptions($request);
496
                    }
497
                    $this->displayAdminOptions($layout, $request, $current_user);
498
                } else {
499
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
500
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
501
                }
502
                break;
503
            case 'admin-perms':
504
                if ($this->userIsAdmin($current_user)) {
505
                    $this->displayAdminPerms($layout, $request, $current_user);
506
                } else {
507
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
508
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
509
                }
510
                break;
511
            case 'admin-perms-tracker':
512
                if ($this->userIsAdmin($current_user)) {
513
                    $this->getPermissionController()->process($layout, $request, $current_user);
514
                } else {
515
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
516
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
517
                }
518
                break;
519
            case 'admin-perms-fields':
520
                if ($this->userIsAdmin($current_user)) {
521
                    if ($request->exist('update')) {
522
                        if ($request->exist('permissions') && is_array($request->get('permissions'))) {
523
                            plugin_tracker_permission_process_update_fields_permissions(
524
                                    $this->getGroupId(),
525
                                    $this->getId(),
526
                                    Tracker_FormElementFactory::instance()->getUsedFields($this),
527
                                    $request->get('permissions')
528
                            );
529
                            $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('project_admin_userperms', 'perm_upd'));
530
                        }
531
                    }
532
                    $this->displayAdminPermsFields($layout, $request, $current_user);
533
                } else {
534
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
535
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
536
                }
537
                break;
538
            case 'admin-formElements':
539
                if ($this->userIsAdmin($current_user)) {
540
                    if (is_array($request->get('add-formElement'))) {
541
                        list($formElement_id,) = each($request->get('add-formElement'));
0 ignored issues
show
Bug introduced by
$request->get('add-formElement') cannot be passed to each() as the parameter $array expects a reference.
Loading history...
542
                        if (Tracker_FormElementFactory::instance()->addFormElement($formElement_id)) {
543
                            $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_admin_index', 'field_added'));
544
                            $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. (int)$this->getId() .'&func=admin-formElements');
545
                        }
546
                    } else if (is_array($request->get('create-formElement'))) {
547
                        list($type,) = each($request->get('create-formElement'));
0 ignored issues
show
Bug introduced by
$request->get('create-formElement') cannot be passed to each() as the parameter $array expects a reference.
Loading history...
548
                        if ($request->get('docreate-formElement') && is_array($request->get('formElement_data'))) {
549
                            try {
550
                                $this->createFormElement($type, $request->get('formElement_data'), $current_user);
551
                            } catch (Exception $e) {
552
                                $GLOBALS['Response']->addFeedback('error', $e->getMessage());
553
                            }
554
                            $GLOBALS['Response']->redirect(
555
                                    TRACKER_BASE_URL.'/?'. http_build_query(
556
                                    array(
557
                                    'tracker' => $this->getId(),
558
                                    'func'    => $func,
559
                                    )
560
                                    )
561
                            );
562
                        } else {
563
                            Tracker_FormElementFactory::instance()->displayAdminCreateFormElement($layout, $request, $current_user, $type, $this);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<TrackerManager>. It seems like you assume a concrete implementation of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
564
                            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method process() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
565
                        }
566
                    }
567
                    $this->displayAdminFormElements($layout, $request, $current_user);
568
                } else {
569
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
570
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
571
                }
572
                break;
573
            case 'admin-formElement-update':
574
            case 'admin-formElement-remove':
575
            case 'admin-formElement-delete':
576
                if ($this->userIsAdmin($current_user)) {
577
                    if ($formElement = Tracker_FormElementFactory::instance()->getFormElementById((int)$request->get('formElement'))) {
578
                        $formElement->process($layout, $request, $current_user);
579
                    } else {
580
                        $this->displayAdminFormElements($layout, $request, $current_user);
581
                    }
582
                } else {
583
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
584
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
585
                }
586
                break;
587
            case 'admin-semantic':
588
                if ($this->userIsAdmin($current_user)) {
589
                    $this->getTrackerSemanticManager()->process($layout, $request, $current_user);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<TrackerManager>. It seems like you assume a concrete implementation of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
590
                } else {
591
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
592
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
593
                }
594
                break;
595
            case 'admin-notifications':
596
                if ($this->userIsAdmin($current_user)) {
597
                    $this->getDateReminderManager()->processReminder($layout, $request, $current_user);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<TrackerManager>. It seems like you assume a concrete implementation of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Compatibility introduced by
$request of type object<Codendi_Request> is not a sub-type of object<HTTPRequest>. It seems like you assume a child class of the class Codendi_Request to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
598
                    $this->getNotificationsManager()->process($layout, $request, $current_user);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<TrackerManager>. It seems like you assume a concrete implementation of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
599
                } else {
600
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
601
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
602
                }
603
                break;
604
            case 'notifications':
605
            // you just need to be registered to have access to this part
606
                if ($current_user->isLoggedIn()) {
607
                    $this->getDateReminderManager()->processReminder($layout, $request, $current_user);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<TrackerManager>. It seems like you assume a concrete implementation of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Compatibility introduced by
$request of type object<Codendi_Request> is not a sub-type of object<HTTPRequest>. It seems like you assume a child class of the class Codendi_Request to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
608
                    $this->getNotificationsManager()->process($layout, $request, $current_user);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<TrackerManager>. It seems like you assume a concrete implementation of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
609
                } else {
610
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
611
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
612
                }
613
                break;
614
            case 'display_reminder_form':
615
                print $this->getDateReminderManager()->getDateReminderRenderer()->getNewDateReminderForm();
616
            break;
617
            case 'admin-canned':
618
            // TODO : project members can access this part ?
619
                if ($this->userIsAdmin($current_user)) {
620
                    $this->getCannedResponseManager()->process($layout, $request, $current_user);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<TrackerManager>. It seems like you assume a concrete implementation of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
621
                } else {
622
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
623
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
624
                }
625
                break;
626
            case Workflow::FUNC_ADMIN_RULES:
627
            case Workflow::FUNC_ADMIN_CROSS_TRACKER_TRIGGERS:
628
            case Workflow::FUNC_ADMIN_TRANSITIONS:
629
            case Workflow::FUNC_ADMIN_GET_TRIGGERS_RULES_BUILDER_DATA:
630
            case Workflow::FUNC_ADMIN_ADD_TRIGGER:
631
            case Workflow::FUNC_ADMIN_DELETE_TRIGGER:
632
                if ($this->userIsAdmin($current_user)) {
633
                    $this->getWorkflowManager()->process($layout, $request, $current_user);
0 ignored issues
show
Compatibility introduced by
$layout of type object<Tracker_IDisplayTrackerLayout> is not a sub-type of object<TrackerManager>. It seems like you assume a concrete implementation of the interface Tracker_IDisplayTrackerLayout to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
634
                } else {
635
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
636
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
637
                }
638
                break;
639
            case 'admin-csvimport':
640
                $session = new Codendi_Session();
641
                if ($this->userIsAdmin($current_user)) {
642
                    if ($request->exist('action') && $request->get('action') == 'import_preview' && array_key_exists('csv_filename', $_FILES)) {
643
                        // display preview before importing artifacts
644
                        $this->displayImportPreview($layout, $request, $current_user, $session);
645
                    } elseif ($request->exist('action') && $request->get('action') == 'import') {
646
                        $csv_header = $session->get('csv_header');
647
                        $csv_body   = $session->get('csv_body');
648
649
                        if ($this->importFromCSV($layout, $request, $current_user, $csv_header, $csv_body)) {
650
                            $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'import_succeed'));
651
                            $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
652
                        } else {
653
                            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'import_failed'));
654
                            $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
655
                        }
656
                    }
657
                    $this->displayAdminCSVImport($layout, $request, $current_user);
658
                } else {
659
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
660
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
661
                }
662
                break;
663
            case 'admin-export':
664
                if ($this->userIsAdmin($current_user)) {
665
                    // TODO: change directory
666
                    $xml_element = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><tracker />');
667
                    $this->sendXML($this->exportToXML($xml_element));
668
                } else {
669
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
670
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
671
                }
672
                break;
673
            case 'admin-dependencies':
674
                if ($this->userIsAdmin($current_user)) {
675
                    $this->getGlobalRulesManager()->process($layout, $request, $current_user);
676
                } else {
677
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
678
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
679
                }
680
                break;
681
            case 'submit-artifact':
682
                $action = new Tracker_Action_CreateArtifact(
683
                    $this,
684
                    $this->getTrackerArtifactFactory(),
685
                    $this->getTrackerFactory(),
686
                    $this->getFormElementFactory()
687
                );
688
                $action->process($layout, $request, $current_user);
689
                break;
690
            case 'submit-copy-artifact':
691
                $logger                    = new Tracker_XML_Importer_CopyArtifactInformationsAggregator(new BackendLogger());
692
                $xml_importer              = $this->getArtifactXMLImporterForArtifactCopy($logger);
693
                $artifact_factory          = $this->getTrackerArtifactFactory();
694
                $file_xml_updater          = $this->getFileXMLUpdater();
695
                $export_children_collector = $this->getChildrenCollector($request);
696
                $file_path_xml_exporter    = new Tracker_XML_Exporter_LocalAbsoluteFilePathXMLExporter();
697
                $artifact_xml_exporter     = $this->getArtifactXMLExporter(
698
                    $export_children_collector,
699
                    $file_path_xml_exporter,
700
                    $current_user
701
                );
702
703
                $action = new Tracker_Action_CopyArtifact(
704
                    $this,
705
                    $artifact_factory,
706
                    $artifact_xml_exporter,
707
                    $xml_importer,
708
                    $this->getChangesetXMLUpdater(),
709
                    $file_xml_updater,
710
                    new Tracker_XML_Exporter_ChildrenXMLExporter(
711
                        $artifact_xml_exporter,
712
                        $file_xml_updater,
713
                        $artifact_factory,
714
                        $export_children_collector
715
                    ),
716
                    new Tracker_XML_Importer_ChildrenXMLImporter(
717
                        $xml_importer,
718
                        $this->getTrackerFactory(),
719
                        $this->getTrackerArtifactFactory(),
720
                        new Tracker_XML_ChildrenCollector()
721
                    ),
722
                    new Tracker_XML_Importer_ArtifactImportedMapping(),
723
                    $logger
724
                );
725
                $action->process($layout, $request, $current_user);
726
                break;
727
            case 'submit-artifact-in-place':
728
                $action = new Tracker_Action_CreateArtifactFromModal($request, $this, $this->getTrackerArtifactFactory());
729
                $action->process($current_user);
730
                break;
731
            case 'admin-hierarchy':
732
                if ($this->userIsAdmin($current_user)) {
733
734
                    $this->displayAdminItemHeader($layout, 'hierarchy');
735
                    $this->getHierarchyController($request)->edit();
736
                    $this->displayFooter($layout);
737
                } else {
738
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
739
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
740
                }
741
                break;
742
            case 'admin-hierarchy-update':
743
                if ($this->userIsAdmin($current_user)) {
744
745
                    $this->getHierarchyController($request)->update();
746
                } else {
747
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
748
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
749
                }
750
                break;
751
752
           case 'admin-clean':
753
                if ($this->userIsAdmin($current_user)) {
754
                    $this->displayAdminClean($layout);
755
                } else {
756
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
757
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
758
                }
759
                break;
760
761
           case 'admin-delete-artifact-confirm':
762
                if ($this->userIsAdmin($current_user)) {
763
                    $token = new CSRFSynchronizerToken(TRACKER_BASE_URL.'/?tracker='. (int)$this->id.'&amp;func=admin-delete-artifact-confirm');
764
                    $token->check();
765
                    $artifact_id = $request->getValidated('id', 'uint', 0);
766
                    $artifact    = $this->getTrackerArtifactFactory()->getArtifactById($artifact_id);
767
                    if ($artifact) {
768
                        $this->displayAdminConfirmDelete($layout, $artifact);
769
                    } else {
770
                        $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_error_noart', array($request->get('id'))));
771
                        $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId().'&func=admin-clean');
772
                    }
773
                } else {
774
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
775
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
776
                }
777
                break;
778
779
            case 'admin-delete-artifact':
780
                if ($this->userIsAdmin($current_user)) {
781
                    $token = new CSRFSynchronizerToken(TRACKER_BASE_URL.'/?tracker='. (int)$this->id.'&amp;func=admin-delete-artifact');
782
                    $token->check();
783
                    if ($request->exist('confirm')) {
784
                        $artifact = $this->getTrackerArtifactFactory()->getArtifactById($request->get('id'));
785
                        if ($artifact && $artifact->getTrackerId() == $this->getId()) {
786
                            $artifact->delete($current_user);
787
                            $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_info_deleted', array($request->get('id'))));
788
                        } else {
789
                            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_error_noart', array($request->get('id'))));
790
                        }
791
                    } else {
792
                        $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_cancel_deleted'));
793
                    }
794
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId().'&func=admin');
795
                } else {
796
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
797
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
798
                }
799
                break;
800
            case 'create_new_public_report':
801
                if (! $this->userIsAdmin($current_user)) {
802
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied'));
803
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
804
                }
805
806
                $name      = $request->get('new_report_name');
807
                $validator = new Valid_String('new_report_name');
808
                $validator->required();
809
810
                if (! $request->valid($validator)) {
811
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker', 'create_new_report_invalid'));
812
                    $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
813
                }
814
815
                $hp = Codendi_HTMLPurifier::instance();
816
                $hp->purify($name);
817
818
                $report = new Tracker_Report(0, $name, 'Public rapport', 0, 0, null, 0, $this->getId(), 1, null, 0);
819
                $report->criterias = array();
820
821
                $this->getReportFactory()->saveObject($this->id, $report);
822
                $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $this->getId());
823
                break;
824
825
            default:
826
                if ($this->userCanView($current_user)) {
827
                    $this->displayAReport($layout, $request, $current_user);
828
                }
829
                break;
830
        }
831
        return false;
832
    }
833
834
    private function getHierarchyController($request) {
835
        $dao                  = new Tracker_Hierarchy_Dao();
836
        $tracker_factory      = $this->getTrackerFactory();
837
        $factory              = new Tracker_Hierarchy_HierarchicalTrackerFactory($tracker_factory, $dao);
838
        $hierarchical_tracker = $factory->getWithChildren($this);
839
        $controller           = new Tracker_Hierarchy_Controller($request, $hierarchical_tracker, $factory, $dao);
840
        return $controller;
841
    }
842
843
844
    public function createFormElement($type, $formElement_data, $user) {
845
        if ($type == 'shared') {
846
            $this->sharedFormElementFactory->createFormElement($this, $formElement_data, $user);
847
        } else {
848
            $this->formElementFactory->createFormElement($this, $type, $formElement_data);
849
        }
850
    }
851
852
    /**
853
     * Display a report. Choose the report among
854
     *  - the requested 'select_report'
855
     *  - the last viewed report (stored in preferences)
856
     *  - the default report of this tracker
857
     *
858
     * If the user request a 'link-artifact-id' then display also manual and recent
859
     * panels to ease the selection of artifacts to link
860
     *
861
     * @param Tracker_IDisplayTrackerLayout  $layout          Displays the page header and footer
862
     * @param Codendi_Request                $request         The request
863
     * @param PFUser                           $current_user    The user who made the request
864
     *
865
     * @return void
866
     */
867
    public function displayAReport(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
868
        $report = null;
869
870
        //Does the user wants to change its report?
871
        if ($request->get('select_report')) {
872
            //Is the report id valid
873
            if ($report = $this->getReportFactory()->getReportById($request->get('select_report'), $current_user->getid())) {
874
                $current_user->setPreference('tracker_'. $this->id .'_last_report', $report->id);
875
            }
876
        }
877
878
        //If no valid report found. Search the last viewed report for the user
879
        if (! $report) {
880
            if ($report_id = $current_user->getPreference('tracker_'. $this->id .'_last_report')) {
881
                $report = $this->getReportFactory()->getReportById($report_id, $current_user->getid());
882
            }
883
        }
884
885
        //If no valid report found. Take the default one
886
        if (! $report) {
887
            $report = $this->getReportFactory()->getDefaultReportsByTrackerId($this->id);
888
        }
889
890
        //If no default one, take the first private one
891
        if (! $report) {
892
            $report_for_user = $this->getReportFactory()->getReportsByTrackerId($this->id, $current_user->getid());
893
            $report = array_shift($report_for_user);
894
        }
895
896
        $link_artifact_id = (int)$request->get('link-artifact-id');
897
        if ($link_artifact_id && !$request->get('report-only')) {
898
899
            $linked_artifact = Tracker_ArtifactFactory::instance()->getArtifactById($link_artifact_id);
900
901
            if (!$linked_artifact) {
902
                $err = "Linked artifact not found or doesn't exist";
903
                if (!$request->isAjax()) {
904
                    $GLOBALS['Response']->addFeedback('error', $err);
905
                    $GLOBALS['Response']->redirect('/');
906
                }
907
                die ($err);
0 ignored issues
show
Coding Style Compatibility introduced by
The method displayAReport() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
908
            }
909
            if (!$request->isAjax()) {
910
                //screwed up
911
                $GLOBALS['Response']->addFeedback('error', 'Something is wrong with your request');
912
                $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?aid='. $linked_artifact->getId());
913
            }
914
915
            echo $linked_artifact->fetchTitleWithoutUnsubscribeButton(
916
                $GLOBALS['Language']->getText('plugin_tracker_artifactlink', 'title_prefix')
917
            );
918
919
            echo '<input type="hidden" id="link-artifact-id" value="'. (int)$link_artifact_id .'" />';
920
921
            echo '<table id="tracker-link-artifact-different-ways" cellpadding="0" cellspacing="0" border="0"><tbody><tr>';
922
923
            //the fast ways
924
            echo '<td id="tracker-link-artifact-fast-ways">';
925
926
            //Manual
927
            echo '<div id="tracker-link-artifact-manual-way">';
928
            echo '<div class="boxtitle">';
929
            echo $GLOBALS['HTML']->getImage('ic/lightning-white.png', array('style' => 'vertical-align:middle')). '&nbsp;';
930
            echo $GLOBALS['Language']->getText('plugin_tracker_artifactlink', 'manual_panel_title');
931
            echo '</div>';
932
            echo '<div class="tracker-link-artifact-manual-way-content">';
933
            echo $GLOBALS['Language']->getText('plugin_tracker_artifactlink', 'manual_panel_desc');
934
            echo '<p><label for="link-artifact-manual-field">';
935
            echo $GLOBALS['Language']->getText('plugin_tracker_artifactlink', 'manual_panel_label');
936
            echo '</label><br />';
937
            echo '<input type="text" name="link-artifact[manual]" value="" id="link-artifact-manual-field" />';
938
            echo '</p>';
939
            echo '</div>';
940
            echo '</div>';
941
942
            //History
943
            echo '<div id="tracker-link-artifact-recentitems-way">';
944
            echo '<div class="boxtitle">';
945
            echo $GLOBALS['HTML']->getImage('ic/star-white.png', array('style' => 'vertical-align:middle')). '&nbsp;';
946
            echo $GLOBALS['Language']->getText('plugin_tracker_artifactlink', 'recent_panel_title');
947
            echo '</div>';
948
            echo '<div class="tracker-link-artifact-recentitems-way-content">';
949
            if ($recent_items = $current_user->getRecentElements()) {
950
                echo $GLOBALS['Language']->getText('plugin_tracker_artifactlink', 'recent_panel_desc');
951
                echo '<ul>';
952
                foreach ($recent_items as $item) {
953
                    if ($item['id'] != $link_artifact_id) {
954
                        echo '<li>';
955
                        echo '<input type="checkbox"
956
                                     name="link-artifact[recent][]"
957
                                     value="'. (int)$item['id'] .'" /> ';
958
                        echo $item['link'];
959
                        echo '</li>';
960
                    }
961
                }
962
                echo '</ul>';
963
            }
964
            echo '</div>';
965
            echo '</div>';
966
967
            //end of fast ways
968
            echo '</td>';
969
970
            //And the slow way (aka need to search)
971
            if ($report) {
972
                echo '<td><div id="tracker-link-artifact-slow-way">';
973
                echo '<div class="boxtitle">';
974
                echo $GLOBALS['HTML']->getImage('ic/magnifier-white.png', array('style' => 'vertical-align:middle')). '&nbsp;';
975
                echo $GLOBALS['Language']->getText('plugin_tracker_artifactlink', 'search_panel_title');
976
                echo '</div>';
977
                echo '<div id="tracker-link-artifact-slow-way-content">';
978
            }
979
        }
980
981
        if ($report) {
982
            $report->process($layout, $request, $current_user);
983
        } elseif (!$link_artifact_id) {
984
            $this->displayHeader($layout, $this->name, array());
985
            echo $GLOBALS['Language']->getText('plugin_tracker', 'no_reports_available');
986
987
            if ($this->userIsAdmin($current_user)) {
988
                $action = '?tracker='. (int)$this->getID() .'&func=create_new_public_report';
989
990
                echo '<form class="form-inline" action="'.$action.'" method="POST">'
991
                    . '<fieldset>'
992
                        . '<legend>'.$GLOBALS['Language']->getText('plugin_tracker', 'create_new_report').'</legend>'
993
                        . '<input required type="text" name="new_report_name" placeholder="'.$GLOBALS['Language']->getText('plugin_tracker', 'create_new_report_name').'" />'
994
                        . '<button type="submit" class="btn">'.$GLOBALS['Language']->getText('plugin_tracker', 'create_new_report_submit').'</button>'
995
                    . '</fieldset></form>';
996
            }
997
998
            $this->displayFooter($layout);
999
        }
1000
1001
        if ($link_artifact_id && !$request->get('report-only')) {
1002
            if ($report) {
1003
                echo '</div></div></td>'; //end of slow
1004
            }
1005
            echo '</tr></tbody></table>'; //end of ways
1006
1007
            echo '<div class="tracker-link-artifact-controls">';
1008
            echo '<a href="#cancel" onclick="myLightWindow.deactivate(); return false;">&laquo;&nbsp;'. $GLOBALS['Language']->getText('global', 'btn_cancel') .'</a>';
1009
            echo ' ';
1010
            echo '<button name="link-artifact-submit">'. $GLOBALS['Language']->getText('global', 'btn_submit') .'</button>';
1011
            echo '</div>';
1012
        }
1013
    }
1014
1015
    /**
1016
     * Display the submit form
1017
     */
1018
    public function displaySubmit(Tracker_IFetchTrackerSwitcher $layout, $request, $current_user, $link = null) {
1019
        if ($link) {
1020
            $source_artifact = $this->getTrackerArtifactFactory()->getArtifactByid($link);
1021
            $submit_renderer = new Tracker_Artifact_SubmitOverlayRenderer($this, $source_artifact, EventManager::instance(), $layout);
1022
        } else {
1023
            $submit_renderer = new Tracker_Artifact_SubmitRenderer($this, EventManager::instance(), $layout);
1024
        }
1025
        $submit_renderer->display($request, $current_user);
1026
    }
1027
1028
    /**
1029
     * Display the submit form
1030
     */
1031
    public function displaySearch(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
1032
        $hp = Codendi_HTMLPurifier::instance();
1033
1034
        $pm = ProjectManager::instance();
1035
        $group_id = $request->get('group_id');
1036
        $group = $pm->getProject($group_id);
1037
        if (!$group || !is_object($group) || $group->isError()) {
1038
            exit_no_group();
1039
        }
1040
1041
        $breadcrumbs = array(
1042
                array(
1043
                        'title' => $GLOBALS['Language']->getText('plugin_tracker_browse', 'search_result'),
1044
                        'url'   => TRACKER_BASE_URL.'/?tracker='. $this->getId(),
1045
                ),
1046
        );
1047
        $this->displayHeader($layout, $this->name, $breadcrumbs);
1048
        $html = '';
1049
1050
        $words    = $request->get('words');
1051
1052
        $criteria = 'OR';
1053
        if ($request->exist('exact') && $request->get('exact') == '1') {
1054
            $criteria = 'AND';
1055
        }
1056
        $offset = 0;
1057
        if ($request->exist('offset')) {
1058
            $offset = $request->get('offset');
1059
        }
1060
        $limit = 25;
1061
1062
        $tracker_artifact_dao = new Tracker_ArtifactDao();
1063
        $dar = $tracker_artifact_dao->searchByKeywords($this->getId(), $words, $criteria, $offset, $limit);
1064
        $rows_returned = $tracker_artifact_dao->foundRows();
1065
1066
        $no_rows = false;
1067
1068
        if ( $dar->rowCount() < 1 || $rows_returned < 1) {
1069
            $no_rows = true;
1070
            $html .= '<h2>'.$GLOBALS['Language']->getText('search_index','no_match_found', $hp->purify($words, CODENDI_PURIFIER_CONVERT_HTML)) .'</h2>';
1071
        } else {
1072
            $html .= '<h3>'.$GLOBALS['Language']->getText('search_index','search_res', array($hp->purify($words, CODENDI_PURIFIER_CONVERT_HTML), $rows_returned)).'</h3>';
1073
1074
            $title_arr = array();
1075
1076
            $art_field_fact = Tracker_FormElementFactory::instance();
1077
            $artifact_factory = Tracker_ArtifactFactory::instance();
1078
            $user_helper = UserHelper::instance();
1079
1080
            $summary_field = $this->getTitleField();
1081
            if ($summary_field && $summary_field->userCanRead()) {
1082
                $title_arr[] = $GLOBALS['Language']->getText('plugin_tracker_search_index','artifact_title');
1083
            }
1084
            $submitted_field = $art_field_fact->getFormElementByName($this->getId(), 'submitted_by');
1085
            if ($submitted_field && $submitted_field->userCanRead()) {
1086
                $title_arr[] = $GLOBALS['Language']->getText('search_index','submitted_by');
1087
            }
1088
            $date_field = $art_field_fact->getFormElementByName($this->getId(), 'open_date');
1089
            if ($date_field && $date_field->userCanRead()) {
1090
                $title_arr[] = $GLOBALS['Language']->getText('search_index','date');
1091
            }
1092
            $status_field = $this->getStatusField();
1093
            if ($status_field && $status_field->userCanRead()) {
1094
                $title_arr[] = $GLOBALS['Language']->getText('global','status');
1095
            }
1096
1097
            $html .= html_build_list_table_top ($title_arr);
1098
            $nb_artifacts = 0;
1099
            while ($row = $dar->getRow()) {
1100
                $nb_artifacts++;
1101
                $artifact_id = $row['artifact_id'];
1102
                $artifact    = $artifact_factory->getArtifactById($artifact_id);
1103
                if ($artifact->userCanView()) {
1104
                    $html .= '<tr class="' . html_get_alt_row_color($nb_artifacts) . '">';
1105
                    if ($summary_field->userCanRead()) {
1106
                        $html .= '<td><a href="'.TRACKER_BASE_URL.'/?aid=' . $artifact_id . '"><img src="' . util_get_image_theme('msg.png') . '" border="0" height="12" width="10"> '
1107
                        . $artifact->getTitle() . '</a></td>';
1108
                    }
1109
                    if ($submitted_field->userCanRead()) {
1110
                        $html .= '<td>' . $hp->purify($user_helper->getDisplayNameFromUserId($artifact->getSubmittedBy())) . '</td>';
1111
                    }
1112
                    if ($date_field->userCanRead()) {
1113
                        $html .=  '<td>' . format_date($GLOBALS['Language']->getText('system', 'datefmt'),$artifact->getSubmittedOn()) . '</td>';
1114
                    }
1115
                    if ($status_field->userCanRead()) {
1116
                        $html .=  '<td>' . $artifact->getStatus() . '</td>';
1117
                    }
1118
                    $html .=  '</tr>';
1119
                }
1120
            }
1121
            $html .= '</table>';
1122
        }
1123
1124
1125
        // Search result pagination
1126
        if ( !$no_rows && ( ($rows_returned > $nb_artifacts) || ($offset != 0) ) ) {
1127
            $html .= '<br />';
1128
            $url_params = array(
1129
                'exact' => $request->get('exact') === '1' ? 1 : 0,
1130
                'group_id' => $this->getGroupId(),
1131
                'tracker' => $this->getId(),
1132
                'type_of_search' => 'tracker',
1133
                'words' => urlencode($words),
1134
                'offset' => ($offset - $limit)
1135
            );
1136
            $html .= '<table class="boxitem" width="100%" cellpadding="5" cellspacing="0">';
1137
            $html .= '<tr>';
1138
            $html .= '<td align="left">';
1139
            if ($offset != 0) {
1140
                $html .= '<span class="normal"><b>';
1141
                $html .= '<a href="/search/?'. http_build_query($url_params);
1142
                $html .= '">' . "<b><img src=\"".util_get_image_theme('t2.png')."\" height=15 width=15 border=0 align=middle> ".$GLOBALS['Language']->getText('search_index','prev_res')." </a></b></span>";
1143
            } else {
1144
                $html .= '&nbsp;';
1145
            }
1146
            $html .= '</td><td align="right">';
1147
            if ( $rows_returned > $nb_artifacts && $rows_returned > $offset + $limit) {
1148
                $url_params['offset'] = ($offset + $limit);
1149
                $html .= '<span class="normal"><b>';
1150
                $html .= '<a href="/search/?'. http_build_query($url_params);
1151
                $html .= '"><b>' . $GLOBALS['Language']->getText('search_index','next_res').' <img src="' . util_get_image_theme('t.png') . '" height="15" width="15" border="0" align="middle"></a></b></span>';
1152
            } else {
1153
                $html .= '&nbsp;';
1154
            }
1155
            $html .= '</td></tr>';
1156
            $html .= '</table>';
1157
        }
1158
1159
        echo $html;
1160
        $this->displayFooter($layout);
1161
    }
1162
1163
    public function displayHeader(Tracker_IDisplayTrackerLayout $layout, $title, $breadcrumbs, $toolbar = null, array $params = array()) {
1164
        if ($project = ProjectManager::instance()->getProject($this->group_id)) {
1165
            $hp = Codendi_HTMLPurifier::instance();
1166
            $breadcrumbs = array_merge(array(array('title' => $this->name,
1167
                            'url'   => TRACKER_BASE_URL.'/?tracker='. $this->id)
1168
                    ),
1169
                    $breadcrumbs);
1170
            if (!$toolbar) {
1171
                $toolbar = $this->getDefaultToolbar();
1172
            }
1173
            $title = ($title ? $title .' - ' : ''). $hp->purify($this->name, CODENDI_PURIFIER_CONVERT_HTML);
1174
            $layout->displayHeader($project, $title, $breadcrumbs, $toolbar, $params);
1175
        }
1176
    }
1177
1178
    public function getDefaultToolbar() {
1179
        $toolbar = array();
1180
1181
        $toolbar[] = array(
1182
                'title'      => $GLOBALS['Language']->getText('plugin_tracker', 'submit_new_artifact'),
1183
                'url'        => $this->getSubmitUrl(),
1184
                'class'      => 'tracker-submit-new',
1185
                'submit-new' => 1
1186
        );
1187
1188
        $artifact_by_email_status = $this->getArtifactByMailStatus();
1189
        if ($artifact_by_email_status->canCreateArtifact($this)) {
1190
            $email_domain = ForgeConfig::get('sys_default_mail_domain');
1191
1192
            if (! $email_domain) {
1193
                $email_domain = ForgeConfig::get('sys_default_domain');
1194
            }
1195
1196
            $email = trackerPlugin::EMAILGATEWAY_INSECURE_ARTIFACT_CREATION .'+'. $this->id .'@'. $email_domain;
1197
            $email = Codendi_HTMLPurifier::instance()->purify($email);
1198
            $toolbar[] = array(
1199
                    'title'      => '<span class="email-tracker" data-email="'. $email .'"><i class="icon-envelope"></i></span>',
1200
                    'url'        => 'javascript:;',
1201
                    'submit-new' => 1
1202
            );
1203
        }
1204
1205
        if (UserManager::instance()->getCurrentUser()->isLoggedIn()) {
1206
            $toolbar[] = array(
1207
                    'title' => $GLOBALS['Language']->getText('plugin_tracker', 'notifications'),
1208
                    'url'   => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=notifications',
1209
            );
1210
        }
1211
        if ($this->userIsAdmin()) {
1212
            $toolbar[] = array(
1213
                    'title' => $GLOBALS['Language']->getText('plugin_tracker', 'administration'),
1214
                    'url'   => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin'
1215
            );
1216
        }
1217
        $toolbar[] = array(
1218
                'title' => $GLOBALS['Language']->getText('plugin_tracker', 'help'),
1219
                'url'   => 'javascript:help_window(\''.get_server_url().'/doc/'.UserManager::instance()->getCurrentUser()->getShortLocale().'/user-guide/tracker.html\');',
1220
        );
1221
1222
        return $toolbar;
1223
    }
1224
1225
    public function displayFooter(Tracker_IDisplayTrackerLayout $layout) {
1226
        if ($project = ProjectManager::instance()->getProject($this->group_id)) {
1227
            $layout->displayFooter($project);
1228
        }
1229
    }
1230
1231
    protected function getAdminItems() {
1232
        $items = array(
1233
                'editoptions' => array(
1234
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-editoptions',
1235
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_include_type','settings'),
1236
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_include_type','settings'),
1237
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_include_type','define_title'),
1238
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-general.png'),
1239
                ),
1240
                'editperms' => array(
1241
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-perms',
1242
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_include_type','permissions'),
1243
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_include_type','manage_permissions'),
1244
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_include_type','define_manage_permissions'),
1245
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-perms.png'),
1246
                ),
1247
                'editformElements' => array(
1248
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-formElements',
1249
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_include_type','field_usage'),
1250
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_include_type','mng_field_usage'),
1251
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_include_type','define_use'),
1252
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-form.png'),
1253
                ),
1254
                'dependencies' => array(
1255
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-dependencies',
1256
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_admin','manage_dependencies'),
1257
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_admin','manage_dependencies'),
1258
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_admin','manage_dependencies_desc'),
1259
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-fdependencies.png'),
1260
                ),
1261
                'editsemantic' => array(
1262
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-semantic',
1263
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_admin','semantic'),
1264
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_admin','manage_semantic'),
1265
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_admin','manage_semantic_desc'),
1266
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-semantic.png'),
1267
                ),
1268
                'editworkflow' => array(
1269
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func='. Workflow::FUNC_ADMIN_RULES,
1270
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_admin','workflow'),
1271
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_admin','manage_workflow'),
1272
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_admin','manage_workflow_desc'),
1273
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-workflow.png'),
1274
                ),
1275
                'editcanned' => array(
1276
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-canned',
1277
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_include_type','canned_resp'),
1278
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_include_type','mng_response'),
1279
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_include_type','add_del_resp'),
1280
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-canned.png'),
1281
                ),
1282
                'editnotifications' => array(
1283
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=notifications',
1284
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_include_type','mail_notif'),
1285
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_include_type','mail_notif'),
1286
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_include_type','define_notif'),
1287
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-notifs.png'),
1288
                ),
1289
                'csvimport' => array(
1290
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-csvimport',
1291
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_admin','csv_import'),
1292
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_admin','csv_import'),
1293
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_admin','csv_import_desc'),
1294
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-import.png'),
1295
                ),
1296
                'export' => array(
1297
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-export',
1298
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_admin','export'),
1299
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_admin','export'),
1300
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_admin','export_desc'),
1301
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-export.png'),
1302
                ),
1303
                'hierarchy' => array(
1304
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-hierarchy',
1305
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_admin','hierarchy'),
1306
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_admin','hierarchy'),
1307
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_admin','hierarchy_desc'),
1308
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-hierarchy.png'),
1309
                ),
1310
                'clean' => array(
1311
                        'url'         => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin-clean',
1312
                        'short_title' => $GLOBALS['Language']->getText('plugin_tracker_admin','clean'),
1313
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_admin','clean'),
1314
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_admin','clean_desc'),
1315
                        'img'         => $GLOBALS['HTML']->getImagePath('ic/48/tracker-delete.png'),
1316
                ),
1317
        );
1318
1319
        return $items;
1320
    }
1321
1322
    public function displayAdminHeader(Tracker_IDisplayTrackerLayout $layout, $title, $breadcrumbs) {
1323
        if ($project = ProjectManager::instance()->getProject($this->group_id)) {
1324
            $hp = Codendi_HTMLPurifier::instance();
1325
            $title = ($title ? $title .' - ' : ''). $GLOBALS['Language']->getText('plugin_tracker_include_type','administration');
1326
            $toolbar = null;
1327
            if ($this->userIsAdmin()) {
1328
                $breadcrumbs = array_merge(
1329
                        array(
1330
                        array(
1331
                                'title' => $GLOBALS['Language']->getText('plugin_tracker_include_type','administration'),
1332
                                'url'   => TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=admin',
1333
                        ),
1334
                        ),
1335
                        $breadcrumbs
1336
                );
1337
                $toolbar = $this->getAdminItems();
1338
            }
1339
            $this->displayHeader($layout, $title, $breadcrumbs, $toolbar);
1340
        }
1341
    }
1342
    public function displayAdmin(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
1343
        $hp = Codendi_HTMLPurifier::instance();
1344
        $title = '';
1345
        $breadcrumbs = array();
1346
        $this->displayAdminHeader($layout, $title, $breadcrumbs);
1347
        echo $this->fetchAdminMenu($this->getAdminItems());
1348
        $this->displayFooter($layout);
1349
    }
1350
    /**
1351
     * Display the items of the menu and their description
1352
     *
1353
     * @param array $items the items, each item is ['url', 'title', 'description'].
1354
     *                     Only name is mandatory (else the item is not displayed.
1355
     *
1356
     * @return string html
1357
     */
1358
    protected function fetchAdminMenu($items) {
1359
        $hp = Codendi_HTMLPurifier::instance();
1360
        $html = '';
1361
        $cleaned_items = $items;
1362
        foreach($cleaned_items as $key => $item) {
1363
            if (!isset($item['title'])) {
1364
                unset($cleaned_items[$key]);
1365
            }
1366
        }
1367
        if ($cleaned_items) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cleaned_items of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1368
            $html .= '<table id="tracker_admin_menu">';
1369
            $chunks = array_chunk($cleaned_items, 2);
1370
            foreach($chunks as $row) {
1371
                $html .= '<tr valign="top">';
1372
                foreach($row as $item) {
1373
                    $html .= '<td width="450">';
1374
                    $html .= '<H3>';
1375
                    $title =  $hp->purify($item['title'], CODENDI_PURIFIER_CONVERT_HTML) ;
1376
                    if (isset($item['url'])) {
1377
                        $html .= '<a href="'.$item['url'].'">';
1378
                        if (isset($item['img']) && $item['img']) {
1379
                            $html .= $GLOBALS['HTML']->getAbsoluteImage($item['img'], array(
1380
                                    'style' => 'float:left;',
1381
                            ));
1382
                        }
1383
                        $html .= $title;
1384
                        $html .= '</a>';
1385
                    } else {
1386
                        $html .= $title;
1387
                    }
1388
                    $html .= '</h3>';
1389
                    if (isset($item['description'])) {
1390
                        $html .= '<div>'. $hp->purify($item['description'], CODENDI_PURIFIER_BASIC, $this->getGroupId()) .'</div>';
1391
                    }
1392
                    $html .= '</td>';
1393
                }
1394
                $html .= '</tr>';
1395
            }
1396
            $html .= '</table>';
1397
        }
1398
        return $html;
1399
    }
1400
1401
    public function displayAdminItemHeader(Tracker_IDisplayTrackerLayout $layout, $item, $breadcrumbs = array(), $title = null) {
1402
        $items = $this->getAdminItems();
1403
        $title = $title ? $title : $items[$item]['title'];
1404
        $breadcrumbs = array_merge(
1405
                array(
1406
                $items[$item]
1407
                ),
1408
                $breadcrumbs
1409
        );
1410
        $this->displayAdminHeader($layout, $title, $breadcrumbs);
1411
        echo '<h1>'. $title .'</h1>';
1412
    }
1413
1414
    public function getColor() {
1415
        return $this->color;
1416
    }
1417
1418
    public function isEmailgatewayEnabled() {
1419
        return $this->enable_emailgateway;
1420
    }
1421
1422
    protected function displayAdminOptions(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
1423
        $this->displayWarningGeneralsettings();
1424
        $this->displayAdminItemHeader($layout, 'editoptions');
1425
1426
        $this->renderer->renderToPage(
1427
            'tracker-general-settings',
1428
            new Tracker_GeneralSettings_Presenter(
1429
                $this,
1430
                TRACKER_BASE_URL.'/?tracker='. (int)$this->id .'&func=admin-editoptions',
1431
                new Tracker_ColorPresenterCollection($this),
1432
                $this->getTrackerPluginConfig(),
1433
                $this->getArtifactByMailStatus()
1434
            )
1435
        );
1436
1437
        $this->displayFooter($layout);
1438
    }
1439
1440
    public function displayAdminPermsHeader(Tracker_IDisplayTrackerLayout $layout, $title, $breadcrumbs) {
1441
        $items = $this->getAdminItems();
1442
        $breadcrumbs = array_merge(array(
1443
                $items['editperms']
1444
                ), $breadcrumbs);
1445
        $this->displayAdminHeader($layout, $title, $breadcrumbs);
1446
    }
1447
1448
    public function getPermsItems() {
1449
        return array(
1450
                'tracker' => array(
1451
                        'url'         => TRACKER_BASE_URL.'/?tracker='.(int)$this->getId().'&amp;func=admin-perms-tracker',
1452
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_include_type','manage_tracker_permissions'),
1453
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_include_type','define_manage_tracker_permissions')
1454
                ),
1455
                'fields' => array(
1456
                        'url'         => TRACKER_BASE_URL.'/?tracker='.(int)$this->getId().'&amp;func=admin-perms-fields',
1457
                        'title'       => $GLOBALS['Language']->getText('plugin_tracker_include_type','manage_fields_tracker_permissions'),
1458
                        'description' => $GLOBALS['Language']->getText('plugin_tracker_include_type','define_manage_fields_tracker_permissions')
1459
                )
1460
        );
1461
    }
1462
1463
    public function displayAdminPerms(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
1464
        $items = $this->getAdminItems();
1465
        $title = $items['editperms']['title'];
1466
        $breadcrumbs = array();
1467
        $this->displayAdminPermsHeader($layout, $title, $breadcrumbs);
1468
        echo '<h2>'. $title .'</h2>';
1469
        echo $this->fetchAdminMenu($this->getPermsItems());
1470
        $this->displayFooter($layout);
1471
    }
1472
1473
    public function displayAdminPermsFields(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
1474
        $items = $this->getPermsItems();
1475
        $title = $items['fields']['title'];
1476
        $breadcrumbs = array(
1477
                $items['fields']
1478
        );
1479
        $this->displayAdminPermsHeader($layout, $title, $breadcrumbs);
1480
        echo '<h2>'. $title .'</h2>';
1481
1482
        $hp = Codendi_HTMLPurifier::instance();
1483
1484
        $group_first = $request->get('group_first') ? 1 : 0;
1485
        $selected_id = $request->get('selected_id');
1486
        $selected_id = $selected_id ? $selected_id : false;
1487
        $ugroups_permissions = plugin_tracker_permission_get_field_tracker_ugroups_permissions(
1488
                $this->getGroupId(),
1489
                $this->getId(),
1490
                Tracker_FormElementFactory::instance()->getUsedFields($this),
1491
                false
1492
        );
1493
1494
        $submit_permission = 'PLUGIN_TRACKER_FIELD_SUBMIT';
1495
        $read_permission   = 'PLUGIN_TRACKER_FIELD_READ';
1496
        $update_permission = 'PLUGIN_TRACKER_FIELD_UPDATE';
1497
        $none              = 'PLUGIN_TRACKER_NONE';
1498
        $attributes_for_selected = 'selected="selected" style="background:#EEE;"'; //TODO: put style in stylesheet
1499
1500
        $html = '';
1501
1502
        //form
1503
        $url_action_without_group_first = '?tracker='. (int)$this->getID() .'&amp;func=admin-perms-fields';
1504
        $url_action_with_group_first    = $url_action_without_group_first .'&amp;group_first='. $group_first;
1505
1506
        //The change form
1507
        $group_first_value = $group_first;
1508
        $group_id          = (int)$this->getGroupID();
1509
        $atid              = (int)$this->getID();
1510
1511
        $url_action_with_group_first_for_js = str_replace('&amp;', '&', $url_action_with_group_first) .'&selected_id=';
1512
1513
        $html .= <<<EOS
1514
            <script type="text/javascript">
1515
            <!--
1516
            function changeFirstPartId(wanted) {
1517
                location.href = '$url_action_with_group_first_for_js' + wanted;
1518
            }
1519
            //-->
1520
            </script>
1521
EOS;
1522
1523
1524
        if ($group_first) {
1525
            //We reorganize the associative array
1526
            $tablo = $ugroups_permissions;
1527
            $ugroups_permissions = array();
1528
            foreach($tablo as $key_field => $value_field) {
1529
                foreach($value_field['ugroups'] as $key_ugroup => $value_ugroup) {
1530
                    if (!isset($ugroups_permissions[$key_ugroup])) {
1531
                        $ugroups_permissions[$key_ugroup] = array(
1532
                                'values'              => $value_ugroup['ugroup'],
1533
                                'related_parts'       => array(),
1534
                                'tracker_permissions' => $value_ugroup['tracker_permissions']
1535
                        );
1536
1537
                    }
1538
                    $ugroups_permissions[$key_ugroup]['related_parts'][$key_field] = array(
1539
                            'values'       => $value_field['field'],
1540
                            'permissions' => $value_ugroup['permissions']
1541
                    );
1542
                }
1543
            }
1544
            ksort($ugroups_permissions);
1545
            $header = array(
1546
                    $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', 'ugroup'),
1547
                    $GLOBALS['Language']->getText('plugin_tracker_include_report', 'field_label'),
1548
                    $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', $submit_permission),
1549
                    $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', 'permissions')) ;
1550
        } else {
1551
            foreach($ugroups_permissions as $key_field => $value_field) {
1552
                $ugroups_permissions[$key_field]['values']        =& $ugroups_permissions[$key_field]['field'];
1553
                $ugroups_permissions[$key_field]['related_parts'] =& $ugroups_permissions[$key_field]['ugroups'];
1554
                foreach($value_field['ugroups'] as $key_ugroup => $value_ugroup) {
1555
                    $ugroups_permissions[$key_field]['related_parts'][$key_ugroup]['values'] =& $ugroups_permissions[$key_field]['related_parts'][$key_ugroup]['ugroup'];
1556
                }
1557
                ksort($ugroups_permissions[$key_field]['related_parts']);
1558
                reset($ugroups_permissions[$key_field]['related_parts']);
1559
            }
1560
            $header = array(
1561
                    $GLOBALS['Language']->getText('plugin_tracker_include_report', 'field_label'),
1562
                    $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', 'ugroup'),
1563
                    $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', $submit_permission),
1564
                    $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', 'permissions')) ;
1565
        }
1566
        reset($ugroups_permissions);
1567
        list($key, $value) = each($ugroups_permissions);
0 ignored issues
show
Unused Code introduced by
The assignment to $value is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1568
1569
        //header
1570
        if (($group_first && count($ugroups_permissions) < 1) || (!$group_first && count($ugroups_permissions[$key]['related_parts']) < 1)) {
1571
            $html .= $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', 'fields_no_ugroups');
1572
        } else {
1573
1574
            //The permission form
1575
            $html .= '<form name="form_tracker_permissions" action="'. $url_action_with_group_first .'" method="post">';
1576
            $html .= '<div>';
1577
            $html .= '<input type="hidden" name="selected_id" value="'. (int)$selected_id .'" />';
1578
1579
            //intro
1580
            $html .= $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', 'fields_tracker_intro');
1581
1582
            //We display 'group_first' or 'field_first'
1583
            if ($group_first) {
1584
                $html .= $GLOBALS['Language']->getText('plugin_tracker_admin_permissions',
1585
                        'fields_tracker_toggle_field',
1586
                        $url_action_without_group_first.'&amp;group_first=0');
1587
            } else {
1588
                $html .= $GLOBALS['Language']->getText('plugin_tracker_admin_permissions',
1589
                        'fields_tracker_toggle_group',
1590
                        $url_action_without_group_first.'&amp;group_first=1');
1591
            }
1592
1593
            $html .= html_build_list_table_top($header);
1594
1595
            //body
1596
            $i = 0;
1597
            $a_star_is_displayed = false;
1598
            $related_parts = array();
1599
            //The select box for the ugroups or fields (depending $group_first)
1600
            $html .= '<tr class="'. util_get_alt_row_color($i++) .'">';
1601
            $html .= '<td rowspan="'. (count($ugroups_permissions[$key]['related_parts'])+1) .'" style="vertical-align:top;">';
1602
            $html .= '<select onchange="changeFirstPartId(this.options[this.selectedIndex].value);">';
1603
1604
            foreach($ugroups_permissions as $part_permissions) {
1605
                if ($selected_id === false) {
1606
                    $selected_id = $part_permissions['values']['id'];
1607
                }
1608
                $html .= '<option value="'. (int)$part_permissions['values']['id'] .'" ';
1609
                if ($part_permissions['values']['id'] === $selected_id) {
1610
                    $first_part    = $part_permissions['values'];
1611
                    $related_parts = $part_permissions['related_parts'];
1612
                    $html .= $attributes_for_selected;
1613
                }
1614
                $html .= ' >';
1615
                $html .= $hp->purify($part_permissions['values']['name']);
1616
                if ($group_first) {
1617
                    if (isset($part_permissions['tracker_permissions'])
1618
                            && count($part_permissions['tracker_permissions']) === 0) {
1619
                        $html .= ' *';
1620
                        $a_star_is_displayed = true;
1621
                    }
1622
                }
1623
                $html .= '</option>';
1624
            }
1625
            $html .= '</select>';
1626
            $html .= '</td>';
1627
            $is_first = true;
1628
1629
            //The permissions for the current item (field or ugroup, depending $group_id)
1630
            foreach($related_parts as $ugroup_permissions) {
1631
                $second_part = $ugroup_permissions['values'];
1632
                $permissions = $ugroup_permissions['permissions'];
1633
1634
1635
                //The group
1636
                if (!$is_first) {
1637
                    $html .= '<tr class="'. util_get_alt_row_color($i++) .'">';
1638
                } else {
1639
                    $is_first = false;
1640
                }
1641
                $html .= '<td>';
1642
1643
                $name = '<a href="'. $url_action_without_group_first .'&amp;selected_id='. (int)$second_part['id'] .'&amp;group_first='. ($group_first?0:1) .'">';
1644
                $name .=  $hp->purify($second_part['name']) ;
1645
                $name .= '</a>';
1646
                if (!$group_first && isset($ugroup_permissions['tracker_permissions']) && count($ugroup_permissions['tracker_permissions']) === 0) {
1647
                    $name = '<span >'. $name .' *</span>'; //TODO css
1648
                    $a_star_is_displayed = true;
1649
                }
1650
                $html .= $name;
1651
1652
                $html .= '</td>';
1653
1654
                //The permissions
1655
                {
1656
                    //Submit permission
1657
                    $html .= '<td style="text-align:center;">';
1658
                    if ($group_first) {
1659
                        $name_of_variable = "permissions[".(int)$second_part['id']."][".(int)$first_part['id']."]";
1660
                    } else {
1661
                        $name_of_variable = "permissions[".(int)$first_part['id']."][".(int)$second_part['id']."]";
1662
                    }
1663
                    $html .= '<input type="hidden" name="'. $name_of_variable .'[submit]" value="off"/>';
1664
1665
                    $can_submit = ($group_first && $second_part['field']->isSubmitable())
1666
                            || (!$group_first && $first_part['field']->isSubmitable());
1667
1668
                    $can_update = ($group_first && $second_part['field']->isUpdateable())
1669
                            || (!$group_first && $first_part['field']->isUpdateable());
1670
1671
                    $html .= "<input type='checkbox' name=\"".$name_of_variable.'[submit]"  '.
1672
                            (isset($permissions[$submit_permission])?"checked='checked'":"")." ".($can_submit?"":"disabled='disabled'")." /> ";
1673
                    $html .= "</td><td>";
1674
1675
1676
                    //Other permissions (R/W)
1677
                    $html .= "<select name='".$name_of_variable."[others]' >";
1678
                    $html .= "<option value='100' ".(!isset($permissions[$read_permission]) && !isset($permissions[$update_permission])?$attributes_for_selected:"")." >".$GLOBALS['Language']->getText('plugin_tracker_admin_permissions', $none)."</option>";
1679
                    $html .= "<option value='0' ".(isset($permissions[$read_permission]) && !isset($permissions[$update_permission])?$attributes_for_selected:"")." >".$GLOBALS['Language']->getText('plugin_tracker_admin_permissions', $read_permission)."</option>";
1680
1681
                    if ($can_update) {
1682
                        $html .= "<option value='1' ".(isset($permissions[$update_permission])?$attributes_for_selected:"")." >".$GLOBALS['Language']->getText('plugin_tracker_admin_permissions', $update_permission)."</option>";
1683
                    }
1684
                    $html .= "</select>";
1685
1686
                }
1687
                $html .= "</td>";
1688
                $html .= "</tr>\n";
1689
            }
1690
1691
            //end of table
1692
            $html .= "</table>";
1693
            if ($a_star_is_displayed) {
1694
                $html .= $GLOBALS['Language']->getText('plugin_tracker_admin_permissions', 'ug_may_have_no_access', TRACKER_BASE_URL."/?tracker=".(int)$this->getID()."&func=admin-perms-tracker");
1695
            }
1696
            $html .= "<input type='submit' name='update' value=\"".$GLOBALS['Language']->getText('project_admin_permissions','submit_perm')."\" />";
1697
            //{{{20050602 NTY: removed. what is default permissions ???
1698
            //$html .= "<input type='submit' name='reset' value=\"".$GLOBALS['Language']->getText('project_admin_permissions','reset_to_def')."\" />";
1699
            //}}}
1700
        }
1701
        $html .= "</div></form>";
1702
        $html .= "<p>";
1703
        $html .= $GLOBALS['Language']->getText('project_admin_permissions',
1704
                'admins_create_modify_ug',
1705
                array(
1706
                "/project/admin/editugroup.php?func=create&group_id=".(int)$this->getGroupID(),
1707
                "/project/admin/ugroup.php?group_id=".(int)$this->getGroupID()
1708
                )
1709
        );
1710
        $html .= "</p>";
1711
        print $html;
1712
1713
        $this->displayFooter($layout);
1714
    }
1715
1716
    public function displayAdminFormElementsHeader(Tracker_IDisplayTrackerLayout $layout, $title, $breadcrumbs) {
1717
        $items = $this->getAdminItems();
1718
        $breadcrumbs = array_merge(array(
1719
                $items['editformElements']
1720
                ), $breadcrumbs);
1721
        $this->displayAdminHeader($layout, $title, $breadcrumbs);
1722
    }
1723
1724
    public function displayAdminFormElements(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
1725
        $hp = Codendi_HTMLPurifier::instance();
1726
        $this->displayWarningArtifactByEmailRequiredFields();
1727
        $items = $this->getAdminItems();
1728
        $title = $items['editformElements']['title'];
1729
        $this->displayAdminFormElementsHeader($layout, $title, array());
1730
1731
        echo '<h2>'. $title .'</h2>';
1732
        echo '<form name="form1" method="POST" action="'.TRACKER_BASE_URL.'/?tracker='. (int)$this->id .'&amp;func=admin-formElements">';
1733
1734
        echo '  <div class="container-fluid">
1735
                  <div class="row-fluid">
1736
                    <div class="span3">';
1737
        $this->fetchAdminPalette();
1738
        echo '      </div>
1739
                    <div class="span9">';
1740
        echo $this->fetchAdminFormElements();
1741
        echo '      </div>
1742
                  </div>
1743
                </div>
1744
              </form>';
1745
        $this->displayFooter($layout);
1746
    }
1747
1748
    private function fetchAdminPalette() {
1749
        echo '<div class="tracker-admin-palette">';
1750
1751
        $this->formElementFactory->displayFactories($this);
1752
1753
        $w = new Widget_Static($GLOBALS['Language']->getText('plugin_tracker_formelement_admin','unused_elements'));
1754
        $unused_elements_content = '';
1755
        $unused_elements_content = $GLOBALS['Language']->getText('plugin_tracker_formelement_admin','unused_elements_desc');
1756
        $unused_elements_content .= '<div class="tracker-admin-palette-content"><table>';
1757
        foreach(Tracker_FormElementFactory::instance()->getUnusedFormElementForTracker($this) as $f) {
1758
            $unused_elements_content .= $f->fetchAdminAdd();
1759
        }
1760
        $unused_elements_content .= '</table></div>';
1761
        $w->setContent($unused_elements_content);
1762
        $w->display();
1763
1764
        echo '</div>';
1765
    }
1766
1767
    public function displayAdminCSVImportHeader(Tracker_IDisplayTrackerLayout $layout, $title, $breadcrumbs) {
1768
        $items = $this->getAdminItems();
1769
        $breadcrumbs = array_merge(array(
1770
                $items['csvimport']
1771
                ), $breadcrumbs);
1772
        $this->displayAdminHeader($layout, $title, $breadcrumbs);
1773
    }
1774
1775
    public function displayAdminCSVImport(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) {
1776
        $hp = Codendi_HTMLPurifier::instance();
1777
        $items = $this->getAdminItems();
1778
        $title = $items['csvimport']['title'];
1779
        $this->displayAdminCSVImportHeader($layout, $title, array());
1780
1781
        echo '<h2>'. $title . ' ' . help_button('tracker.html#tracker-artifact-import') . '</h2>';
1782
        echo '<form name="form1" method="POST" enctype="multipart/form-data" action="'.TRACKER_BASE_URL.'/?tracker='. (int)$this->id .'&amp;func=admin-csvimport">';
1783
        echo '<input type="file" name="csv_filename" size="50">';
1784
        echo '<br>';
1785
        echo '<span class="smaller"><em>';
1786
        echo $GLOBALS['Language']->getText('plugin_tracker_import', 'file_upload_instructions', formatByteToMb($GLOBALS['sys_max_size_upload']));
1787
        echo '</em></span>';
1788
        echo '<br>';
1789
        echo $GLOBALS['Language']->getText('plugin_tracker_admin_import','send_notifications');
1790
        echo '<input type="checkbox" name="notify" value="ok" />';
1791
        echo '<br>';
1792
        echo '<input type="hidden" name="action" value="import_preview">';
1793
        echo '<input type="submit" value="'.$GLOBALS['Language']->getText('plugin_tracker_import','submit_info').'">';
1794
        echo '</form>';
1795
        $this->displayFooter($layout);
1796
    }
1797
1798
    public function displayAdminClean(Tracker_IDisplayTrackerLayout $layout) {
1799
        $token = new CSRFSynchronizerToken(TRACKER_BASE_URL.'/?tracker='. (int)$this->id.'&amp;func=admin-delete-artifact-confirm');
1800
        $this->displayAdminItemHeader($layout, 'clean');
1801
        echo '<p>'.$GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_info').'</p>';
1802
        echo '<form name="delete_artifact" method="post" action="'.TRACKER_BASE_URL.'/?tracker='. (int)$this->id.'&amp;func=admin-delete-artifact-confirm">';
1803
        echo $token->fetchHTMLInput();
1804
        echo '<label>'.$GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_id').' <input type="text" name="id" value=""></label>';
1805
        echo '<br>';
1806
        echo '<input type="submit" value="'.$GLOBALS['Language']->getText('global','btn_submit').'">';
1807
        echo '</form>';
1808
        $this->displayFooter($layout);
1809
    }
1810
1811
    public function displayAdminConfirmDelete(Tracker_IDisplayTrackerLayout $layout, Tracker_Artifact $artifact) {
1812
        $token = new CSRFSynchronizerToken(TRACKER_BASE_URL.'/?tracker='. (int)$this->id.'&amp;func=admin-delete-artifact');
1813
        $this->displayAdminItemHeader($layout, 'clean');
1814
        echo '<div class="tracker_confirm_delete">';
1815
        echo '<form name="delete_artifact" method="post" action="'.TRACKER_BASE_URL.'/?tracker='. (int)$this->id.'&amp;func=admin-delete-artifact">';
1816
        echo $token->fetchHTMLInput();
1817
        echo $GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_confirm_text', array($artifact->getXRefAndTitle()));
1818
        echo '<div class="tracker_confirm_delete_preview">';
1819
        echo $this->fetchFormElementsReadOnly($artifact);
1820
        echo '</div>';
1821
        echo '<div class="tracker_confirm_delete_buttons">';
1822
        echo '<input type="submit" tabindex="2" name="confirm" value="'. $GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_confirm') .'" />';
1823
        echo '<input type="submit" tabindex="1" name="cancel" value="'. $GLOBALS['Language']->getText('plugin_tracker_admin', 'clean_cancel') .'" />';
1824
        echo '</div>';
1825
        echo '<input type="hidden" name="id" value="'.$artifact->getId().'" />';
1826
        echo '</form>';
1827
        echo '</div>';
1828
        $this->displayFooter($layout);
1829
    }
1830
1831
    public function displayMasschangeForm(Tracker_IDisplayTrackerLayout $layout, $masschange_aids) {
1832
        $breadcrumbs = array(
1833
            array(
1834
                'title' => $GLOBALS['Language']->getText('plugin_tracker_index', 'mass_change'),
1835
                'url'   => '#' //TRACKER_BASE_URL.'/?tracker='. $this->id .'&amp;func=display-masschange-form',
1836
            ),
1837
        );
1838
        $this->displayHeader($layout, $this->name, $breadcrumbs);
1839
1840
        $this->renderer->renderToPage(
1841
            'masschange',
1842
            new Tracker_Masschange_Presenter(
1843
                $masschange_aids,
1844
                $this->fetchFormElementsMasschange(),
1845
                $this->displayRulesAsJavascript()
1846
            )
1847
        );
1848
1849
        $this->displayFooter($layout);
1850
    }
1851
1852
    public function updateArtifactsMasschange(
1853
        $submitter,
1854
        $masschange_aids,
1855
        $fields_data,
1856
        $comment,
1857
        $send_notifications,
1858
        $comment_format
1859
    ) {
1860
        $fields_data['request_method_called'] = 'artifact-masschange';
1861
1862
        $this->augmentDataFromRequest($fields_data);
1863
1864
        unset($fields_data['request_method_called']);
1865
1866
        $not_updated_aids = array();
1867
        foreach ( $masschange_aids as $aid ) {
1868
            $artifact = Tracker_ArtifactFactory::instance()->getArtifactById($aid);
1869
            if ( !$artifact ) {
1870
                $not_updated_aids[] = $aid;
1871
                continue;
1872
            }
1873
1874
            try {
1875
                $artifact->createNewChangeset($fields_data, $comment, $submitter, $send_notifications, $comment_format);
1876
            } catch (Tracker_NoChangeException $e) {
1877
                $GLOBALS['Response']->addFeedback('info', $e->getMessage(), CODENDI_PURIFIER_LIGHT);
1878
                $not_updated_aids[] = $aid;
1879
                continue;
1880
            } catch (Tracker_Exception $e) {
1881
                $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'unable_to_update_artifact', array($aid)));
1882
                $GLOBALS['Response']->addFeedback('error', $e->getMessage());
1883
                $not_updated_aids[] = $aid;
1884
                continue;
1885
            }
1886
        }
1887
        if ( !empty($not_updated_aids) ) {
1888
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_index', 'mass_update_failed', implode(', ', $not_updated_aids)));
1889
            return false;
1890
        } else {
1891
            $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_index', 'mass_update_success'));
1892
            $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_index', 'updated_aid', implode(', ', $masschange_aids)));
1893
            return true;
1894
        }
1895
    }
1896
1897
    protected function editOptions($request) {
1898
        $old_item_name = $this->getItemName();
1899
        $old_name      = $this->getName();
1900
1901
        $this->name                         = trim($request->getValidated('name', 'string', ''));
1902
        $this->description                  = trim($request->getValidated('description', 'text', ''));
1903
        $this->color                        = trim($request->getValidated('tracker_color', 'string', ''));
1904
        $this->item_name                    = trim($request->getValidated('item_name', 'string', ''));
1905
        $this->allow_copy                   = $request->getValidated('allow_copy') ? 1 : 0;
1906
        $this->enable_emailgateway          = $request->getValidated('enable_emailgateway') ? 1 : 0;
1907
        $this->submit_instructions          = $request->getValidated('submit_instructions', 'text', '');
1908
        $this->browse_instructions          = $request->getValidated('browse_instructions', 'text', '');
1909
        $this->instantiate_for_new_projects = $request->getValidated('instantiate_for_new_projects') ? 1 : 0;
1910
        $this->log_priority_changes         = $request->getValidated('log_priority_changes') ? 1 : 0;
1911
1912
        if (!$this->name || !$this->description || !$this->color || !$this->item_name) {
1913
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_type','name_requ'));
1914
        } else {
1915
            if ($old_name != $this->name) {
1916
                if(TrackerFactory::instance()->isNameExists($this->name, $this->group_id)) {
1917
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_type','name_already_exists', $this->name));
1918
                    return false;
1919
                }
1920
            }
1921
            if ($old_item_name != $this->item_name) {
1922
                if (!$this->itemNameIsValid($this->item_name)) {
1923
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_type','invalid_shortname', $this->item_name, CODENDI_PURIFIER_CONVERT_HTML));
1924
                    return false;
1925
                }
1926
1927
                if(TrackerFactory::instance()->isShortNameExists($this->item_name, $this->group_id)) {
1928
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_type','shortname_already_exists', $this->item_name));
1929
                    return false;
1930
                }
1931
1932
                $reference_manager = ReferenceManager::instance();
1933
                if (!$reference_manager->checkKeyword($this->item_name) ) {
1934
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_type','invalid_shortname', $this->item_name, CODENDI_PURIFIER_CONVERT_HTML));
1935
                    return false;
1936
                }
1937
1938
                if ($reference_manager->_isKeywordExists($this->item_name, $this->group_id)) {
1939
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_type', 'shortname_already_exists', $this->item_name, CODENDI_PURIFIER_CONVERT_HTML));
1940
                    return false;
1941
                }
1942
1943
                //Update reference and cross references
1944
                //WARNING this replace existing reference(s) so that all old_item_name reference won't be extracted anymore
1945
                $reference_manager->updateProjectReferenceShortName($this->group_id, $old_item_name, $this->item_name);
1946
1947
                $artifact_link_value_dao = new Tracker_FormElement_Field_Value_ArtifactLinkDao();
1948
                $artifact_link_value_dao->updateItemName($this->group_id, $old_item_name, $this->item_name);
1949
            }
1950
1951
            $dao = new TrackerDao();
1952
            if ($dao->save($this)) {
1953
                $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_admin', 'successfully_updated'));
1954
            } else {
1955
                $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('global', 'error'));
1956
            }
1957
        }
1958
    }
1959
1960
1961
1962
    /**
1963
     * Validate the format of the item name
1964
     * @param string $itemname
0 ignored issues
show
Documentation introduced by
There is no parameter named $itemname. Did you maybe mean $item_name?

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 method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1965
     * @return boolean
1966
     */
1967
    public function itemNameIsValid($item_name) {
1968
        return eregi("^[a-zA-Z0-9_]+$",$item_name);
1969
    }
1970
1971
    /**
1972
     * Test if the tracker is active
1973
     * @return boolean
1974
     */
1975
    public function isActive() {
1976
        return !$this->isDeleted();
1977
    }
1978
1979
    /**
1980
     * Test if tracker is deleted
1981
     *
1982
     * @return Boolean
1983
     */
1984
    public function isDeleted() {
1985
        return ($this->deletion_date != '');
1986
    }
1987
1988
    /**
1989
     * @return Tracker_SemanticManager
1990
     */
1991
    public function getTrackerSemanticManager() {
1992
        return new Tracker_SemanticManager($this);
1993
    }
1994
1995
    /**
1996
     * @return Tracker_Tooltip
1997
     */
1998
    public function getTooltip() {
1999
        return new Tracker_Tooltip($this);
2000
    }
2001
2002
    /**
2003
     * @return Tracker_NotificationsManager
2004
     */
2005
    public function getNotificationsManager() {
2006
        return new Tracker_NotificationsManager($this);
2007
    }
2008
2009
    /**
2010
     * @return Tracker_DateReminderManager
2011
     */
2012
    public function getDateReminderManager() {
2013
        return new Tracker_DateReminderManager($this);
2014
    }
2015
2016
    /**
2017
     * @return Tracker_CannedResponseManager
2018
     */
2019
    public function getCannedResponseManager() {
2020
        return new Tracker_CannedResponseManager($this);
2021
    }
2022
2023
    /**
2024
     * @return Tracker_CannedResponseFactory
2025
     */
2026
    public function getCannedResponseFactory() {
2027
        return Tracker_CannedResponseFactory::instance();
2028
    }
2029
2030
    /**
2031
     * @return WorkflowManager
2032
     */
2033
    public function getWorkflowManager() {
2034
        return new WorkflowManager($this);
2035
    }
2036
2037
    /**
2038
     * @return Tracker_Permission_PermissionController
2039
     */
2040
    protected function getPermissionController() {
2041
        return new Tracker_Permission_PermissionController($this);
2042
    }
2043
2044
    /**
2045
     * @return Tracker_RulesManager
2046
     */
2047
    private function getGlobalRulesManager() {
2048
        return $this->getWorkflowFactory()->getGlobalRulesManager($this);
2049
    }
2050
2051
    /**
2052
     * @return TrackerPluginConfig
2053
     */
2054
    private function getTrackerPluginConfig() {
2055
        return new TrackerPluginConfig(
2056
            new TrackerPluginConfigDao()
2057
        );
2058
    }
2059
2060
    /**
2061
     * @return Tracker_ArtifactByEmailStatus
2062
     */
2063
    private function getArtifactByMailStatus() {
2064
        return new Tracker_ArtifactByEmailStatus($this->getTrackerPluginConfig());
2065
    }
2066
2067
    /**
2068
     * @return string
2069
     */
2070
    public function displayRulesAsJavascript() {
2071
        return $this->getGlobalRulesManager()->displayRulesAsJavascript();
2072
    }
2073
2074
    /**
2075
     * Determine if the user can view this tracker.
2076
     * Note that if there is no group explicitely auhtorized, access is denied (don't check default values)
2077
     *
2078
     * @param int $user if not specified, use the current user id. The params accept also User object
2079
     *
2080
     * @return boolean true if the user can view the tracker.
2081
     */
2082
    public function userCanView($user = 0) {
2083
2084
        $user_manager = $this->getUserManager();
2085
2086
        if (! $user instanceof PFUser) {
2087
            if (!$user) {
2088
                $user = $user_manager->getCurrentUser();
2089
            } else {
2090
                $user = $user_manager->getUserById((int)$user);
2091
            }
2092
        }
2093
2094
        $project_manager    = ProjectManager::instance();
2095
        $permission_checker = new Tracker_Permission_PermissionChecker($user_manager, $project_manager);
2096
2097
        return $permission_checker->userCanViewTracker($user, $this);
2098
    }
2099
2100
    protected $cache_permissions = null;
2101
2102
    /**
2103
     * get the permissions for this tracker
2104
     * E.g.
2105
     * array(
2106
     *     $ugroup_id_1 => array('PLUGIN_TRACKER_ADMIN'),
2107
     *     $ugroup_id_2 => array('PLUGIN_TRACKER_ACCESS')
2108
     * );
2109
     *
2110
     * @return array
2111
     */
2112
    public function getPermissionsByUgroupId() {
2113
        if (! $this->cache_permissions) {
2114
            $this->cache_permissions = array();
2115
            $perm_dao = new Tracker_PermDao();
2116
            if ($dar = $perm_dao->searchAccessPermissionsByTrackerId($this->getId())) {
2117
                while ($row = $dar->getRow()) {
2118
                    $this->cache_permissions[$row['ugroup_id']][] = $row['permission_type'];
2119
                }
2120
            }
2121
        }
2122
        return $this->cache_permissions;
2123
    }
2124
2125
    /**
2126
     * Set the cache permission for the ugroup_id
2127
     * Use during the two-step xml import
2128
     *
2129
     * @param int    $ugroup_id The ugroup id
2130
     * @param string $permission_type The permission type
2131
     *
2132
     * @return void
2133
     */
2134
    public function setCachePermission($ugroup_id, $permission_type) {
2135
        $this->cache_permissions[$ugroup_id][] = $permission_type;
2136
    }
2137
2138
    /**
2139
     * Empty cache permissions
2140
     *
2141
     * @return void
2142
     */
2143
    public function permissionsAreCached() {
2144
        return is_array($this->cache_permissions);
2145
    }
2146
2147
    /**
2148
     * @var array
2149
     */
2150
    private $cached_permission_authorized_ugroups;
2151
2152
    /**
2153
     * Retreives the permissions set on a given tracker
2154
     * E.g.
2155
     * array(
2156
     *     Tracker::PERMISSION_ADMIN => array($ugroup_id_1, $ugroup_id_2)
2157
     *     Tracker::PERMISSION_NONE  => array($ugroup_id_1, $ugroup_id_2)
2158
     * );
2159
     *
2160
     * @return array
2161
     */
2162
    public function getAuthorizedUgroupsByPermissionType() {
2163
        if (! $this->cached_permission_authorized_ugroups || empty($this->cached_permission_authorized_ugroups)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cached_permission_authorized_ugroups of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2164
2165
            $this->cached_permission_authorized_ugroups = array();
2166
            $perm_dao = new Tracker_PermDao();
2167
2168
            if ($dar = $perm_dao->searchAccessPermissionsByTrackerId($this->getId())) {
2169
                while ($row = $dar->getRow()) {
2170
                    $this->cached_permission_authorized_ugroups[$row['permission_type']][] = $row['ugroup_id'];
2171
                }
2172
            }
2173
        }
2174
        return $this->cached_permission_authorized_ugroups;
2175
    }
2176
2177
    /**
2178
     * See if the user's perms are >= 2 or project admin.
2179
     *
2180
     * @param int $user Either the user ID or the User object to test, or current user if false
2181
     *
2182
     * @return boolean True if the user is tracker admin, false otherwise
2183
     */
2184
    function userIsAdmin($user = false) {
2185
        if (! $user instanceof PFUser) {
2186
            $user_manager = UserManager::instance();
2187
            if (! $user) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2188
                $user = $user_manager->getCurrentUser();
2189
            } else {
2190
                $user = $user_manager->getUserById((int) $user);
2191
            }
2192
        }
2193
2194
        if ($user->isSuperUser() || $user->isMember($this->getGroupId(), 'A')) {
2195
            return true;
2196
        }
2197
2198
        if ($this->getTrackerManager()->userCanAdminAllProjectTrackers($user)) {
2199
            return true;
2200
        }
2201
2202
        $permissions = $this->getPermissionsByUgroupId();
2203
2204
        foreach ($permissions as $ugroup_id => $permission_types) {
2205
2206
            foreach ( $permission_types as $permission_type ) {
2207
2208
                if($permission_type == self::PERMISSION_ADMIN) {
2209
2210
                    if ($user->isMemberOfUGroup($ugroup_id, $this->getGroupId())) {
2211
                        return true;
2212
                    }
2213
                }
2214
            }
2215
        }
2216
        return false;
2217
    }
2218
2219
    /**
2220
     * @return TrackerManager
2221
     */
2222
    protected function getTrackerManager() {
2223
        return new TrackerManager();
2224
    }
2225
2226
2227
    /**
2228
     * Check if user has permission to submit artifact or not
2229
     *
2230
     * @param PFUser $user The user to test (current user if not defined)
2231
     *
2232
     * @return boolean true if user has persission to submit artifacts, false otherwise
2233
     */
2234
    function userCanSubmitArtifact($user = false) {
2235
        if (! $user instanceof PFUser) {
2236
            $um = UserManager::instance();
2237
            $user = $um->getCurrentUser();
2238
        }
2239
2240
        if ($user->isAnonymous() || ! $this->userCanView($user)) {
2241
            return false;
2242
        }
2243
2244
        $can_submit = false;
2245
        foreach($this->getFormElementFactory()->getUsedFields($this) as $form_element) {
2246
            if ($form_element->userCanSubmit($user)) {
2247
                $can_submit = true;
2248
            }
2249
        }
2250
2251
        return $can_submit;
2252
    }
2253
2254
    /**
2255
     * Check if user has permission to delete a tracker or not
2256
     *
2257
     * @param PFUser $user The user to test (current user if not defined)
2258
     *
2259
     * @return boolean true if user has persission to delete trackers, false otherwise
2260
     */
2261
    function userCanDeleteTracker($user = false) {
2262
        if (!($user instanceof PFUser)) {
2263
            $um = UserManager::instance();
2264
            $user = $um->getCurrentUser();
2265
        }
2266
        return $user->isSuperUser() || $user->isMember($this->getGroupId(), 'A');
2267
    }
2268
2269
    public function getInformationsFromOtherServicesAboutUsage() {
2270
        $result                   = array();
2271
        $result['can_be_deleted'] = true;
2272
2273
        EventManager::instance()->processEvent(
2274
            TRACKER_USAGE,
2275
            array(
2276
                'tracker'   => $this,
2277
                'result'    => &$result
2278
            )
2279
        );
2280
2281
        return $result;
2282
    }
2283
2284
    /**
2285
     * Check if user has full access to a tracker or not
2286
     *
2287
     * @param PFUser $user The user to test (current user if not defined)
2288
     *
2289
     * @return boolean true if user has full access to tracker, false otherwise
2290
     */
2291
    function userHasFullAccess($user = false) {
2292
        if (!($user instanceof PFUser)) {
2293
            $um = UserManager::instance();
2294
            $user = $um->getCurrentUser();
2295
        }
2296
        if ($user->isSuperUser() || $user->isMember($this->getGroupId(), 'A')) {
2297
            return true;
2298
        }  else {
2299
            $permissions = $this->getPermissionsByUgroupId();
2300
            foreach ($permissions as $ugroup_id => $permission_types) {
2301
                foreach ( $permission_types as $permission_type ) {
2302
                    if($permission_type == self::PERMISSION_FULL || $permission_type == self::PERMISSION_ADMIN) {
2303
                        if ($user->isMemberOfUGroup($ugroup_id, $this->getGroupId())) {
2304
                                return true;
2305
                        }
2306
                    }
2307
                }
2308
            }
2309
        }
2310
        return false;
2311
    }
2312
2313
    public function exportToXML(SimpleXMLElement $xmlElem, array &$xmlMapping = array()) {
2314
        $user_xml_exporter = $this->getUserXMLExporter();
2315
2316
        return $this->exportTrackerToXML($xmlElem, $user_xml_exporter, $xmlMapping, false);
2317
    }
2318
2319
    public function exportToXMLInProjectExportContext(
2320
        SimpleXMLElement $xmlElem,
2321
        UserXMLExporter $user_xml_exporter,
2322
        array &$xmlMapping = array()
2323
    ) {
2324
        return $this->exportTrackerToXML($xmlElem, $user_xml_exporter, $xmlMapping, true);
2325
    }
2326
2327
    /**
2328
     * Exports the tracker to an XML file.
2329
     *
2330
     * @return SimpleXMLElement
2331
     */
2332
    private function exportTrackerToXML(
2333
        SimpleXMLElement $xmlElem,
2334
        UserXMLExporter $user_xml_exporter,
2335
        array &$xmlMapping,
2336
        $project_export_context
2337
    ) {
2338
        $xmlElem->addAttribute('id', "T". $this->getId());
2339
2340
        $cdata_section_factory = new XML_SimpleXMLCDATAFactory();
2341
2342
        $parent_id = $this->getParentId();
2343
        if ($parent_id && ! $project_export_context) {
2344
            $parent_id = "T". $parent_id;
2345
        } else {
2346
            $parent_id = "0";
2347
        }
2348
2349
        $xmlElem->addAttribute('parent_id', (string)$parent_id);
2350
2351
        // only add attributes which are different from the default value
2352
        if ($this->enable_emailgateway) {
2353
            $xmlElem->addAttribute('enable_emailgateway', $this->enable_emailgateway);
2354
        }
2355
        if ($this->allow_copy) {
2356
            $xmlElem->addAttribute('allow_copy', $this->allow_copy);
2357
        }
2358
        if ($this->instantiate_for_new_projects) {
2359
            $xmlElem->addAttribute('instantiate_for_new_projects', $this->instantiate_for_new_projects);
2360
        }
2361
        if ($this->log_priority_changes) {
2362
            $xmlElem->addAttribute('log_priority_changes', $this->log_priority_changes);
2363
        }
2364
        if ($this->stop_notification) {
2365
            $xmlElem->addAttribute('stop_notification', $this->stop_notification);
2366
        }
2367
2368
        // these will not be used at the import
2369
        $cdata_section_factory->insert($xmlElem, 'name', $this->getName());
2370
        $xmlElem->addChild('item_name', $this->getItemName());
2371
        $cdata_section_factory->insert($xmlElem, 'description', $this->getDescription());
2372
        $xmlElem->addChild('color', $this->getColor());
2373
2374
        // add only if not empty
2375
        if ($this->submit_instructions) {
2376
            $cdata_section_factory->insert($xmlElem, 'submit_instructions', $this->submit_instructions);
2377
        }
2378
        if ($this->browse_instructions) {
2379
            $cdata_section_factory->insert($xmlElem, 'browse_instructions', $this->browse_instructions);
2380
        }
2381
2382
        $child = $xmlElem->addChild('cannedResponses');
2383
        if ($responses = $this->getCannedResponseFactory()->getCannedResponses($this)) {
2384
            foreach ($responses as $response) {
2385
                $grandchild = $child->addChild('cannedResponse');
2386
                $response->exportToXML($grandchild);
2387
            }
2388
        }
2389
2390
        $child = $xmlElem->addChild('formElements');
2391
        // association between ids in database and ids in xml
2392
2393
2394
        foreach ($this->getFormElementFactory()->getUsedFormElementForTracker($this) as $formElement) {
0 ignored issues
show
Bug introduced by
The expression $this->getFormElementFac...lementForTracker($this) of type object<Tracker_FormElement> is not traversable.
Loading history...
2395
            $grandchild = $child->addChild('formElement');
2396
            $formElement->exportToXML($grandchild, $xmlMapping, $project_export_context, $user_xml_exporter);
2397
        }
2398
2399
        // semantic
2400
        $tsm = $this->getTrackerSemanticManager();
2401
        $child = $xmlElem->addChild('semantics');
2402
        $tsm->exportToXML($child, $xmlMapping);
2403
2404
        // rules
2405
        $child = $xmlElem->addChild('rules');
2406
        $this->getGlobalRulesManager()->exportToXML($child, $xmlMapping);
2407
2408
        // only the reports with project scope are exported
2409
        $reports = $this->getReportFactory()->getReportsByTrackerId($this->id, null);
2410
        if ($reports) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $reports of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2411
            $child = $xmlElem->addChild('reports');
2412
            foreach ($this->getReportFactory()->getReportsByTrackerId($this->id, null) as $report) {
2413
                $report->exportToXML($child, $xmlMapping);
2414
            }
2415
        }
2416
2417
        // workflow
2418
        $child = $xmlElem->addChild('workflow');
2419
        $workflow = $this->getWorkflowFactory()->getWorkflowByTrackerId($this->id);
2420
        if(!empty($workflow)) {
2421
            $workflow->exportToXML($child, $xmlMapping);
2422
        }
2423
2424
        // permissions
2425
        $node_perms = $xmlElem->addChild('permissions');
2426
        // tracker permissions
2427
        if ($permissions = $this->getPermissionsByUgroupId()) {
2428
            foreach ($permissions as $ugroup_id => $permission_types) {
2429
                if (($ugroup = array_search($ugroup_id, $GLOBALS['UGROUPS'])) !== false && $ugroup_id < 100) {
2430
                    foreach ( $permission_types as $permission_type) {
2431
                        $node_perm = $node_perms->addChild('permission');
2432
                        $node_perm->addAttribute('scope', 'tracker');
2433
                        $node_perm->addAttribute('ugroup', $ugroup);
2434
                        $node_perm->addAttribute('type', $permission_type);
2435
                        unset($node_perm);
2436
                    }
2437
                }
2438
            }
2439
        }
2440
        // fields permission
2441
        if ($formelements = $this->getFormElementFactory()->getUsedFormElementForTracker($this)) {
2442
            foreach ($formelements as $formelement) {
0 ignored issues
show
Bug introduced by
The expression $formelements of type object<Tracker_FormElement> is not traversable.
Loading history...
2443
                $formelement->exportPermissionsToXML($node_perms, $xmlMapping);
2444
            }
2445
        }
2446
2447
        return $xmlElem;
2448
    }
2449
2450
    /**
2451
     * Send the xml to the client
2452
     *
2453
     * @param SimpleXMLElement $xmlElem The xml
2454
     */
2455
    protected function sendXML(SimpleXMLElement $xmlElem) {
2456
        $dom = dom_import_simplexml($xmlElem)->ownerDocument;
2457
        $dom->formatOutput = true;
2458
2459
        $output_filename = 'Tracker_'.$this->item_name.'.xml';
2460
        $xml             = $dom->saveXML();
2461
2462
        $GLOBALS['Response']->sendXMLAttachementFile($xml, $output_filename);
2463
    }
2464
2465
    /**
2466
     * Returns the array of fields corresponding with the CSV header field_names $header
2467
     * Returns false if there is an unknown field
2468
     * field_name 'aid' is kept like this
2469
     *
2470
     * @param array $header the array of field names (string), in the same order than the CSV file
2471
     *
2472
     * @return array of Tracker_FormElementField the fields, in the same order than the header, or false if there is an unknown field
2473
     */
2474
    private function _getCSVFields($header) {
2475
        $fef = $this->getFormElementFactory();
2476
        $fields = array();
2477
        foreach($header as $field_name) {
2478
            if ($field_name != 'aid') {
2479
                $field = $fef->getUsedFieldByName($this->getId(), $field_name);
2480
                if ($field) {
2481
                    $fields[] = $field;
2482
                } else {
2483
                    return false;
2484
                }
2485
            } else {
2486
                $fields[] = 'aid';
2487
            }
2488
        }
2489
        return $fields;
2490
    }
2491
2492
    private function _getCSVSeparator($current_user) {
2493
        if ( ! $current_user || ! ($current_user instanceof PFUser)) {
2494
            $current_user = UserManager::instance()->getCurrentUser();
2495
        }
2496
2497
        $separator = ",";   // by default, comma.
2498
        $separator_csv_export_pref = $current_user->getPreference('user_csv_separator');
2499
        switch ($separator_csv_export_pref) {
2500
        case "comma":
2501
            $separator = ',';
2502
            break;
2503
        case "semicolon":
2504
            $separator = ';';
2505
            break;
2506
        case "tab":
2507
            $separator = chr(9);
2508
            break;
2509
        }
2510
        return $separator;
2511
    }
2512
2513
    private function _getCSVDateformat($current_user) {
2514
        if ( ! $current_user || ! ($current_user instanceof PFUser)) {
2515
            $current_user = UserManager::instance()->getCurrentUser();
2516
        }
2517
        $dateformat_csv_export_pref = $current_user->getPreference('user_csv_dateformat');
2518
        if ($dateformat_csv_export_pref === false) {
2519
            $dateformat_csv_export_pref = "month_day_year"; // by default, mm/dd/yyyy
2520
        }
2521
        return $dateformat_csv_export_pref;
2522
    }
2523
2524
    protected function displayImportPreview(Tracker_IDisplayTrackerLayout $layout, $request, $current_user, $session) {
2525
        $purifier = Codendi_HTMLPurifier::instance();
2526
2527
        if ($_FILES['csv_filename']) {
2528
            $f = fopen($_FILES['csv_filename']['tmp_name'], 'r');
2529
            if ($f) {
2530
                // get the csv separator (defined in user preferences)
2531
                $separator = $this->_getCSVSeparator($current_user);
2532
2533
                $is_valid = true;
2534
                $i = 0;
2535
                $lines = array();
2536
                while ($line = fgetcsv($f, 0, $separator)) {
2537
                    if ($line === false) {
2538
                        $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'error_in_csv_file', array($i)));
2539
                        $is_valid = false;
2540
                    } else {
2541
                        $lines[] = $line;
2542
                    }
2543
                    $i++;
2544
                }
2545
                fclose($f);
2546
2547
                if ($is_valid) {
2548
                    if (count($lines) >= 2) {
2549
                        $is_valid = $this->isValidCSV($lines, $separator);
2550
2551
                        //header
2552
                        $items = $this->getAdminItems();
2553
                        $title = $items['csvimport']['title'];
2554
                        $this->displayAdminCSVImportHeader($layout, $title, array());
2555
2556
                        echo '<h2>'. $title .'</h2>';
2557
2558
                        //body
2559
                        if (count($lines) > 1) {
2560
                            $html_table = '';
2561
                            $html_table .= '<table class="table table-bordered table-striped csv-import-preview">';
2562
                            $html_table .=  '<thead>';
2563
                            $header = array_shift($lines);
2564
                            $html_table .=  '<tr class="boxtable">';
2565
                            $html_table .=  '<th class="boxtitle"></th>';
2566
                            $fields = $this->_getCSVFields($header);
2567
2568
                            foreach ($header as $field_name) {
2569
                                $html_table .=  '<th class="boxtitle tracker_report_table_column">';
2570
                                $html_table .=  $purifier->purify($field_name);
2571
                                $html_table .=  '</th>';
2572
                            }
2573
                            $html_table .=  '</tr>';
2574
                            $html_table .=  '</thead>';
2575
                            $html_table .=  '<tbody>';
2576
                            $nb_lines = 0;
2577
                            $nb_artifact_creation = 0;
2578
                            $nb_artifact_update = 0;
2579
                            foreach ($lines as $line_number => $data_line) {
2580
                                if ($nb_lines % 2 == 0) {
2581
                                    $tr_class = 'boxitem';
2582
                                } else {
2583
                                    $tr_class = 'boxitemalt';
2584
                                }
2585
                                $html_table .= '<tr class="'. $tr_class .'">';
2586
                                $html_table .= '<td style="color:gray;">'. ($line_number + 1) .'</td>';
2587
                                $mode = 'creation';
2588
                                foreach ($data_line as $idx => $data_cell) {
2589
                                    if ($fields[$idx] && is_a($fields[$idx], 'Tracker_FormElement_Field')) {
2590
                                        $field  = $fields[$idx];
2591
                                        $displayed_data = $field->getFieldDataForCSVPreview($data_cell);
2592
                                    } else  {
2593
                                        // else: this cell is an 'aid' cell
2594
                                        if ($data_cell) {
2595
                                            $mode = 'update';
2596
                                        }
2597
                                        $displayed_data = $purifier->purify($data_cell);
2598
                                    }
2599
                                    $html_table .=  '<td class="tracker_report_table_column">' . $displayed_data .'</td>';
2600
                                }
2601
                                $html_table .=  '</tr>';
2602
                                $nb_lines++;
2603
                                if ($mode == 'creation') {
2604
                                    $nb_artifact_creation++;
2605
                                } else {
2606
                                    $nb_artifact_update++;
2607
                                }
2608
                            }
2609
                            $html_table .=  '</tbody>';
2610
                            $html_table .=  '</table>';
2611
2612
                            echo '<p>';
2613
                            echo $GLOBALS['Language']->getText('plugin_tracker_import', 'check_data') . '<br />';
2614
                            echo $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'date_format_help', array($GLOBALS['Language']->getText('account_preferences', $this->_getCSVDateformat($current_user))));
2615
                            echo '</p>';
2616
2617
                            if ($is_valid) {
2618
                                echo '<form name="form1" method="POST" enctype="multipart/form-data" action="'.TRACKER_BASE_URL.'/?tracker='. (int)$this->id .'&amp;func=admin-csvimport">';
2619
                                echo '<p>' . $GLOBALS['Language']->getText('plugin_tracker_import','ready', array($nb_lines, $nb_artifact_creation, $nb_artifact_update)) . '</p>';
2620
                                echo '<input type="hidden" name="action" value="import">';
2621
                                if ($request->exist('notify') && $request->get('notify') == 'ok') {
2622
                                    echo '<input type="hidden" name="notify" value="ok">';
2623
                                }
2624
                                echo '<input type="submit" class="csv-preview-import-button" value="'.$GLOBALS['Language']->getText('plugin_tracker_import','import_new_hdr').'">';
2625
                            }
2626
                            echo $html_table;
2627
                            if ($is_valid) {
2628
                                echo '</form>';
2629
2630
                                $session->set('csv_header', $header);
2631
                                $session->set('csv_body', $lines);
2632
2633
                            }
2634
                        }
2635
2636
                        //footer
2637
                        $this->displayFooter($layout);
2638
                        exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method displayImportPreview() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2639
                    } else {
2640
                        $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'no_data'));
2641
                    }
2642
                }
2643
            } else {
2644
                $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'unable_to_open_file', array($_FILES['csv_filename']['tmp_name'])));
2645
            }
2646
        } else {
2647
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'file_not_found'));
2648
            $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. (int)$this->getId() .'&func=admin-csvimport');
2649
        }
2650
    }
2651
2652
    public function displayWarningArtifactByEmailSemantic() {
2653
        $artifactbyemail_status = $this->getArtifactByMailStatus();
2654
2655
        if (! $artifactbyemail_status->isSemanticConfigured($this)) {
2656
            $GLOBALS['Response']->addFeedback(
2657
                'warning',
2658
                $GLOBALS['Language']->getText('plugin_tracker_emailgateway','semantic_missing')
2659
            );
2660
        }
2661
    }
2662
2663
    public function displayWarningArtifactByEmailRequiredFields() {
2664
        $artifactbyemail_status = $this->getArtifactByMailStatus();
2665
2666
        if (! $artifactbyemail_status->isRequiredFieldsConfigured($this)) {
2667
            $GLOBALS['Response']->addFeedback(
2668
                'warning',
2669
                $GLOBALS['Language']->getText('plugin_tracker_emailgateway','invalid_required_fields')
2670
            );
2671
        }
2672
    }
2673
2674
    public function displayWarningGeneralsettings() {
2675
        $artifactbyemail_status = $this->getArtifactByMailStatus();
2676
2677
        if (! $artifactbyemail_status->isRequiredFieldsConfigured($this)
2678
            || ! $artifactbyemail_status->isSemanticConfigured($this)
2679
        ) {
2680
            $GLOBALS['Response']->addFeedback(
2681
                'warning',
2682
                $GLOBALS['Language']->getText('plugin_tracker_emailgateway','invalid_configuration')
2683
            );
2684
        }
2685
    }
2686
2687
    /**
2688
     * Validation of CSV file datas in this tracker
2689
     *
2690
     * @return bool, true if CSV file is valid, false otherwise
0 ignored issues
show
Documentation introduced by
The doc-type bool, could not be parsed: Expected "|" or "end of type", but got "," at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
2691
     */
2692
    public function isValidCSV($lines, $separator) {
2693
        $is_valid = true;
2694
        $header_line = array_shift($lines);
2695
2696
        if (count($header_line) == 1) {
2697
            // not sure it is an error, so don't set is_valid to false.
2698
            $GLOBALS['Response']->addFeedback('warning', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'separator_not_found', array($separator)), CODENDI_PURIFIER_FULL);
2699
        }
2700
2701
        if ($this->hasError($header_line, $lines)) {
2702
            $is_valid = false;
2703
        }
2704
        return $is_valid;
2705
    }
2706
2707
    /**
2708
     * Check if CSV file contains unknown aid
2709
     *
2710
     * @param Array $header_line, the CSV file header line
0 ignored issues
show
Documentation introduced by
There is no parameter named $header_line,. Did you maybe mean $header_line?

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 method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
2711
     * @param Array $lines, the CSV file lines
0 ignored issues
show
Documentation introduced by
There is no parameter named $lines,. Did you maybe mean $lines?

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 method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
2712
     *
2713
     * @return bool true if has unknown fields, false otherwise
2714
     */
2715
    public function hasUnknownAid($header_line, $lines) {
2716
        $has_unknown = false;
2717
        $aid_key = array_search('aid', $header_line);
2718
        //Update mode
2719
        if ($aid_key !== false) {
2720
            foreach ($lines as $line) {
2721
                if($line[$aid_key] != '') {
2722
                    if (!$this->aidExists($line[$aid_key])) {
2723
                        $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'aid_does_not_exist', array($line[$aid_key])));
2724
                        $has_unknown = true;
2725
                    }
2726
                }
2727
            }
2728
        }
2729
        return $has_unknown;
2730
    }
2731
2732
    /**
2733
     * Check if CSV file contains unknown fields
2734
     *
2735
     * @param Array $lines, the CSV file lines
0 ignored issues
show
Documentation introduced by
There is no parameter named $lines,. Did you maybe mean $lines?

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 method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
2736
     *
2737
     * @return bool true if has unknown fields, false otherwise
2738
     */
2739
    public function hasError($header_line, $lines) {
2740
        $has_error = false;
2741
        $fef = $this->getFormElementFactory();
2742
        $aid_key = array_search('aid', $header_line);
2743
        $af = $this->getTrackerArtifactFactory();
2744
        $artifact = null;
2745
        $hp       = Codendi_HTMLPurifier::instance();
2746
2747
        foreach ($lines as $cpt_line => $line) {
2748
            $data = array();
2749
            foreach ($header_line as $idx => $field_name) {
2750
                //Fields other than aid
2751
                if ($field_name != 'aid') {
2752
                    $field = $fef->getUsedFieldByName($this->getId(), $field_name);
2753
                    if (! $field) {
2754
                        // a field is unknown
2755
                        $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'unknown_field', array($field_name)));
2756
                        $has_error = true;
2757
                    } else {
2758
                        //check if value is ok
2759
                        if ($aid_key !== false && $this->aidExists($line[$aid_key])) {
2760
                            $artifact_id = $line[$aid_key];
2761
                        } else {
2762
                            $artifact_id = 0;
2763
                        }
2764
2765
                        $artifact = $af->getInstanceFromRow(
2766
                                        array(
2767
                                            'id'                       => $artifact_id,
2768
                                            'tracker_id'               => $this->id,
2769
                                            'submitted_by'             => $this->getUserManager()->getCurrentuser()->getId(),
2770
                                            'submitted_on'             => $_SERVER['REQUEST_TIME'],
2771
                                            'use_artifact_permissions' => 0,
2772
                                        )
2773
                                  );
2774
                        if ($line[$idx]!=''){
2775
2776
                            $data[$field->getId()] = $field->getFieldDataFromCSVValue($line[$idx]);
2777
2778
                            if ($data[$field->getId()] === null) {
2779
                                $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'unknown_value', array($line[$idx], $field_name)));
2780
                                $has_error = true;
2781
                            }
2782
                        }
2783
                    }
2784
                } else {
2785
                    //Field is aid : we check if the artifact id exists
2786
                    if ($this->hasUnknownAid($header_line, $lines)) {
2787
                        $has_error = true;
2788
                    }
2789
                }
2790
            }
2791
            if ($artifact) {
2792
                $is_new_artifact = $artifact->getId() == 0;
2793
                if ($is_new_artifact) {
2794
                    $fields_validator = new Tracker_Artifact_Changeset_InitialChangesetFieldsValidator($this->getFormElementFactory());
2795
                } else {
2796
                    $fields_validator = new Tracker_Artifact_Changeset_NewChangesetFieldsValidator($this->getFormElementFactory());
2797
                }
2798
                if (! $fields_validator->validate($artifact, $data)) {
2799
                     $has_error = true;
2800
                }
2801
            }
2802
        }
2803
        return $has_error;
2804
    }
2805
2806
2807
    /**
2808
     * Check if CSV contains all the required fields and values associated
2809
     *
2810
     * @param Array $lines, the CSV file lines
0 ignored issues
show
Documentation introduced by
There is no parameter named $lines,. Did you maybe mean $lines?

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 method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
2811
     *
2812
     * @return bool true if missing required fields, false otherwise
2813
     */
2814
    public function isMissingRequiredFields($header_line, $lines) {
2815
        $is_missing = false;
2816
        $fields = array();
2817
        $fef = $this->getFormElementFactory();
2818
        $fields = $fef->getUsedFields($this);
2819
        foreach ($fields as $field) {
2820
            if($field->isRequired()) {
2821
                $key = array_search($field->getName(), $header_line);
2822
                if ($key === false) {
2823
                    //search if field  is in the CSV file header line
2824
                    $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'missing_required_field', array($field->getName())));
2825
                    $is_missing = true;
2826
                } else {
2827
                    //search if there is a value at each line for that field
2828
                    foreach ($lines as $line) {
2829
                        if (! isset($line[$key]) || $line[$key] == '') {
2830
                            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'missing_required_field_value', array($field->getName())));
2831
                            $is_missing = true;
2832
                        }
2833
                    }
2834
                }
2835
            }
2836
        }
2837
        return $is_missing;
2838
    }
2839
2840
    /**
2841
     * Check if aid exists in update mode in CSV import
2842
     *
2843
     * @param Int $aid, the artifact id
0 ignored issues
show
Bug introduced by
There is no parameter named $aid,. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2844
     *
2845
     * @return String $error_message
2846
     */
2847
    protected function aidExists($aid) {
2848
        $af = $this->getTrackerArtifactFactory();
2849
        $artifact = $af->getArtifactById($aid);
2850
        if ($artifact) {
2851
            if ($artifact->getTrackerId() == $this->getId()) {
2852
                return true;
2853
            } else {
2854
                return false;
2855
            }
2856
        }
2857
        return false;
2858
    }
2859
2860
2861
    /**
2862
     * Import artifacts from CSV file ($_FILES['csv_filename']['tmp_name'])  in this tracker
2863
     *
2864
     * @return boolean true if import succeed, false otherwise
2865
     */
2866
    protected function importFromCSV(Tracker_IDisplayTrackerLayout $layout, $request, $current_user, $header, $lines) {
2867
        $is_error = false;
2868
        if (count($lines) >= 1) {
2869
            if ($request->exist('notify') && $request->get('notify') == 'ok') {
2870
                $send_notifications = true;
2871
            } else {
2872
                $send_notifications = false;
2873
            }
2874
            $fields = $this->_getCSVFields($header);
2875
            $af = Tracker_ArtifactFactory::instance();
2876
            $nb_lines = 0;
2877
            $nb_artifact_creation = 0;
2878
            $nb_artifact_update = 0;
2879
            foreach ($lines as $line_number => $data_line) {
2880
                $mode = 'creation';
2881
                $fields_data = array();
2882
                foreach ($data_line as $idx => $data_cell) {
2883
                    if ($fields[$idx] && is_a($fields[$idx], 'Tracker_FormElement_Field')) {
2884
                        $field = $fields[$idx];
2885
                        $fields_data[$field->getId()] = $field->getFieldDataFromCSVValue($data_cell);
2886
                    } else  {
2887
                        // else: this cell is an 'aid' cell
2888
                        if ($data_cell) {
2889
                            $mode = 'update';
2890
                            $artifact_id = (int) $data_cell;
2891
                        } else {
2892
                            $artifact_id = 0;
2893
                        }
2894
                    }
2895
                }
2896
                $nb_lines++;
2897
                if ($mode == 'creation') {
2898
                    if ($artifact = $af->createArtifact($this, $fields_data, $current_user, null, $send_notifications)) {
2899
                        $nb_artifact_creation++;
2900
                    } else {
2901
                        $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'unable_to_create_artifact'));
2902
                        $is_error = true;
2903
                    }
2904
                } else {
2905
                    // $idx is the artifact id
2906
                    $artifact = $af->getArtifactById($artifact_id);
2907
                    if ($artifact) {
2908
                        $followup_comment = '';
2909
                        try {
2910
                            $artifact->createNewChangeset($fields_data, $followup_comment, $current_user, $send_notifications);
2911
                            $nb_artifact_update++;
2912
                        } catch (Tracker_NoChangeException $e) {
2913
                            $GLOBALS['Response']->addFeedback('info', $e->getMessage(), CODENDI_PURIFIER_LIGHT);
2914
                            $is_error = true;
2915
                        } catch (Tracker_Exception $e) {
2916
                            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'unable_to_update_artifact', array($artifact_id)));
2917
                            $GLOBALS['Response']->addFeedback('error', $e->getMessage());
2918
                            $is_error = true;
2919
                        }
2920
                    } else {
2921
                        $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'unknown_artifact', array($artifact_id)));
2922
                        $is_error = true;
2923
                    }
2924
                }
2925
            }
2926
            if ( ! $is_error) {
2927
                if ($nb_artifact_creation > 0) {
2928
                    $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'nb_created_import', array($nb_artifact_creation)));
2929
                }
2930
                if ($nb_artifact_update > 0) {
2931
                    $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'nb_updated_import', array($nb_artifact_update)));
2932
                }
2933
            }
2934
        } else {
2935
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin_import', 'no_data'));
2936
            $is_error = true;
2937
        }
2938
        return  ! $is_error;
2939
    }
2940
2941
     /**
2942
     * Get UserManager instance
2943
     *
2944
     * @return Tracker_ArtifactFactory
2945
     */
2946
    protected function getUserManager() {
2947
        return UserManager::instance();
2948
    }
2949
2950
    /**
2951
     * Get Tracker_ArtifactFactory instance
2952
     *
2953
     * @return Tracker_ArtifactFactory
2954
     */
2955
    protected function getTrackerArtifactFactory() {
2956
        return Tracker_ArtifactFactory::instance();
2957
    }
2958
2959
    /**
2960
     * Get FormElementFactory instance
2961
     *
2962
     * @return Tracker_FormElementFactory
2963
     */
2964
    protected function getFormElementFactory() {
2965
        return $this->formElementFactory;
2966
    }
2967
2968
    /**
2969
     * Get WorkflowFactory instance
2970
     *
2971
     * @return WorkflowFactory
2972
     */
2973
    protected function getWorkflowFactory() {
2974
        return WorkflowFactory::instance();
2975
    }
2976
2977
    /**
2978
     * Get ReportFactory instance
2979
     *
2980
     * @return Tracker_ReportFactory
2981
     */
2982
    protected function getReportFactory() {
2983
        return Tracker_ReportFactory::instance();
2984
    }
2985
2986
    /**
2987
     * Verifies the consistency of the imported Tracker
2988
     *
2989
     * @return true if Tracker is ok
2990
     */
2991
    public function testImport() {
2992
        foreach ($this->formElements as $form) {
2993
            if (!$form->testImport()) {
2994
                return false;
2995
            }
2996
        }
2997
        return true;
2998
    }
2999
3000
    /**
3001
     * Ask to fields to augment the fields_data
3002
     *
3003
     * @param array &$fields_data The user submitted data
3004
     *
3005
     * @return void
3006
     */
3007
    public function augmentDataFromRequest(&$fields_data) {
3008
        foreach(Tracker_FormElementFactory::instance()->getUsedFields($this) as $field) {
3009
            $field->augmentDataFromRequest($fields_data);
3010
        }
3011
    }
3012
3013
    /**
3014
     * Get a recipients list for (global) notifications.
3015
     *
3016
     * @return array
3017
     */
3018
    public function getRecipients() {
3019
        $recipients = array();
3020
        $nm = new Tracker_NotificationsManager($this);
3021
        $notifs = $nm->getGlobalNotifications();
3022
        foreach ( $notifs as $id=>$notif ) {
3023
            $recipients[$id] = array( 'recipients'=>$notif->getAddresses(true), 'on_updates'=> $notif->isAllUpdates(), 'check_permissions'=> $notif->isCheckPermissions()  );
3024
        }
3025
        return $recipients;
3026
    }
3027
3028
    protected $cache_stats;
3029
3030
    /**
3031
     * get stats for this tracker
3032
     *
3033
     * @return array
3034
     */
3035
    public function getStats() {
3036
        if (!isset($this->cache_stats)) {
3037
            $dao = new Tracker_ArtifactDao();
3038
            $this->cache_stats = $dao->searchStatsForTracker($this->id)->getRow();
3039
        }
3040
        return $this->cache_stats;
3041
    }
3042
3043
    /**
3044
     * Fetch some statistics about this tracker to display on trackers home page
3045
     *
3046
     * @return string html
3047
     */
3048
    public function fetchStats() {
3049
        $html = '';
3050
        if ($row = $this->getStats()) {
3051
            $html .= '<div class="tracker_statistics" style="font-size:0.9em; color:#666;">';
3052
            $html .= '<div style="text-align:right;font-size:0.825em;">#'. $this->id .'</div>';
3053
            if ($row['nb_total'] && $this->hasSemanticsStatus()) {
3054
                $html .= $GLOBALS['Language']->getText('plugin_tracker_stat','number_open_artifacts').' '. $row['nb_open'] .'<br />';
3055
            }
3056
3057
            $html .= $GLOBALS['Language']->getText('plugin_tracker_stat','total_number_artifacts'). ' '.$row['nb_total'] .'<br />';
3058
            if ($row['last_creation'] && $row['last_update']) {
3059
3060
                $html .= $GLOBALS['Language']->getText('plugin_tracker_stat','recent_activity');
3061
                $html .= '<ul>';
3062
                if ($row['last_update']) {
3063
                    $html .= '<li>'. $GLOBALS['Language']->getText('plugin_tracker_stat','last_update').' ';
3064
                    $html .= DateHelper::timeAgoInWords($row['last_update'], true, true);
3065
                    $html .= '</li>';
3066
                }
3067
                if ($row['last_creation']) {
3068
                    $html .= '<li>'. $GLOBALS['Language']->getText('plugin_tracker_stat','last_artifact_created').' ';
3069
                    $html .= DateHelper::timeAgoInWords($row['last_creation'], true, true);
3070
                    $html .= '</li>';
3071
                }
3072
                $html .= '</ul>';
3073
            }
3074
            $html .= '</div>';
3075
        }
3076
        return $html;
3077
    }
3078
3079
    /**
3080
     * Say if the tracker as "title" defined
3081
     *
3082
     * @return bool
3083
     */
3084
    public function hasSemanticsTitle() {
3085
        return Tracker_Semantic_Title::load($this)->getFieldId() ? true : false;
3086
    }
3087
3088
    /**
3089
     * Return the title field, or null if no title field defined
3090
     *
3091
     * @return Tracker_FormElement_Field_Text the title field, or null if not defined
3092
     */
3093
    public function getTitleField() {
3094
        $title_field = Tracker_Semantic_Title::load($this)->getField();
3095
        if ($title_field) {
3096
            return $title_field;
3097
        } else {
3098
            return null;
3099
        }
3100
    }
3101
3102
    /**
3103
     * Say if the tracker as "description" defined
3104
     *
3105
     * @return bool
3106
     */
3107
    public function hasSemanticsDescription() {
3108
        return Tracker_Semantic_Description::load($this)->getFieldId() ? true : false;
3109
    }
3110
3111
    /**
3112
     * Return the description field, or null if no title field defined
3113
     *
3114
     * @return Tracker_FormElement_Field_Text the title field, or null if not defined
3115
     */
3116
    public function getDescriptionField() {
3117
        $title_field = Tracker_Semantic_Description::load($this)->getField();
3118
        if ($title_field) {
3119
            return $title_field;
3120
        } else {
3121
            return null;
3122
        }
3123
    }
3124
3125
    /**
3126
     * Say if the tracker as "status" defined
3127
     *
3128
     * @return bool
3129
     */
3130
    public function hasSemanticsStatus() {
3131
        return Tracker_Semantic_Status::load($this)->getFieldId() ? true : false;
3132
    }
3133
3134
    /**
3135
     * Return the status field, or null if no status field defined
3136
     *
3137
     * @return Tracker_FormElement_Field_List the status field, or null if not defined
3138
     */
3139
    public function getStatusField() {
3140
        $status_field = Tracker_Semantic_Status::load($this)->getField();
3141
        if ($status_field) {
3142
            return $status_field;
3143
        } else {
3144
            return null;
3145
        }
3146
    }
3147
3148
    /**
3149
     * Return the contributor field, or null if no contributor field defined
3150
     *
3151
     * @return Tracker_FormElement_Field_List the contributor field, or null if not defined
3152
     */
3153
    public function getContributorField() {
3154
        $contributor_field = Tracker_Semantic_Contributor::load($this)->getField();
3155
        if ($contributor_field) {
3156
            return $contributor_field;
3157
        } else {
3158
            return null;
3159
        }
3160
    }
3161
3162
    /**
3163
     *	existUser - check if a user is already in the project permissions
3164
     *
3165
     *	@param	int		user_id of the new user.
3166
     *	@return boolean	success.
3167
     */
3168
    function existUser($id) {
3169
        if (!$id) {
3170
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_canned','missing_param'));
3171
            return false;
3172
        }
3173
        $perm_dao = new Tracker_PermDao();
3174
        if ($perm_dao->searchByUserIdAndTrackerId($id, $this->getId())) {
3175
            return true;
3176
        } else {
3177
            return false;
3178
        }
3179
    }
3180
3181
3182
    /**
3183
     *	updateUser - update a user's permissions.
3184
     *
3185
     *	@param	int		user_id of the user to update.
3186
     *	@param	int		(1) tech only, (2) admin & tech (3) admin only.
3187
     *	@return boolean	success.
3188
     */
3189
    function updateUser($id, $perm_level) {
3190
        if (!$this->userIsAdmin()) {
3191
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_canned','perm_denied'));
3192
            return false;
3193
        }
3194
        if (!$id) {
3195
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_canned','missing_param'));
3196
            return false;
3197
        }
3198
3199
        $perm_dao = new Tracker_PermDao();
3200
3201
        $row = $perm_dao->searchByUserIdAndTrackerId($id, $this->getId())->getRow();
3202
3203
        if ($row) {
3204
		    if ($perm_dao->updateUser($id, $perm_level, $this->getId())) {
3205
                return true;
3206
            } else {
3207
                return false;
3208
            }
3209
        } else if ($perm_dao->createUser($id, $perm_level, $this->getId())) {
3210
3211
            return true;
3212
3213
        } else {
3214
            return false;
3215
        }
3216
    }
3217
3218
    /**
3219
     *	addUser - add a user to this ArtifactType - depends on UNIQUE INDEX preventing duplicates.
3220
     *
3221
     *	@param	int		user_id of the new user.
3222
     *  @param  value: the value permission
3223
     *
3224
     *	@return boolean	success.
3225
     */
3226
    function addUser($id, $value) {
3227
        global $Language;
3228
3229
        if (!$this->userIsAdmin()) {
3230
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_canned','perm_denied'));
3231
            return false;
3232
        }
3233
        if (!$id) {
3234
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_canned','missing_param'));
3235
            return false;
3236
        }
3237
        $perm_dao = new Tracker_PermDao();
3238
        if ($perm_dao->createUser($id, $value, $this->getId())) {
3239
            return true;
3240
        } else {
3241
            return false;
3242
        }
3243
    }
3244
3245
    /**
3246
     *	deleteUser - delete a user's permissions.
3247
     *
3248
     *	@param	int		user_id of the user who's permissions to delete.
3249
     *	@return boolean	success.
3250
     */
3251
    function deleteUser($id) {
3252
        global $Language;
3253
3254
        if (!$this->userIsAdmin()) {
3255
            $this->setError($Language->getText('plugin_tracker_common_canned','perm_denied'));
3256
            return false;
3257
        }
3258
        if (!$id) {
3259
            $this->setError($Language->getText('plugin_tracker_common_canned','missing_param'));
3260
            return false;
3261
        }
3262
        $perm_dao = new Tracker_PermDao();
3263
        if ($perm_dao->deleteUser($id, $this->getId())) {
3264
            return true;
3265
        } else {
3266
            return false;
3267
        }
3268
    }
3269
3270
    public function setFormElementFactory(Tracker_FormElementFactory $factory) {
3271
        $this->formElementFactory = $factory;
3272
    }
3273
3274
    public function setSharedFormElementFactory(Tracker_SharedFormElementFactory $factory) {
3275
        $this->sharedFormElementFactory = $factory;
3276
    }
3277
3278
    /**
3279
     * Set children trackers
3280
     *
3281
     * @param Tracker[] $trackers
3282
     */
3283
    public function setChildren(array $trackers) {
3284
        $this->children = $trackers;
3285
    }
3286
3287
    /**
3288
     * Return the children of the tracker
3289
     *
3290
     * @return Tracker[]
3291
     */
3292
    public function getChildren() {
3293
        if ($this->children === null) {
3294
            $this->children = $this->getHierarchyFactory()->getChildren($this->getId());
3295
        }
3296
        return $this->children;
3297
    }
3298
3299
    /**
3300
     * Return the hierarchy the tracker belongs to
3301
     *
3302
     * @return Tracker_Hierarchy
3303
     */
3304
    public function getHierarchy() {
3305
        return $this->getHierarchyFactory()->getHierarchy(array($this->getId()));
3306
    }
3307
3308
    /**
3309
     * @return Tracker_HierarchyFactory
3310
     */
3311
    protected function getHierarchyFactory() {
3312
        return new Tracker_HierarchyFactory(new Tracker_Hierarchy_Dao(), $this->getTrackerFactory(), $this->getTrackerArtifactFactory());
3313
    }
3314
3315
    /**
3316
     * Set parent
3317
     *
3318
     * @param Tracker $tracker
3319
     */
3320
    public function setParent(Tracker $tracker = null) {
3321
        $this->parent = $tracker;
0 ignored issues
show
Documentation Bug introduced by
It seems like $tracker can also be of type object<Tracker>. However, the property $parent is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
3322
    }
3323
3324
    /**
3325
     * Return parent tracker of current tracker (if any)
3326
     *
3327
     * @return Tracker
3328
     */
3329
    public function getParent() {
3330
        if ($this->parent === false) {
3331
            $parent_tracker_id = $this->getParentId();
3332
            if ($parent_tracker_id) {
3333
                $this->parent = $this->getTrackerFactory()->getTrackerById($parent_tracker_id);
3334
            } else {
3335
                $this->parent = self::NO_PARENT;
0 ignored issues
show
Documentation Bug introduced by
The property $parent was declared of type boolean, but self::NO_PARENT is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
3336
            }
3337
        }
3338
        if ($this->parent === self::NO_PARENT) {
3339
            return null;
3340
        }
3341
        return $this->parent;
3342
    }
3343
3344
    protected function getParentId() {
3345
        return $this->getHierarchy()->getParent($this->getId());
3346
    }
3347
3348
    /**
3349
     * Return workflow of the current tracker (there is always a workflow).
3350
     *
3351
     * @return Workflow
3352
     */
3353
    public function getWorkflow() {
3354
        if (! $this->workflow) {
3355
            $this->workflow = $this->getWorkflowFactory()->getWorkflowByTrackerId($this->getId());
3356
            if (! $this->workflow) {
3357
                $this->workflow = $this->getWorkflowFactory()->getWorkflowWithoutTransition($this);
3358
            }
3359
        }
3360
        return $this->workflow;
3361
    }
3362
3363
    /**
3364
     * @return string
3365
     */
3366
    public function getUri() {
3367
        return TRACKER_BASE_URL . '/?tracker=' . $this->getId();
3368
    }
3369
3370
    private function getArtifactXMLImporterForArtifactCopy(Tracker_XML_Importer_CopyArtifactInformationsAggregator $logger) {
3371
        $fields_validator      = new Tracker_Artifact_Changeset_AtGivenDateFieldsValidator(
3372
            $this->getFormElementFactory()
3373
        );
3374
3375
        $changeset_dao         = new Tracker_Artifact_ChangesetDao();
3376
        $changeset_comment_dao = new Tracker_Artifact_Changeset_CommentDao();
3377
        $send_notifications    = true;
3378
3379
        $artifact_creator = new Tracker_ArtifactCreator(
3380
            $this->getTrackerArtifactFactory(),
3381
            $fields_validator,
3382
            new Tracker_Artifact_Changeset_InitialChangesetAtGivenDateCreator(
3383
                $fields_validator,
3384
                $this->getFormElementFactory(),
3385
                $changeset_dao,
3386
                $this->getTrackerArtifactFactory(),
3387
                EventManager::instance()
3388
            )
3389
        );
3390
3391
        $new_changeset_creator = new Tracker_Artifact_Changeset_NewChangesetAtGivenDateCreator(
3392
            $fields_validator,
3393
            $this->getFormElementFactory(),
3394
            $changeset_dao,
3395
            $changeset_comment_dao,
3396
            $this->getTrackerArtifactFactory(),
3397
            EventManager::instance(),
3398
            ReferenceManager::instance()
3399
        );
3400
3401
        return new Tracker_Artifact_XMLImport(
3402
            new XML_RNGValidator(),
3403
            $artifact_creator,
3404
            $new_changeset_creator,
3405
            Tracker_FormElementFactory::instance(),
3406
            new XMLImportHelper($this->getUserManager()),
3407
            new Tracker_FormElement_Field_List_Bind_Static_ValueDao(),
3408
            $logger,
3409
            $send_notifications
3410
        );
3411
    }
3412
3413
    private function getChildrenCollector(Codendi_Request $request) {
3414
        if ($request->get('copy_children')) {
3415
            return new Tracker_XML_ChildrenCollector();
3416
        }
3417
3418
        return new Tracker_XML_Exporter_NullChildrenCollector();
3419
    }
3420
3421
    private function getArtifactXMLExporter(
3422
        Tracker_XML_ChildrenCollector $children_collector,
3423
        Tracker_XML_Exporter_FilePathXMLExporter $file_path_xml_exporter,
3424
        PFUser $current_user
3425
    ) {
3426
        $builder           = new Tracker_XML_Exporter_ArtifactXMLExporterBuilder();
3427
        $user_xml_exporter = $this->getUserXMLExporter();
3428
3429
        return $builder->build($children_collector, $file_path_xml_exporter, $current_user, $user_xml_exporter);
3430
    }
3431
3432
    private function getUserXMLExporter() {
3433
        return new UserXMLExporter(
3434
            $this->getUserManager(),
3435
            new UserXMLExportedCollection(new XML_RNGValidator(), new XML_SimpleXMLCDATAFactory())
3436
        );
3437
    }
3438
3439
    private function getChangesetXMLUpdater() {
3440
        $visitor = new Tracker_XML_Updater_FieldChangeXMLUpdaterVisitor(
3441
            new Tracker_XML_Updater_FieldChange_FieldChangeDateXMLUpdater(),
3442
            new Tracker_XML_Updater_FieldChange_FieldChangeFloatXMLUpdater(),
3443
            new Tracker_XML_Updater_FieldChange_FieldChangeIntegerXMLUpdater(),
3444
            new Tracker_XML_Updater_FieldChange_FieldChangeTextXMLUpdater(),
3445
            new Tracker_XML_Updater_FieldChange_FieldChangeStringXMLUpdater(),
3446
            new Tracker_XML_Updater_FieldChange_FieldChangePermissionsOnArtifactXMLUpdater(),
3447
            new Tracker_XML_Updater_FieldChange_FieldChangeListXMLUpdater(),
3448
            new Tracker_XML_Updater_FieldChange_FieldChangeOpenListXMLUpdater(),
3449
            new Tracker_XML_Updater_FieldChange_FieldChangeUnknownXMLUpdater()
3450
        );
3451
3452
        return new Tracker_XML_Updater_ChangesetXMLUpdater(
3453
            $visitor,
3454
            $this->getFormElementFactory()
3455
        );
3456
3457
    }
3458
3459
    private function getFileXMLUpdater() {
3460
        return new Tracker_XML_Updater_TemporaryFileXMLUpdater(
3461
            new Tracker_XML_Updater_TemporaryFileCreator()
3462
        );
3463
    }
3464
3465
    public function hasFieldBindedToUserGroupsViewableByUser(PFUser $user) {
3466
        $form_elements = $this->formElementFactory->getUsedFieldsBindedToUserGroups($this);
3467
3468
        foreach ($form_elements as $field) {
3469
            if ($field->userCanRead($user)) {
3470
                return true;
3471
            }
3472
        }
3473
3474
        return false;
3475
    }
3476
}
3477