Complex classes like Tracker_Report often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Tracker_Report, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
26 | class Tracker_Report extends Error implements Tracker_Dispatchable_Interface { |
||
27 | |||
28 | const ACTION_SAVE = 'report-save'; |
||
29 | const ACTION_SAVEAS = 'report-saveas'; |
||
30 | const ACTION_REPLACE = 'report-replace'; |
||
31 | const ACTION_DELETE = 'report-delete'; |
||
32 | const ACTION_SCOPE = 'report-scope'; |
||
33 | const ACTION_DEFAULT = 'report-default'; |
||
34 | const ACTION_CLEANSESSION = 'clean-session'; |
||
35 | const TYPE_CRITERIA = 'criteria'; |
||
36 | const TYPE_TABLE = 'table'; |
||
37 | |||
38 | public $id; |
||
39 | public $name; |
||
40 | public $description; |
||
41 | public $current_renderer_id; |
||
42 | public $parent_report_id; |
||
43 | public $user_id; |
||
44 | public $group_id; |
||
45 | public $is_default; |
||
46 | public $tracker_id; |
||
47 | public $is_query_displayed; |
||
48 | public $updated_by; |
||
49 | public $updated_at; |
||
50 | |||
51 | public $renderers; |
||
52 | public $criteria; |
||
53 | |||
54 | /** |
||
55 | * Constructor |
||
56 | * |
||
57 | * @param int $id The id of the report |
||
58 | * @param string $name The name of the report |
||
59 | * @param string $description The description of the report |
||
60 | * @param int $current_renderer_id The current Renderer id to display |
||
61 | * @param int $parent_report_id The parent report if this report is temporary (null else) |
||
62 | * @param int $user_id The owner of the report (null if scope = project) |
||
63 | * @param bool $is_default true if the report is the default one |
||
64 | * @param int $tracker_id The id of the tracker to which this Tracker_Report is associated. |
||
65 | */ |
||
66 | function __construct($id, $name, $description, $current_renderer_id, $parent_report_id, $user_id, $is_default, $tracker_id, $is_query_displayed, $updated_by, $updated_at) { |
||
67 | parent::__construct(); |
||
68 | $this->id = $id; |
||
69 | $this->name = $name; |
||
70 | $this->description = $description; |
||
71 | $this->current_renderer_id = $current_renderer_id; |
||
72 | $this->parent_report_id = $parent_report_id; |
||
73 | $this->user_id = $user_id; |
||
74 | $this->is_default = $is_default; |
||
75 | $this->tracker_id = $tracker_id; |
||
76 | $this->is_query_displayed = $is_query_displayed; |
||
77 | $this->updated_by = $updated_by; |
||
78 | $this->updated_at = $updated_at; |
||
79 | } |
||
80 | |||
81 | public function setProjectId($id) { |
||
84 | |||
85 | protected function getProjectId() { |
||
86 | if (!$this->group_id) { |
||
87 | $this->group_id = $this->tracker->getGroupId(); |
||
88 | } |
||
89 | return $this->group_id; |
||
90 | } |
||
91 | |||
92 | public function registerInSession() { |
||
95 | |||
96 | protected function getCriteriaDao() { |
||
99 | |||
100 | /** @return Tracker_Report_Criteria[] */ |
||
101 | public function getCriteria() { |
||
102 | $session_criteria = null; |
||
103 | if (isset($this->report_session)) { |
||
104 | $session_criteria = &$this->report_session->getCriteria(); |
||
105 | } |
||
106 | |||
107 | $this->criteria = array(); |
||
108 | $ff = $this->getFormElementFactory(); |
||
109 | //there is previously stored |
||
110 | if ($session_criteria) { |
||
111 | $rank = 0; |
||
112 | foreach ($session_criteria as $key => $value) { |
||
113 | if ($value['is_removed'] == 0) { |
||
114 | $is_advanced = isset($value['is_advanced']) ? $value['is_advanced'] : 0 ; |
||
115 | if ($formElement = $ff->getUsedFormElementById($key)) { |
||
116 | if ($formElement->userCanRead()) { |
||
117 | $formElement->setCriteriaValue( (!empty($value['value']) ? $value['value']: ''), $this->id ) ; |
||
118 | $this->criteria[$key] = new Tracker_Report_Criteria( |
||
119 | 0, |
||
120 | $this, |
||
121 | $formElement, |
||
122 | $rank, |
||
123 | $is_advanced |
||
124 | ); |
||
125 | $rank++; |
||
126 | } |
||
127 | } |
||
128 | } |
||
129 | } |
||
130 | } else { |
||
131 | //retrieve data from database |
||
132 | foreach($this->getCriteriaDao()->searchByReportId($this->id) as $row) { |
||
|
|||
133 | if ($formElement = $ff->getFormElementFieldById($row['field_id'])) { |
||
134 | if ($formElement->userCanRead()) { |
||
135 | $this->criteria[$row['field_id']] = new Tracker_Report_Criteria( |
||
136 | $row['id'], |
||
137 | $this, |
||
138 | $formElement, |
||
139 | $row['rank'], |
||
140 | $row['is_advanced'] |
||
141 | ); |
||
142 | $criterion_value = $formElement->getCriteriaValue($this->criteria[$row['field_id']]); |
||
143 | $criterion_opts['is_advanced'] = $row['is_advanced']; |
||
144 | if (isset($this->report_session)) { |
||
145 | $this->report_session->storeCriterion($row['field_id'], $criterion_value, $criterion_opts ); |
||
146 | } |
||
147 | } |
||
148 | } |
||
149 | } |
||
150 | } |
||
151 | return $this->criteria; |
||
152 | } |
||
153 | |||
154 | public function getCriteriaFromDb() { |
||
173 | |||
174 | public function getFormElementFactory() { |
||
177 | /** |
||
178 | * Sets or adds a criterion to the global report search criteria list |
||
179 | * @param integer $field_id criterion id to be added or set |
||
180 | * @return Tracker_Report_Criteria |
||
181 | * @TODO refactor : must be renamed after addCriterion, and return the current criterion |
||
182 | */ |
||
183 | protected function setCriteria($field_id) { |
||
184 | $ff = $this->getFormElementFactory(); |
||
185 | $formElement = $ff->getFormElementById($field_id); |
||
186 | $this->criteria[$field_id] = new Tracker_Report_Criteria( |
||
187 | 0, |
||
188 | $this, |
||
189 | $formElement, |
||
190 | 0, |
||
191 | 0 |
||
192 | ); |
||
193 | return $this->criteria[$field_id]; |
||
194 | } |
||
195 | |||
196 | protected $current_user; |
||
197 | protected function getCurrentUser() { |
||
198 | if (!$this->current_user) { |
||
199 | $this->current_user = UserManager::instance()->getCurrentUser(); |
||
200 | } |
||
201 | return $this->current_user; |
||
202 | } |
||
203 | |||
204 | protected $permissions_manager; |
||
205 | private function getPermissionsManager() { |
||
206 | if (!$this->permissions_manager) { |
||
207 | $this->permissions_manager = PermissionsManager::instance(); |
||
208 | } |
||
209 | return $this->permissions_manager; |
||
210 | } |
||
211 | |||
212 | protected $matching_ids; |
||
213 | public function getMatchingIds($request = null, $use_data_from_db = false) { |
||
214 | if (!$this->matching_ids) { |
||
215 | $user = $this->getCurrentUser(); |
||
216 | if ($use_data_from_db) { |
||
217 | $criteria = $this->getCriteriaFromDb(); |
||
218 | } else { |
||
219 | $criteria = $this->getCriteria(); |
||
220 | } |
||
221 | $this->matching_ids = $this->getMatchingIdsInDb($this->getDao(), $this->getPermissionsManager(), $this->getTracker(), $user, $criteria); |
||
222 | |||
223 | $additional_criteria = $this->getAdditionalCriteria(); |
||
224 | $result = array(); |
||
225 | $search_performed = false; |
||
226 | EventManager::instance()->processEvent( |
||
227 | TRACKER_EVENT_REPORT_PROCESS_ADDITIONAL_QUERY, |
||
228 | array( |
||
229 | 'request' => $request, |
||
230 | 'result' => &$result, |
||
231 | 'search_performed' => &$search_performed, |
||
232 | 'tracker' => $this->getTracker(), |
||
233 | 'additional_criteria' => $additional_criteria, |
||
234 | 'user' => $user, |
||
235 | 'form_element_factory' => $this->getFormElementFactory() |
||
236 | ) |
||
237 | ); |
||
238 | if ($search_performed) { |
||
239 | $joiner = new Tracker_Report_ResultJoiner(); |
||
240 | |||
241 | $this->matching_ids = $this->implodeMatchingIds( |
||
242 | $joiner->joinResults( |
||
243 | $this->getLastChangesetIdByArtifactId($this->matching_ids), |
||
244 | $result |
||
245 | ) |
||
246 | ); |
||
247 | } |
||
248 | } |
||
249 | |||
250 | return $this->matching_ids; |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Given the output of getMatchingIds() which returns an array containing 'artifacts ids' and 'Last changeset ids' |
||
255 | * as two strings with comma separated values, this method would format such output to an array with artifactIds in keys |
||
256 | * and last changeset ids in values. |
||
257 | * @see Tracker_Report::getMatchingIds() for usage |
||
258 | * |
||
259 | * @return Array |
||
260 | */ |
||
261 | private function getLastChangesetIdByArtifactId($matching_ids) { |
||
262 | $artifact_ids = explode(',', $matching_ids['id']); |
||
263 | $last_changeset_ids = explode(',', $matching_ids['last_changeset_id']); |
||
264 | |||
265 | $formatted_matching_ids = array(); |
||
266 | foreach ($artifact_ids as $key => $artifactId) { |
||
267 | $formatted_matching_ids[$artifactId] = $last_changeset_ids[$key]; |
||
268 | } |
||
269 | return $formatted_matching_ids; |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * This method is the opposite of getLastChangesetIdByArtifactId(). |
||
274 | * Given an array with artifactIds in keys and lastChangesetIds on values it would return and array with two elements of type string |
||
275 | * the first contains comma separated "artifactIds" and the second contains comma separated "lastChangesetIds". |
||
276 | * @see Tracker_Report::getMatchingIds() for usage |
||
277 | * |
||
278 | * @param Array $formattedMatchingIds Matching Id's that will get converted in that format |
||
279 | * |
||
280 | * @return Array |
||
281 | */ |
||
282 | private function implodeMatchingIds($formattedMatchingIds) { |
||
297 | |||
298 | protected function getMatchingIdsInDb(DataAccessObject $dao, PermissionsManager $permissionManager, Tracker $tracker, PFUser $user, array $criteria) { |
||
299 | $dump_criteria = array(); |
||
300 | foreach ($criteria as $c) { |
||
301 | $dump_criteria[$c->field->getName()] = $c->field->getCriteriaValue($c); |
||
302 | } |
||
303 | $dao->logStart(__METHOD__, json_encode(array( |
||
304 | 'user' => $user->getUserName(), |
||
305 | 'project' => $tracker->getGroupId(), |
||
306 | 'query' => $dump_criteria, |
||
307 | 'trackers' => array($tracker->getId()), |
||
308 | ))); |
||
309 | |||
310 | $matching_ids = array(); |
||
311 | |||
312 | $group_id = $tracker->getGroupId(); |
||
313 | $permissions = $permissionManager->getPermissionsAndUgroupsByObjectid($tracker->getId()); |
||
314 | $contributor_field = $tracker->getContributorField(); |
||
315 | $contributor_field_id = $contributor_field ? $contributor_field->getId() : null; |
||
316 | |||
317 | $additional_from = array(); |
||
318 | $additional_where = array(); |
||
319 | foreach($criteria as $c) { |
||
320 | if ($f = $c->getFrom()) { |
||
321 | $additional_from[] = $f; |
||
322 | } |
||
323 | |||
324 | if ($w = $c->getWhere()) { |
||
325 | $additional_where[] = $w; |
||
326 | } |
||
327 | } |
||
328 | $matching_ids['id'] = ''; |
||
329 | $matching_ids['last_changeset_id'] = ''; |
||
330 | $matching_ids_result = $dao->searchMatchingIds($group_id, $tracker->getId(), $additional_from, $additional_where, $user, $permissions, $contributor_field_id); |
||
331 | if ($matching_ids_result) { |
||
332 | $matching_ids = $matching_ids_result->getRow(); |
||
333 | if ($matching_ids) { |
||
334 | if (substr($matching_ids['id'], -1) === ',') { |
||
335 | $matching_ids['id'] = substr($matching_ids['id'], 0, -1); |
||
336 | } |
||
337 | if (substr($matching_ids['last_changeset_id'], -1) === ',') { |
||
338 | $matching_ids['last_changeset_id'] = substr($matching_ids['last_changeset_id'], 0, -1); |
||
339 | } |
||
340 | } |
||
341 | } |
||
342 | |||
343 | $nb_matching = $matching_ids['id'] ? substr_count($matching_ids['id'], ',') + 1 : 0; |
||
344 | $dao->logEnd(__METHOD__, $nb_matching); |
||
345 | |||
346 | return $matching_ids; |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * @return boolean true if the report has been modified since the last checkout |
||
351 | */ |
||
352 | public function isObsolete() { |
||
355 | |||
356 | /** |
||
357 | * @return string html the user who has modified the report. Or false if the report has not been modified |
||
358 | */ |
||
359 | public function getLastUpdaterUserName() { |
||
360 | if ($this->isObsolete()) { |
||
361 | return UserHelper::instance()->getLinkOnUserFromUserId($this->updated_by); |
||
362 | } |
||
363 | return ''; |
||
364 | } |
||
365 | |||
366 | protected function displayHeader(Tracker_IFetchTrackerSwitcher $layout, $request, $current_user, $report_can_be_modified) { |
||
367 | $header_builder = new Tracker_Report_HeaderRenderer( |
||
368 | Tracker_ReportFactory::instance(), |
||
369 | Codendi_HTMLPurifier::instance(), |
||
370 | $this->getTemplateRenderer() |
||
371 | ); |
||
372 | $header_builder->displayHeader($layout, $request, $current_user, $this, $report_can_be_modified); |
||
373 | } |
||
374 | |||
375 | public function nbPublicReport($reports) { |
||
376 | $i = 0; |
||
377 | foreach ($reports as $report) { |
||
378 | if ($report->user_id == null) { |
||
379 | $i++; |
||
380 | } |
||
381 | } |
||
382 | return $i; |
||
383 | } |
||
384 | |||
385 | public function fetchDisplayQuery(array $criteria, array $additional_criteria, $report_can_be_modified, PFUser $current_user) { |
||
386 | $hp = Codendi_HTMLPurifier::instance(); |
||
387 | |||
388 | $html = ''; |
||
389 | $html .= '<div id="tracker_report_query" data-report-id="'.$this->id.'">'; |
||
390 | $html .= '<form action="" method="POST" id="tracker_report_query_form">'; |
||
391 | $html .= '<input type="hidden" name="report" value="' . $this->id . '" />'; |
||
392 | $id = 'tracker_report_query_' . $this->id; |
||
393 | $html .= '<h4 class="backlog-planning-search-title ' . Toggler::getClassname($id, $this->is_query_displayed ? true : false) . '" id="' . $id . '">'; |
||
394 | |||
395 | // Query title |
||
396 | $html .= $GLOBALS['Language']->getText('plugin_tracker_report', 'search').'</h4>'; |
||
397 | $used = array(); |
||
398 | $criteria_fetched = array(); |
||
399 | foreach ($criteria as $criterion) { |
||
400 | if ($criterion->field->isUsed()) { |
||
401 | $li = '<li id="tracker_report_crit_' . $criterion->field->getId() . '">'; |
||
402 | if ($current_user->isAnonymous()) { |
||
403 | $li .= $criterion->fetchWithoutExpandFunctionnality(); |
||
404 | } else { |
||
405 | $li .= $criterion->fetch(); |
||
406 | } |
||
407 | $li .= '</li>'; |
||
408 | $criteria_fetched[] = $li; |
||
409 | $used[$criterion->field->getId()] = $criterion->field; |
||
410 | } |
||
411 | } |
||
412 | if ($report_can_be_modified && ! $current_user->isAnonymous()) { |
||
413 | $html .= '<div class="pull-right">'; |
||
414 | $html .= $this->getAddCriteriaDropdown($used); |
||
415 | $html .= '</div>'; |
||
416 | } |
||
417 | |||
418 | $array_of_html_criteria = array(); |
||
419 | EventManager::instance()->processEvent( |
||
420 | TRACKER_EVENT_REPORT_DISPLAY_ADDITIONAL_CRITERIA, |
||
421 | array( |
||
422 | 'array_of_html_criteria' => &$array_of_html_criteria, |
||
423 | 'tracker' => $this->getTracker(), |
||
424 | 'additional_criteria' => $additional_criteria, |
||
425 | 'user' => $current_user, |
||
426 | ) |
||
427 | ); |
||
428 | foreach ($array_of_html_criteria as $additional_criteria) { |
||
429 | $criteria_fetched[] = '<li>'. $additional_criteria .'</li>'; |
||
430 | } |
||
431 | $html .= '<ul id="tracker_query">' . implode('', $criteria_fetched).'</ul>'; |
||
432 | |||
433 | $html .= '<div align="center">'; |
||
434 | $html .= '<button type="submit" name="tracker_query_submit" class="btn btn-primary">'; |
||
435 | $html .= '<i class="icon-search"></i> '; |
||
436 | $html .= $GLOBALS['Language']->getText('global', 'btn_search'); |
||
437 | $html .= '</button>'; |
||
438 | $html .= '</div>'; |
||
439 | $html .= '</form>'; |
||
440 | $html .= '</div>'; |
||
441 | return $html; |
||
442 | } |
||
443 | |||
444 | private function getAddCriteriaDropdown($used) { |
||
445 | $add_criteria_presenter = new Templating_Presenter_ButtonDropdownsMini( |
||
446 | 'tracker_report_add_criteria_dropdown', |
||
447 | $GLOBALS['Language']->getText('plugin_tracker_report', 'toggle_criteria'), |
||
448 | $this->getFieldsAsDropdownOptions('tracker_report_add_criterion', $used, self::TYPE_CRITERIA) |
||
449 | ); |
||
450 | $add_criteria_presenter->setIcon('icon-eye-close'); |
||
451 | |||
452 | return $this->getTemplateRenderer()->renderToString('button_dropdowns', $add_criteria_presenter); |
||
453 | } |
||
454 | |||
455 | public function getFieldsAsDropdownOptions($id_prefix, array $used, $dropdown_type) { |
||
456 | $fields_for_criteria = array(); |
||
457 | $fields_for_sort = array(); |
||
458 | |||
459 | foreach($this->getFormElementFactory()->getFields($this->getTracker()) as $field) { |
||
460 | if ($dropdown_type === self::TYPE_CRITERIA && ! $field->canBeUsedAsReportCriterion()) { |
||
461 | continue; |
||
462 | } |
||
463 | |||
464 | if ($field->userCanRead() && $field->isUsed()) { |
||
465 | $fields_for_criteria[$field->getId()] = $field; |
||
466 | $fields_for_sort[$field->getId()] = strtolower($field->getLabel()); |
||
467 | } |
||
468 | } |
||
469 | asort($fields_for_sort); |
||
470 | $criteria_options = array(); |
||
471 | foreach ($fields_for_sort as $id => $nop) { |
||
472 | $option = new Templating_Presenter_ButtonDropdownsOption( |
||
473 | $id_prefix.'_'.$id, |
||
474 | $fields_for_criteria[$id]->getLabel(), |
||
475 | isset($used[$id]), |
||
476 | '#' |
||
477 | ); |
||
478 | $option->setLiParameters( |
||
479 | array( |
||
480 | 'data-field-id' => $id, |
||
481 | 'data-field-is-used' => intval(isset($used[$id])), |
||
482 | ) |
||
483 | ); |
||
484 | $criteria_options[] = $option; |
||
485 | } |
||
486 | return $criteria_options; |
||
487 | } |
||
488 | |||
489 | public function getTemplateRenderer() { |
||
490 | return TemplateRendererFactory::build()->getRenderer( |
||
491 | array( |
||
492 | TRACKER_TEMPLATE_DIR.'/report', |
||
493 | ForgeConfig::get('codendi_dir').'/src/templates/common' |
||
494 | ) |
||
495 | ); |
||
496 | } |
||
497 | |||
498 | public function display(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) { |
||
670 | |||
671 | public function getRenderers() { |
||
674 | |||
675 | protected function orderRenderersByRank($renderers) { |
||
676 | $array_rank = array(); |
||
677 | foreach($renderers as $field_id => $properties) { |
||
678 | $array_rank[$field_id] = $properties->rank; |
||
679 | } |
||
680 | asort($array_rank); |
||
681 | $renderers_sort = array(); |
||
682 | foreach ($array_rank as $id => $rank) { |
||
683 | $renderers_sort[$id] = $renderers[$id]; |
||
684 | } |
||
685 | return $renderers_sort; |
||
686 | } |
||
687 | |||
688 | protected function getRendererFactory() { |
||
691 | |||
692 | protected function _fetchAddCriteria($used) { |
||
709 | |||
710 | /** |
||
711 | * Say if the report is public |
||
712 | * |
||
713 | * @return bool |
||
714 | */ |
||
715 | public function isPublic() { |
||
718 | |||
719 | /** |
||
720 | * Only owners of a report can update it. |
||
721 | * owner = report->user_id |
||
722 | * or if null, owner = tracker admin or site admins |
||
723 | * @param PFUser $user the user who wants to update the report |
||
724 | * @return boolean |
||
725 | */ |
||
726 | public function userCanUpdate($user) { |
||
738 | |||
739 | private function isBelongingToATracker() { |
||
742 | |||
743 | protected $tracker; |
||
744 | public function getTracker() { |
||
745 | if (!$this->tracker) { |
||
746 | $this->tracker = TrackerFactory::instance()->getTrackerById($this->tracker_id); |
||
747 | } |
||
748 | return $this->tracker; |
||
749 | } |
||
750 | |||
751 | public function setTracker(Tracker $tracker) { |
||
755 | |||
756 | /** |
||
757 | * hide or show the criteria |
||
758 | */ |
||
759 | public function toggleQueryDisplay() { |
||
763 | |||
764 | /** |
||
765 | * Remove a formElement from criteria |
||
766 | * @param int $formElement_id the formElement used for the criteria |
||
767 | */ |
||
768 | public function removeCriteria($formElement_id) { |
||
769 | $criteria = $this->getCriteria(); |
||
770 | if (isset($criteria[$formElement_id])) { |
||
771 | if ($this->getCriteriaDao()->delete($this->id, $formElement_id)) { |
||
772 | $criteria[$formElement_id]->delete(); |
||
773 | unset($criteria[$formElement_id]); |
||
774 | } |
||
775 | } |
||
776 | return $this; |
||
777 | } |
||
778 | |||
779 | /** |
||
780 | * Add a criteria |
||
781 | * |
||
782 | * @param Tracker_Report_Criteria the formElement used for the criteria |
||
783 | * |
||
784 | * @return int the id of the new criteria |
||
785 | */ |
||
786 | public function addCriteria( Tracker_Report_Criteria $criteria ) { |
||
790 | |||
791 | public function deleteAllCriteria() { |
||
794 | |||
795 | /** |
||
796 | * Toggle the state 'is_advanced' of a criteria |
||
797 | * @param int $formElement_id the formElement used for the criteria |
||
798 | */ |
||
799 | public function toggleAdvancedCriterion($formElement_id) { |
||
800 | $advanced = 1; |
||
801 | $session_criterion = $this->report_session->getCriterion($formElement_id); |
||
802 | if ( !empty($session_criterion['is_advanced']) ) { |
||
803 | $advanced = 0; |
||
804 | } |
||
805 | $this->report_session->updateCriterion($formElement_id, '', array('is_advanced'=>$advanced)); |
||
806 | return $this; |
||
807 | } |
||
808 | |||
809 | /** |
||
810 | * Store the criteria value |
||
811 | * NOTICE : if a criterion does not exist it is not created |
||
812 | * @param array $criteria_values |
||
813 | */ |
||
814 | public function updateCriteriaValues($criteria_values) { |
||
825 | |||
826 | public function updateAdditionalCriteriaValues($additional_criteria_values) { |
||
827 | foreach($additional_criteria_values as $key => $new_value) { |
||
828 | $additional_criterion = new Tracker_Report_AdditionalCriterion($key, $new_value); |
||
829 | $this->report_session->storeAdditionalCriterion($additional_criterion); |
||
830 | } |
||
831 | } |
||
832 | |||
833 | /** |
||
834 | * Process the request for the specified renderer |
||
835 | * @param int $renderer_id |
||
836 | * @param Request $request |
||
837 | * @return ReportRenderer |
||
838 | */ |
||
839 | public function processRendererRequest($renderer_id, Tracker_IDisplayTrackerLayout $layout, $request, $current_user, $store_in_session = true) { |
||
840 | $rrf = Tracker_Report_RendererFactory::instance(); |
||
841 | if ($renderer = $rrf->getReportRendererByReportAndId($this, $renderer_id, $store_in_session)) { |
||
842 | $renderer->process($layout, $request, $current_user); |
||
843 | } |
||
844 | } |
||
845 | |||
846 | /** |
||
847 | * Delete a renderer from the report |
||
848 | * @param mixed the renderer to remove (Tracker_Report_Renderer or the id as int) |
||
849 | */ |
||
850 | public function deleteRenderer($renderer) { |
||
851 | $rrf = Tracker_Report_RendererFactory::instance(); |
||
852 | if (!is_a($renderer, 'Tracker_Report_Renderer')) { |
||
853 | $renderer_id = (int)$renderer; |
||
854 | $renderer = $rrf->getReportRendererByReportAndId($this, $renderer_id); |
||
855 | } |
||
856 | if ($renderer) { |
||
857 | $renderer_id = $renderer->id; |
||
858 | $renderer->delete(); |
||
859 | $rrf->delete($renderer_id); |
||
860 | } |
||
861 | return $this; |
||
862 | } |
||
863 | |||
864 | /** |
||
865 | * Move a renderer at a specific position |
||
866 | * |
||
867 | * @param mixed $renderer the renderer to remove (Tracker_Report_Renderer or the id as int) |
||
868 | * @param int $position the new position |
||
869 | */ |
||
870 | public function moveRenderer($renderer, $position) { |
||
871 | $rrf = Tracker_Report_RendererFactory::instance(); |
||
872 | if (!is_a($renderer, 'Tracker_Report_Renderer')) { |
||
873 | $renderer_id = (int)$renderer; |
||
874 | $renderer = $rrf->getReportRendererByReportAndId($this, $renderer_id); |
||
875 | } |
||
876 | if ($renderer) { |
||
877 | $rrf->move($renderer->id, $this, $position); |
||
878 | } |
||
879 | return $this; |
||
880 | } |
||
881 | |||
882 | /** |
||
883 | * Add a new renderer to the report |
||
884 | * |
||
885 | * @param string $name |
||
886 | * @param string $description |
||
887 | * |
||
888 | * @return int the id of the new renderer |
||
889 | */ |
||
890 | public function addRenderer($name, $description, $type) { |
||
894 | |||
895 | public function addRendererInSession($name, $description, $type) { |
||
899 | |||
900 | |||
901 | public function process(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) { |
||
902 | if ($this->isObsolete()) { |
||
903 | header('X-Codendi-Tracker-Report-IsObsolete: '. $this->getLastUpdaterUserName()); |
||
904 | } |
||
905 | $hp = Codendi_HTMLPurifier::instance(); |
||
906 | $tracker = $this->getTracker(); |
||
907 | |||
908 | if ($request->exist('tracker') && $request->get('tracker') != $tracker->getId()) { |
||
909 | $GLOBALS['Response']->addFeedback( |
||
910 | Feedback::ERROR, |
||
911 | $GLOBALS['Language']->getText('plugin_tracker_admin', 'invalid_request') |
||
912 | ); |
||
913 | |||
914 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
915 | 'tracker' => $tracker->getId() |
||
916 | ))); |
||
917 | } |
||
918 | |||
919 | switch ($request->get('func')) { |
||
920 | case 'display-masschange-form': |
||
921 | if ($tracker->userIsAdmin($current_user)) { |
||
922 | $masschange_aids = array(); |
||
923 | $renderer_table = $request->get('renderer_table'); |
||
924 | |||
925 | if ( !empty($renderer_table['masschange_checked']) ) { |
||
926 | $masschange_aids = $request->get('masschange_aids'); |
||
927 | } else if (!empty($renderer_table['masschange_all'])) { |
||
928 | $masschange_aids = $request->get('masschange_aids_all'); |
||
929 | } |
||
930 | |||
931 | if( empty($masschange_aids) ) { |
||
932 | $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_masschange_detail', 'no_items_selected')); |
||
933 | $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $tracker->getId()); |
||
934 | } |
||
935 | $tracker->displayMasschangeForm($layout, $masschange_aids); |
||
936 | } else { |
||
937 | $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_admin', 'access_denied')); |
||
938 | $GLOBALS['Response']->redirect(TRACKER_BASE_URL.'/?tracker='. $tracker->getId()); |
||
939 | } |
||
940 | break; |
||
941 | case 'update-masschange-aids': |
||
942 | $masschange_updater = new Tracker_MasschangeUpdater($tracker, $this); |
||
943 | $masschange_updater->updateArtifacts($current_user, $request); |
||
944 | break; |
||
945 | case 'remove-criteria': |
||
946 | if ($request->get('field') && ! $current_user->isAnonymous()) { |
||
947 | $this->report_session->removeCriterion($request->get('field')); |
||
948 | $this->report_session->setHasChanged(); |
||
949 | } |
||
950 | break; |
||
951 | case 'add-criteria': |
||
952 | if ($request->get('field') && ! $current_user->isAnonymous()) { |
||
953 | //TODO: make sure that the formElement exists and the user can read it |
||
954 | if ($request->isAjax()) { |
||
955 | $criteria = $this->getCriteria(); |
||
956 | $field_id = $request->get('field'); |
||
957 | $this->setCriteria($field_id); |
||
958 | $this->report_session->storeCriterion($field_id, '', array('is_advanced'=>0)); |
||
959 | $this->report_session->setHasChanged(); |
||
960 | echo $this->criteria[$field_id]->fetch(); |
||
961 | } |
||
962 | } |
||
963 | break; |
||
964 | case 'toggle-advanced': |
||
965 | if ($request->get('field') && ! $current_user->isAnonymous()) { |
||
966 | $this->toggleAdvancedCriterion($request->get('field')); |
||
967 | $this->report_session->setHasChanged(); |
||
968 | if ($request->isAjax()) { |
||
969 | $criteria = $this->getCriteria(); |
||
970 | if (isset($criteria[$request->get('field')])) { |
||
971 | echo $criteria[$request->get('field')]->fetch(); |
||
972 | } |
||
973 | } |
||
974 | } |
||
975 | break; |
||
976 | case self::ACTION_CLEANSESSION: |
||
977 | $this->report_session->clean(); |
||
978 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
979 | 'tracker' => $this->tracker_id |
||
980 | ))); |
||
981 | break; |
||
982 | case 'renderer': |
||
983 | if ($request->get('renderer')) { |
||
984 | $store_in_session = true; |
||
985 | if ($request->exist('store_in_session')) { |
||
986 | $store_in_session = (bool)$request->get('store_in_session'); |
||
987 | } |
||
988 | $this->processRendererRequest($request->get('renderer'), $layout, $request, $current_user, $store_in_session); |
||
989 | } |
||
990 | break; |
||
991 | case 'rename-renderer': |
||
992 | if ($request->get('new_name') == '') { |
||
993 | $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_report','renderer_name_mandatory')); |
||
994 | } else if (! $current_user->isAnonymous() && (int)$request->get('renderer') && trim($request->get('new_name'))) { |
||
995 | $this->report_session->renameRenderer((int)$request->get('renderer'), trim($request->get('new_name')), trim($request->get('new_description'))); |
||
996 | $this->report_session->setHasChanged(); |
||
997 | } |
||
998 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
999 | 'report' => $this->id |
||
1000 | ))); |
||
1001 | break; |
||
1002 | case 'delete-renderer': |
||
1003 | if (! $current_user->isAnonymous() && (int)$request->get('renderer')) { |
||
1004 | $this->report_session->removeRenderer((int)$request->get('renderer')); |
||
1005 | $this->report_session->setHasChanged(); |
||
1006 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
1007 | 'report' => $this->id |
||
1008 | ))); |
||
1009 | } |
||
1010 | break; |
||
1011 | case 'move-renderer': |
||
1012 | if (! $current_user->isAnonymous() && (int)$request->get('renderer')) { |
||
1013 | if ($request->isAjax()) { |
||
1014 | $this->report_session->moveRenderer($request->get('tracker_report_renderers')); |
||
1015 | $this->report_session->setHasChanged(); |
||
1016 | } else { |
||
1017 | if ( $request->get('move-renderer-direction')) { |
||
1018 | $this->moveRenderer((int)$request->get('renderer'), $request->get('move-renderer-direction')); |
||
1019 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
1020 | 'report' => $this->id |
||
1021 | ))); |
||
1022 | } |
||
1023 | } |
||
1024 | } |
||
1025 | break; |
||
1026 | case 'add-renderer': |
||
1027 | $new_name = trim($request->get('new_name')); |
||
1028 | $new_description = trim($request->get('new_description')); |
||
1029 | $new_type = trim($request->get('new_type')); |
||
1030 | if (! $current_user->isAnonymous() && $new_name) { |
||
1031 | $new_renderer_id = $this->addRendererInSession($new_name, $new_description, $new_type); |
||
1032 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
1033 | 'report' => $this->id, |
||
1034 | 'renderer' => $new_renderer_id ? $new_renderer_id : '' |
||
1035 | ))); |
||
1036 | } |
||
1037 | break; |
||
1038 | case self::ACTION_SAVE: |
||
1039 | Tracker_ReportFactory::instance()->save($this); |
||
1040 | $this->saveCriteria(); |
||
1041 | $this->saveAdditionalCriteria(); |
||
1042 | $this->saveRenderers(); |
||
1043 | //Clean session |
||
1044 | $this->report_session->cleanNamespace(); |
||
1045 | |||
1046 | $GLOBALS['Response']->addFeedback('info', '<a href="?report='. $this->id .'">'. $hp->purify($this->name, CODENDI_PURIFIER_CONVERT_HTML) .'</a> has been saved.', CODENDI_PURIFIER_DISABLED); |
||
1047 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
1048 | 'report' => $this->id |
||
1049 | ))); |
||
1050 | break; |
||
1051 | case self::ACTION_SAVEAS: |
||
1052 | $redirect_to_report_id = $this->id; |
||
1053 | $report_copy_name = trim($request->get('report_copy_name')); |
||
1054 | if ($report_copy_name) { |
||
1055 | $new_report = Tracker_ReportFactory::instance()->duplicateReportSkeleton($this, $this->tracker_id, $current_user->getId()); |
||
1056 | //Set the name |
||
1057 | $new_report->name = $report_copy_name; |
||
1058 | //The new report is individual |
||
1059 | $new_report->user_id = $current_user->getId(); |
||
1060 | Tracker_ReportFactory::instance()->save($new_report); |
||
1061 | $GLOBALS['Response']->addFeedback('info', '<a href="?report='. $new_report->id .'">'. $hp->purify($new_report->name, CODENDI_PURIFIER_CONVERT_HTML) .'</a> has been created.', CODENDI_PURIFIER_DISABLED); |
||
1062 | $redirect_to_report_id = $new_report->id; |
||
1063 | //copy parent tracker session content |
||
1064 | $this->report_session->copy($this->id, $redirect_to_report_id); |
||
1065 | //clean current session namespace |
||
1066 | $this->report_session->cleanNamespace(); |
||
1067 | //save session content into db |
||
1068 | $new_report->saveCriteria(); |
||
1069 | $new_report->saveAdditionalCriteria(); |
||
1070 | $new_report->saveRenderers(); |
||
1071 | $new_report->report_session->cleanNamespace(); |
||
1072 | } else { |
||
1073 | $GLOBALS['Response']->addFeedback('error', 'Invalid copy name', CODENDI_PURIFIER_DISABLED); |
||
1074 | } |
||
1075 | |||
1076 | |||
1077 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
1078 | 'report' => $redirect_to_report_id |
||
1079 | ))); |
||
1080 | break; |
||
1081 | case self::ACTION_DELETE: |
||
1082 | $this->delete(); |
||
1083 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
1084 | 'tracker' => $this->tracker_id |
||
1085 | ))); |
||
1086 | break; |
||
1087 | case self::ACTION_SCOPE: |
||
1088 | if ($this->getTracker()->userIsAdmin($current_user) && (!$this->user_id || $this->user_id == $current_user->getId())) { |
||
1089 | if ($request->exist('report_scope_public')) { |
||
1090 | $old_user_id = $this->user_id; |
||
1091 | if ($request->get('report_scope_public') && $this->user_id == $current_user->getId()) { |
||
1092 | $this->user_id = null; |
||
1093 | } else if (!$request->get('report_scope_public') && !$this->user_id) { |
||
1094 | $this->user_id = $current_user->getId(); |
||
1095 | } |
||
1096 | if ($this->user_id != $old_user_id) { |
||
1097 | Tracker_ReportFactory::instance()->save($this); |
||
1098 | } |
||
1099 | } |
||
1100 | } |
||
1101 | case self::ACTION_DEFAULT: |
||
1102 | if ($this->getTracker()->userIsAdmin($current_user)) { |
||
1103 | if ($request->exist('report_default')) { |
||
1104 | $old_user_id = $this->user_id; |
||
1105 | if ($request->get('report_default')) { |
||
1106 | $this->is_default = '1'; |
||
1107 | } else { |
||
1108 | $this->is_default = '0'; |
||
1109 | } |
||
1110 | } |
||
1111 | $this->setDefaultReport(); |
||
1112 | $GLOBALS['Response']->redirect('?'. http_build_query(array( |
||
1113 | 'report' => $this->id |
||
1114 | ))); |
||
1115 | break; |
||
1116 | } |
||
1117 | default: |
||
1118 | if ($request->exist('tracker_query_submit')) { |
||
1119 | $criteria_values = $request->get('criteria'); |
||
1120 | if (!empty($criteria_values)) { |
||
1121 | $this->updateCriteriaValues($criteria_values); |
||
1122 | } |
||
1123 | |||
1124 | $additional_criteria_values = $request->get('additional_criteria'); |
||
1125 | if (!empty($additional_criteria_values)) { |
||
1126 | $this->updateAdditionalCriteriaValues($additional_criteria_values); |
||
1127 | } |
||
1128 | |||
1129 | $this->report_session->setHasChanged(); |
||
1130 | } |
||
1131 | $this->display($layout, $request, $current_user); |
||
1132 | break; |
||
1133 | } |
||
1134 | } |
||
1135 | |||
1136 | public function setDefaultReport() { |
||
1137 | $default_report = Tracker_ReportFactory::instance()->getDefaultReportByTrackerId($this->tracker_id); |
||
1138 | if ($default_report) { |
||
1139 | $default_report->is_default = '0'; |
||
1140 | Tracker_ReportFactory::instance()->save($default_report); |
||
1141 | } |
||
1142 | Tracker_ReportFactory::instance()->save($this); |
||
1143 | |||
1144 | } |
||
1145 | /** |
||
1146 | * NOTICE: make sure you are in the correct session namespace |
||
1147 | */ |
||
1148 | public function saveCriteria() { |
||
1149 | //populate $this->criteria |
||
1150 | $this->getCriteria(); |
||
1151 | //Delete criteria value |
||
1152 | foreach($this->criteria as $c) { |
||
1153 | if ($c->field->getCriteriaValue($c)) { |
||
1154 | $c->field->delete($c->id); |
||
1155 | } |
||
1156 | } |
||
1157 | //Delete criteria in the db |
||
1158 | $this->deleteAllCriteria(); |
||
1159 | |||
1160 | $session_criteria = $this->report_session->getCriteria(); |
||
1161 | if (is_array($session_criteria)) { |
||
1162 | foreach($session_criteria as $key=>$session_criterion) { |
||
1163 | if ( !empty($session_criterion['is_removed'])) { |
||
1164 | continue; |
||
1165 | } |
||
1166 | |||
1167 | if (isset($this->criteria[$key])) { |
||
1168 | $c = $this->criteria[$key]; |
||
1169 | $id = $this->addCriteria($c); |
||
1170 | $c->setId($id); |
||
1171 | $c->updateValue($session_criterion['value']); |
||
1172 | } |
||
1173 | } |
||
1174 | } |
||
1175 | } |
||
1176 | |||
1177 | public function saveAdditionalCriteria() { |
||
1178 | $additional_criteria = $this->getAdditionalCriteria(); |
||
1179 | EventManager::instance()->processEvent( |
||
1180 | TRACKER_EVENT_REPORT_SAVE_ADDITIONAL_CRITERIA, |
||
1181 | array( |
||
1182 | 'additional_criteria' => $additional_criteria, |
||
1183 | 'report' => $this, |
||
1184 | ) |
||
1185 | ); |
||
1186 | } |
||
1187 | |||
1188 | /** |
||
1189 | * Save report renderers |
||
1190 | * NOTICE: make sure you are in the correct session namespace |
||
1191 | * |
||
1192 | * @return void |
||
1193 | */ |
||
1194 | public function saveRenderers() { |
||
1195 | $rrf = Tracker_Report_RendererFactory::instance(); |
||
1196 | |||
1197 | //Get the renderers in the session and in the db |
||
1198 | $renderers_session = $this->getRenderers(); |
||
1199 | $renderers_db = $rrf->getReportRenderersByReportFromDb($this); |
||
1200 | |||
1201 | //Delete renderers in db if they are deleted in the session |
||
1202 | foreach ($renderers_db as $renderer_db_key => $renderer_db) { |
||
1203 | if ( ! isset($renderers_session[$renderer_db_key]) ) { |
||
1204 | $this->deleteRenderer($renderer_db_key); |
||
1205 | } |
||
1206 | } |
||
1207 | |||
1208 | //Create or update renderers in db |
||
1209 | if(is_array($renderers_session)) { |
||
1210 | foreach ($renderers_session as $renderer_key => $renderer) { |
||
1211 | if( ! isset($renderers_db[$renderer_key]) ) { |
||
1212 | // this is a new renderer |
||
1213 | $renderer->create(); |
||
1214 | } else { |
||
1215 | // this is an old renderer |
||
1216 | $rrf->save($renderer); |
||
1217 | $renderer->update(); |
||
1218 | } |
||
1219 | } |
||
1220 | } |
||
1221 | } |
||
1222 | |||
1223 | /** |
||
1224 | * Delete the report and its renderers |
||
1225 | */ |
||
1226 | protected function delete() { |
||
1227 | //Delete user preferences |
||
1228 | $dao = new UserPreferencesDao(); |
||
1229 | $dao->deleteByPreferenceNameAndValue('tracker_'. $this->tracker_id .'_last_report', $this->id); |
||
1230 | |||
1231 | //Delete criteria |
||
1232 | foreach($this->getCriteria() as $criteria) { |
||
1233 | $this->removeCriteria($criteria->field->id); |
||
1234 | } |
||
1235 | |||
1236 | //Delete renderers |
||
1237 | foreach($this->getRenderers() as $renderer) { |
||
1238 | $this->deleteRenderer($renderer); |
||
1239 | } |
||
1240 | |||
1241 | //clean session |
||
1242 | $this->report_session->cleanNamespace(); |
||
1243 | |||
1244 | //Delete me |
||
1245 | return Tracker_ReportFactory::instance()->delete($this->id); |
||
1246 | } |
||
1247 | |||
1248 | public function duplicate($from_report, $formElement_mapping) { |
||
1249 | //Duplicate criteria |
||
1250 | Tracker_Report_CriteriaFactory::instance()->duplicate($from_report, $this, $formElement_mapping); |
||
1251 | |||
1252 | //Duplicate renderers |
||
1253 | Tracker_Report_RendererFactory::instance()->duplicate($from_report, $this, $formElement_mapping); |
||
1254 | } |
||
1255 | |||
1256 | /** |
||
1257 | * Transforms Report into a SimpleXMLElement |
||
1258 | * |
||
1259 | * @param SimpleXMLElement $root the node to which the Report is attached (passed by reference) |
||
1260 | */ |
||
1261 | public function exportToXml(SimpleXMLElement $roott, $xmlMapping) { |
||
1262 | $root = $roott->addChild('report'); |
||
1263 | // if old ids are important, modify code here |
||
1264 | if (false) { |
||
1265 | $root->addAttribute('id', $this->id); |
||
1266 | $root->addAttribute('tracker_id', $this->tracker_id); |
||
1267 | $root->addAttribute('current_renderer_id', $this->current_renderer_id); |
||
1268 | $root->addAttribute('user_id', $this->user_id); |
||
1269 | $root->addAttribute('parent_report_id', $this->parent_report_id); |
||
1270 | } |
||
1271 | // only add if different from default values |
||
1272 | if (!$this->is_default) { |
||
1273 | $root->addAttribute('is_default', $this->is_default); |
||
1274 | } |
||
1275 | if (!$this->is_query_displayed) { |
||
1276 | $root->addAttribute('is_query_displayed', $this->is_query_displayed); |
||
1277 | } |
||
1278 | $root->addChild('name', $this->name); |
||
1279 | // only add if not empty |
||
1280 | if ($this->description) { |
||
1281 | $root->addChild('description', $this->description); |
||
1282 | } |
||
1283 | $child = $root->addChild('criterias'); |
||
1284 | foreach($this->getCriteria() as $criteria) { |
||
1285 | if ($criteria->field->isUsed()) { |
||
1286 | $grandchild = $child->addChild('criteria'); |
||
1287 | $criteria->exportToXML($grandchild, $xmlMapping); |
||
1288 | } |
||
1289 | } |
||
1290 | $child = $root->addChild('renderers'); |
||
1291 | foreach($this->getRenderers() as $renderer) { |
||
1292 | $grandchild = $child->addChild('renderer'); |
||
1293 | $renderer->exportToXML($grandchild, $xmlMapping); |
||
1294 | } |
||
1295 | } |
||
1296 | |||
1297 | /** |
||
1298 | * Convert the current report to its SOAP representation |
||
1299 | * |
||
1300 | * @return Array |
||
1301 | */ |
||
1302 | public function exportToSoap() { |
||
1303 | return array( |
||
1304 | 'id' => (int)$this->id, |
||
1305 | 'name' => (string)$this->name, |
||
1306 | 'description' => (string)$this->description, |
||
1307 | 'user_id' => (int)$this->user_id, |
||
1308 | 'is_default' => (bool)$this->is_default, |
||
1309 | ); |
||
1310 | } |
||
1311 | |||
1312 | protected $dao; |
||
1313 | /** |
||
1314 | * @return Tracker_ReportDao |
||
1315 | */ |
||
1316 | public function getDao() { |
||
1317 | if (!$this->dao) { |
||
1318 | $this->dao = new Tracker_ReportDao(); |
||
1319 | } |
||
1320 | return $this->dao; |
||
1321 | } |
||
1322 | |||
1323 | public function getId() { |
||
1326 | |||
1327 | public function getName() { |
||
1330 | |||
1331 | public function getAdditionalCriteria() { |
||
1332 | $session_additional_criteria = null; |
||
1333 | if (isset($this->report_session)) { |
||
1334 | $session_additional_criteria = &$this->report_session->getAdditionalCriteria(); |
||
1335 | } |
||
1336 | |||
1337 | $additional_criteria = array(); |
||
1338 | if ($session_additional_criteria) { |
||
1339 | foreach ($session_additional_criteria as $key => $additional_criterion_value) { |
||
1340 | $additional_criterion = new Tracker_Report_AdditionalCriterion($key, $additional_criterion_value['value']); |
||
1341 | $additional_criteria[$key] = $additional_criterion; |
||
1342 | } |
||
1343 | } else { |
||
1344 | $additional_criteria_values = array(); |
||
1345 | EventManager::instance()->processEvent( |
||
1346 | TRACKER_EVENT_REPORT_LOAD_ADDITIONAL_CRITERIA, |
||
1347 | array( |
||
1348 | 'additional_criteria_values' => &$additional_criteria_values, |
||
1349 | 'report' => $this, |
||
1350 | ) |
||
1351 | ); |
||
1352 | foreach ($additional_criteria_values as $key => $additional_criterion_value) { |
||
1353 | $additional_criterion = new Tracker_Report_AdditionalCriterion($key, $additional_criterion_value['value']); |
||
1354 | $additional_criteria[$key] = $additional_criterion; |
||
1355 | if (isset($this->report_session)) { |
||
1356 | $this->report_session->storeAdditionalCriterion($additional_criterion); |
||
1357 | } |
||
1358 | } |
||
1359 | } |
||
1360 | |||
1361 | return $additional_criteria; |
||
1362 | } |
||
1363 | |||
1364 | private function fetchUpdateRendererForm(Tracker_Report_Renderer $renderer) { |
||
1365 | $hp = Codendi_HTMLPurifier::instance(); |
||
1398 | |||
1399 | private function fetchAddRendererForm($current_renderer) { |
||
1438 | } |
||
1439 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.