Complex classes like Tracker_FormElement_Field_ArtifactLink 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_FormElement_Field_ArtifactLink, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 22 | class Tracker_FormElement_Field_ArtifactLink extends Tracker_FormElement_Field { |
||
| 23 | |||
| 24 | const CREATE_NEW_PARENT_VALUE = -1; |
||
| 25 | const NEW_VALUES_KEY = 'new_values'; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * @var Tracker_ArtifactFactory |
||
| 29 | */ |
||
| 30 | private $artifact_factory; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * @var Tracker_Artifact|null |
||
| 34 | */ |
||
| 35 | private $source_of_association = array(); |
||
| 36 | |||
| 37 | /** |
||
| 38 | * Display the html form in the admin ui |
||
| 39 | * |
||
| 40 | * @return string html |
||
| 41 | */ |
||
| 42 | protected function fetchAdminFormElement() { |
||
| 43 | $hp = Codendi_HTMLPurifier::instance(); |
||
| 44 | $html = ''; |
||
| 45 | $value = ''; |
||
| 46 | if ($this->hasDefaultValue()) { |
||
| 47 | $value = $this->getDefaultValue(); |
||
| 48 | } |
||
| 49 | $html .= '<input type="text" |
||
| 50 | value="'. $hp->purify($value, CODENDI_PURIFIER_CONVERT_HTML) .'" autocomplete="off" />'; |
||
| 51 | $html .= '<br />'; |
||
| 52 | $html .= '<a href="#">bug #123</a><br />'; |
||
| 53 | $html .= '<a href="#">bug #321</a><br />'; |
||
| 54 | $html .= '<a href="#">story #10234</a>'; |
||
| 55 | return $html; |
||
| 56 | } |
||
| 57 | |||
| 58 | /** |
||
| 59 | * Display the field value as a criteria |
||
| 60 | * |
||
| 61 | * @param Tracker_ReportCriteria $criteria |
||
| 62 | * |
||
| 63 | * @return string |
||
| 64 | */ |
||
| 65 | public function fetchCriteriaValue($criteria) { |
||
| 66 | $html = '<input type="text" name="criteria['. $this->id .']" id="tracker_report_criteria_'. $this->id .'" value="'; |
||
| 67 | if ($criteria_value = $this->getCriteriaValue($criteria)) { |
||
| 68 | $hp = Codendi_HTMLPurifier::instance(); |
||
| 69 | $html .= $hp->purify($criteria_value, CODENDI_PURIFIER_CONVERT_HTML); |
||
| 70 | } |
||
| 71 | $html .= '" />'; |
||
| 72 | return $html; |
||
| 73 | } |
||
| 74 | |||
| 75 | /** |
||
| 76 | * Display the field as a Changeset value. |
||
| 77 | * Used in report table |
||
| 78 | * |
||
| 79 | * @param int $artifact_id the corresponding artifact id |
||
| 80 | * @param int $changeset_id the corresponding changeset |
||
| 81 | * @param mixed $value the value of the field |
||
| 82 | * |
||
| 83 | * @return string |
||
| 84 | */ |
||
| 85 | public function fetchChangesetValue($artifact_id, $changeset_id, $value, $report=null, $from_aid = null) { |
||
| 86 | $arr = array(); |
||
| 87 | $values = $this->getChangesetValues($changeset_id); |
||
| 88 | foreach ($values as $artifact_link_info) { |
||
| 89 | $arr[] = $artifact_link_info->getUrl(); |
||
| 90 | } |
||
| 91 | $html = implode(', ', $arr); |
||
| 92 | return $html; |
||
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * Display the field as a Changeset value. |
||
| 97 | * Used in CSV data export. |
||
| 98 | * |
||
| 99 | * @param int $artifact_id the corresponding artifact id |
||
| 100 | * @param int $changeset_id the corresponding changeset |
||
| 101 | * @param mixed $value the value of the field |
||
| 102 | * |
||
| 103 | * @return string |
||
| 104 | */ |
||
| 105 | public function fetchCSVChangesetValue($artifact_id, $changeset_id, $value, $report) { |
||
| 106 | $arr = array(); |
||
| 107 | $values = $this->getChangesetValues($changeset_id); |
||
| 108 | foreach ($values as $artifact_link_info) { |
||
| 109 | $arr[] = $artifact_link_info->getArtifactId(); |
||
| 110 | } |
||
| 111 | $html = implode(',', $arr); |
||
| 112 | return $html; |
||
| 113 | } |
||
| 114 | |||
| 115 | /** |
||
| 116 | * Fetch the value |
||
| 117 | * @param mixed $value the value of the field |
||
| 118 | * @return string |
||
| 119 | */ |
||
| 120 | public function fetchRawValue($value) { |
||
| 124 | |||
| 125 | /** |
||
| 126 | * Get available values of this field for SOAP usage |
||
| 127 | * Fields like int, float, date, string don't have available values |
||
| 128 | * |
||
| 129 | * @return mixed The values or null if there are no specific available values |
||
| 130 | */ |
||
| 131 | public function getSoapAvailableValues() { |
||
| 134 | |||
| 135 | /** |
||
| 136 | * Return data that can be proceced by createArtifact or updateArtifact based on SOAP request |
||
| 137 | * |
||
| 138 | * @param stdClass $soap_value |
||
| 139 | * @param Tracker_Artifact $artifact |
||
| 140 | * |
||
| 141 | * @return array |
||
| 142 | */ |
||
| 143 | public function getFieldDataFromSoapValue(stdClass $soap_value, Tracker_Artifact $artifact = null) { |
||
| 146 | |||
| 147 | |||
| 148 | /** |
||
| 149 | * @see Tracker_FormElement_Field::getFieldDataFromRESTValue() |
||
| 150 | * @param array $value |
||
| 151 | * @param Tracker_Artifact $artifact |
||
| 152 | * @return array |
||
| 153 | * @throws Exception |
||
| 154 | */ |
||
| 155 | public function getFieldDataFromRESTValue(array $value, Tracker_Artifact $artifact = null) { |
||
| 156 | if (array_key_exists('links', $value) && is_array($value['links'])){ |
||
| 157 | $link_ids = array(); |
||
| 158 | foreach ($value['links'] as $link) { |
||
| 159 | if (array_key_exists('id', $link)) { |
||
| 160 | $link_ids[] = $link['id']; |
||
| 161 | } |
||
| 162 | } |
||
| 163 | return $this->getFieldData(implode(',', $link_ids), $artifact); |
||
| 164 | } |
||
| 165 | throw new Tracker_FormElement_InvalidFieldValueException('Value should be \'links\' and an array of {"id": integer}'); |
||
| 166 | } |
||
| 167 | |||
| 168 | public function getFieldDataFromRESTValueByField($value, Tracker_Artifact $artifact = null) { |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Get the field data (SOAP or CSV) for artifact submission |
||
| 174 | * |
||
| 175 | * @param string $string_value The soap field value |
||
| 176 | * @param Tracker_Artifact $artifact The artifact the value is to be added/removed |
||
| 177 | * |
||
| 178 | * @return array |
||
| 179 | */ |
||
| 180 | public function getFieldData($string_value, Tracker_Artifact $artifact = null) { |
||
| 181 | $existing_links = $this->getArtifactLinkIdsOfLastChangeset($artifact); |
||
| 182 | $submitted_values = $this->getArrayOfIdsFromString($string_value); |
||
| 183 | $new_values = array_diff($submitted_values, $existing_links); |
||
| 184 | $removed_values = array_diff($existing_links, $submitted_values); |
||
| 185 | return $this->getDataLikeWebUI($new_values, $removed_values); |
||
| 186 | } |
||
| 187 | |||
| 188 | public function fetchArtifactForOverlay(Tracker_Artifact $artifact) { |
||
| 189 | $user_manager = UserManager::instance(); |
||
| 190 | $user = $user_manager->getCurrentUser(); |
||
| 191 | $parent_tracker = $this->getTracker()->getParent(); |
||
| 192 | |||
| 193 | if ($artifact->getParent($user) || ! $parent_tracker) { |
||
| 194 | return ''; |
||
| 195 | } |
||
| 196 | |||
| 197 | $prefill_parent = ''; |
||
| 198 | $name = 'artifact['. $this->id .']'; |
||
| 199 | $current_user = $this->getCurrentUser(); |
||
| 200 | $can_create = false; |
||
| 201 | |||
| 202 | return $this->fetchParentSelector($prefill_parent, $name, $parent_tracker, $current_user, $can_create); |
||
| 203 | } |
||
| 204 | |||
| 205 | public function fetchSubmitForOverlay($submitted_values) { |
||
| 222 | |||
| 223 | private function getArtifactLinkIdsOfLastChangeset(Tracker_Artifact $artifact = null) { |
||
| 224 | if ($artifact) { |
||
| 225 | return array_map(array($this, 'getArtifactLinkId'), $this->getChangesetValues($artifact->getLastChangeset()->getId())); |
||
| 226 | } |
||
| 227 | return array(); |
||
| 228 | } |
||
| 229 | |||
| 230 | private function getArtifactLinkId(Tracker_ArtifactLinkInfo $link_info) { |
||
| 233 | |||
| 234 | private function getArrayOfIdsFromString($value) { |
||
| 237 | |||
| 238 | private function getDataLikeWebUI(array $new_values, array $removed_values) { |
||
| 239 | return array( |
||
| 240 | 'new_values' => $this->formatNewValuesLikeWebUI($new_values), |
||
| 241 | 'removed_values' => $this->formatRemovedValuesLikeWebUI($removed_values) |
||
| 242 | ); |
||
| 243 | } |
||
| 244 | |||
| 245 | private function formatNewValuesLikeWebUI(array $new_values) { |
||
| 248 | |||
| 249 | private function formatRemovedValuesLikeWebUI(array $removed_values) { |
||
| 250 | $values = array(); |
||
| 251 | foreach ($removed_values as $value) { |
||
| 252 | $values[$value] = array($value); |
||
| 253 | } |
||
| 254 | return $values; |
||
| 255 | } |
||
| 256 | |||
| 257 | /** |
||
| 258 | * Get the "from" statement to allow search with this field |
||
| 259 | * You can join on 'c' which is a pseudo table used to retrieve |
||
| 260 | * the last changeset of all artifacts. |
||
| 261 | * |
||
| 262 | * @param Tracker_ReportCriteria $criteria |
||
| 263 | * |
||
| 264 | * @return string |
||
| 265 | */ |
||
| 266 | public function getCriteriaFrom($criteria) { |
||
| 267 | //Only filter query if field is used |
||
| 268 | if($this->isUsed()) { |
||
| 269 | //Only filter query if criteria is valuated |
||
| 270 | if ($criteria_value = $this->getCriteriaValue($criteria)) { |
||
| 271 | $a = 'A_'. $this->id; |
||
| 272 | $b = 'B_'. $this->id; |
||
| 273 | return " INNER JOIN tracker_changeset_value AS $a ON ($a.changeset_id = c.id AND $a.field_id = $this->id ) |
||
| 274 | INNER JOIN tracker_changeset_value_artifactlink AS $b ON ( |
||
| 275 | $b.changeset_value_id = $a.id |
||
| 276 | AND ". $this->buildMatchExpression("$b.artifact_id", $criteria_value) ." |
||
| 277 | ) "; |
||
| 278 | } |
||
| 279 | } |
||
| 280 | return ''; |
||
| 281 | } |
||
| 282 | protected $pattern = '[+\-]*[0-9]+'; |
||
| 283 | protected function cast($value) { |
||
| 286 | protected function buildMatchExpression($field_name, $criteria_value) { |
||
| 287 | $expr = ''; |
||
| 288 | $matches = array(); |
||
| 289 | if (preg_match('/\/(.*)\//', $criteria_value, $matches)) { |
||
| 290 | |||
| 291 | // If it is sourrounded by /.../ then assume a regexp |
||
| 292 | $expr = $field_name." RLIKE ".$this->getCriteriaDao()->da->quoteSmart($matches[1]); |
||
| 293 | } |
||
| 294 | if (!$expr) { |
||
| 295 | $matches = array(); |
||
| 296 | if (preg_match("/^(<|>|>=|<=)\s*($this->pattern)\$/", $criteria_value, $matches)) { |
||
| 297 | // It's < or >, = and a number then use as is |
||
| 298 | $matches[2] = (string)($this->cast($matches[2])); |
||
| 299 | $expr = $field_name.' '.$matches[1].' '.$matches[2]; |
||
| 300 | |||
| 301 | } else if (preg_match("/^($this->pattern)\$/", $criteria_value, $matches)) { |
||
| 302 | // It's a number so use equality |
||
| 303 | $matches[1] = $this->cast($matches[1]); |
||
| 304 | $expr = $field_name.' = '.$matches[1]; |
||
| 305 | |||
| 306 | } else if (preg_match("/^($this->pattern)\s*-\s*($this->pattern)\$/", $criteria_value, $matches)) { |
||
| 307 | // it's a range number1-number2 |
||
| 308 | $matches[1] = (string)($this->cast($matches[1])); |
||
| 309 | $matches[2] = (string)($this->cast($matches[2])); |
||
| 310 | $expr = $field_name.' >= '.$matches[1].' AND '.$field_name.' <= '. $matches[2]; |
||
| 311 | |||
| 312 | } else { |
||
| 313 | // Invalid syntax - no condition |
||
| 314 | $expr = '1'; |
||
| 315 | } |
||
| 316 | } |
||
| 317 | return $expr; |
||
| 318 | } |
||
| 319 | |||
| 320 | /** |
||
| 321 | * Get the "where" statement to allow search with this field |
||
| 322 | * |
||
| 323 | * @param Tracker_ReportCriteria $criteria |
||
| 324 | * |
||
| 325 | * @return string |
||
| 326 | */ |
||
| 327 | public function getCriteriaWhere($criteria) { |
||
| 330 | |||
| 331 | public function getQuerySelect() { |
||
| 332 | $R1 = 'R1_'. $this->id; |
||
| 333 | $R2 = 'R2_'. $this->id; |
||
| 334 | return "$R2.artifact_id AS `". $this->name . "`"; |
||
| 335 | } |
||
| 336 | |||
| 337 | public function getQueryFrom() { |
||
| 338 | $R1 = 'R1_'. $this->id; |
||
| 339 | $R2 = 'R2_'. $this->id; |
||
| 340 | |||
| 341 | return "LEFT JOIN ( tracker_changeset_value AS $R1 |
||
| 342 | INNER JOIN tracker_changeset_value_artifactlink AS $R2 ON ($R2.changeset_value_id = $R1.id) |
||
| 343 | ) ON ($R1.changeset_id = c.id AND $R1.field_id = ". $this->id ." )"; |
||
| 344 | } |
||
| 345 | |||
| 346 | /** |
||
| 347 | * Return the dao of the criteria value used with this field. |
||
| 348 | * @return DataAccessObject |
||
| 349 | */ |
||
| 350 | protected function getCriteriaDao() { |
||
| 353 | |||
| 354 | private function fetchParentSelector($prefill_parent, $name, Tracker $parent_tracker, PFUser $user, $can_create) { |
||
| 355 | $purifier = Codendi_HTMLPurifier::instance(); |
||
| 356 | $possible_parents_getr = new Tracker_Artifact_PossibleParentsRetriever($this->getArtifactFactory()); |
||
| 357 | $html = ''; |
||
| 358 | $html .= '<p>'; |
||
| 359 | list($label, $paginated_possible_parents, $display_selector) = $possible_parents_getr->getPossibleArtifactParents($parent_tracker, $user, 0, 0); |
||
| 360 | $possible_parents = $paginated_possible_parents->getArtifacts(); |
||
| 361 | if ($display_selector) { |
||
| 362 | $html .= '<label>'; |
||
| 363 | $html .= $GLOBALS['Language']->getText('plugin_tracker_artifact', 'formelement_artifactlink_choose_parent', $purifier->purify($parent_tracker->getItemName())); |
||
| 364 | $html .= '<select name="'. $purifier->purify($name) .'[parent]">'; |
||
| 365 | $html .= '<option value="">'. $GLOBALS['Language']->getText('global', 'please_choose_dashed') .'</option>'; |
||
| 366 | if ($can_create) { |
||
| 367 | $html .= '<option value="'.self::CREATE_NEW_PARENT_VALUE.'">'. $GLOBALS['Language']->getText('plugin_tracker_artifact', 'formelement_artifactlink_create_new_parent') .'</option>'; |
||
| 368 | } |
||
| 369 | $html .= $this->fetchArtifactParentsOptions($prefill_parent, $label, $possible_parents); |
||
| 370 | $html .= '</select>'; |
||
| 371 | $html .= '</label>'; |
||
| 372 | } elseif (count($possible_parents) > 0) { |
||
| 373 | $html .= $GLOBALS['Language']->getText('plugin_tracker_artifact', 'formelement_artifactlink_will_have_as_parent', array($possible_parents[0]->fetchDirectLinkToArtifactWithTitle())); |
||
| 374 | } |
||
| 375 | $html .= '</p>'; |
||
| 376 | return $html; |
||
| 377 | } |
||
| 378 | |||
| 379 | private function fetchArtifactParentsOptions($prefill_parent, $label, array $possible_parents) { |
||
| 395 | |||
| 396 | /** |
||
| 397 | * Fetch the html widget for the field |
||
| 398 | * |
||
| 399 | * @param Tracker_Artifact $artifact Artifact on which we operate |
||
| 400 | * @param string $name The name, if any |
||
| 401 | * @param array $artifact_links The current artifact links |
||
| 402 | * @param string $prefill_new_values Prefill new values field (what the user has submitted, if any) |
||
| 403 | * @param array $prefill_removed_values Pre-remove values (what the user has submitted, if any) |
||
| 404 | * @param string $prefill_parent Prefilled parent (what the user has submitted, if any) - Only valid on submit |
||
| 405 | * @param bool $read_only True if the user can't add or remove links |
||
| 406 | * |
||
| 407 | * @return string html |
||
| 408 | */ |
||
| 409 | protected function fetchHtmlWidget( |
||
| 410 | Tracker_Artifact $artifact, |
||
| 411 | $name, |
||
| 412 | $artifact_links, |
||
| 413 | $prefill_new_values, |
||
| 414 | $prefill_removed_values, |
||
| 415 | $prefill_parent, |
||
| 416 | $read_only, |
||
| 417 | $from_aid = null, |
||
| 418 | $reverse_artifact_links = false |
||
| 419 | ) { |
||
| 420 | $current_user = $this->getCurrentUser(); |
||
| 421 | $html = ''; |
||
| 422 | |||
| 423 | if ($reverse_artifact_links) { |
||
| 424 | $html .= '<div class="artifact-link-value-reverse">'; |
||
| 425 | $html .= '<a href="" class="btn" id="display-tracker-form-element-artifactlink-reverse">' . $GLOBALS['Language']->getText('plugin_tracker_artifact', 'formelement_artifactlink_display_reverse') . '</a>'; |
||
| 426 | $html .= '<div id="tracker-form-element-artifactlink-reverse" style="display: none">'; |
||
| 427 | } else { |
||
| 428 | $html .= '<div class="artifact-link-value">'; |
||
| 429 | } |
||
| 430 | |||
| 431 | $html .= '<h5 class="artifack_link_subtitle">'.$this->getWidgetTitle($reverse_artifact_links).'</h5>'; |
||
| 432 | |||
| 433 | $html_name_new = ''; |
||
| 434 | $html_name_del = ''; |
||
| 435 | |||
| 436 | if ($name) { |
||
| 437 | $html_name_new = 'name="'. $name .'[new_values]"'; |
||
| 438 | $html_name_del = 'name="'. $name .'[removed_values]'; |
||
| 439 | } |
||
| 440 | |||
| 441 | $hp = Codendi_HTMLPurifier::instance(); |
||
| 442 | $read_only_class = 'read-only'; |
||
| 443 | |||
| 444 | if (! $read_only) { |
||
| 445 | $read_only_class = ''; |
||
| 446 | $html .= '<div><span class="input-append" style="display:inline;"><input type="text" |
||
| 447 | '. $html_name_new .' |
||
| 448 | class="tracker-form-element-artifactlink-new" |
||
| 449 | size="40" |
||
| 450 | value="'. $hp->purify($prefill_new_values, CODENDI_PURIFIER_CONVERT_HTML) .'" |
||
| 451 | title="' . $GLOBALS['Language']->getText('plugin_tracker_artifact', 'formelement_artifactlink_help') . '" />'; |
||
| 452 | $html .= '</span></div>'; |
||
| 453 | |||
| 454 | $parent_tracker = $this->getTracker()->getParent(); |
||
| 455 | $is_submit = $artifact->getId() == -1; |
||
| 456 | if ($parent_tracker && $is_submit) { |
||
| 457 | $can_create = true; |
||
| 458 | $html .= $this->fetchParentSelector($prefill_parent, $name, $parent_tracker, $current_user, $can_create); |
||
| 459 | } |
||
| 460 | } |
||
| 461 | |||
| 462 | $html .= '<div class="tracker-form-element-artifactlink-list '.$read_only_class.'">'; |
||
| 463 | if ($artifact_links) { |
||
| 464 | $ids = array(); |
||
| 465 | // build an array of artifact_id / last_changeset_id for fetch renderer method |
||
| 466 | foreach ($artifact_links as $artifact_link) { |
||
| 467 | if ($artifact_link->getTracker()->isActive() && $artifact_link->userCanView($current_user)) { |
||
| 468 | if (!isset($ids[$artifact_link->getTrackerId()])) { |
||
| 469 | $ids[$artifact_link->getTrackerId()] = array( |
||
| 470 | 'id' => '', |
||
| 471 | 'last_changeset_id' => '', |
||
| 472 | ); |
||
| 473 | } |
||
| 474 | $ids[$artifact_link->getTrackerId()]['id'] .= $artifact_link->getArtifactId() .','; |
||
| 475 | $ids[$artifact_link->getTrackerId()]['last_changeset_id'] .= $artifact_link->getLastChangesetId() .','; |
||
| 476 | } |
||
| 477 | } |
||
| 478 | |||
| 479 | $projects = array(); |
||
| 480 | $this_project_id = $this->getTracker()->getProject()->getGroupId(); |
||
| 481 | foreach ($ids as $tracker_id => $matching_ids) { |
||
| 482 | //remove last coma |
||
| 483 | $matching_ids['id'] = substr($matching_ids['id'], 0, -1); |
||
| 484 | $matching_ids['last_changeset_id'] = substr($matching_ids['last_changeset_id'], 0, -1); |
||
| 485 | |||
| 486 | $tracker = $this->getTrackerFactory()->getTrackerById($tracker_id); |
||
| 487 | $project = $tracker->getProject(); |
||
| 488 | if ($tracker->userCanView()) { |
||
| 489 | $trf = Tracker_ReportFactory::instance(); |
||
| 490 | $report = $trf->getDefaultReportsByTrackerId($tracker->getId()); |
||
| 491 | if ($report) { |
||
| 492 | $renderers = $report->getRenderers(); |
||
| 493 | $renderer_table_found = false; |
||
| 494 | // looking for the first table renderer |
||
| 495 | foreach ($renderers as $renderer) { |
||
| 496 | if ($renderer->getType() === Tracker_Report_Renderer::TABLE) { |
||
| 497 | $projects[$project->getGroupId()][$tracker_id] = array( |
||
| 498 | 'project' => $project, |
||
| 499 | 'tracker' => $tracker, |
||
| 500 | 'report' => $report, |
||
| 501 | 'renderer' => $renderer, |
||
| 502 | 'matching_ids' => $matching_ids, |
||
| 503 | ); |
||
| 504 | $renderer_table_found = true; |
||
| 505 | break; |
||
| 506 | } |
||
| 507 | } |
||
| 508 | if ( ! $renderer_table_found) { |
||
| 509 | $html .= $GLOBALS['Language']->getText('plugin_tracker', 'no_reports_available'); |
||
| 510 | } |
||
| 511 | } else { |
||
| 512 | $html .= $GLOBALS['Language']->getText('plugin_tracker', 'no_reports_available'); |
||
| 513 | } |
||
| 514 | } |
||
| 515 | } |
||
| 516 | |||
| 517 | foreach ($projects as $trackers) { |
||
| 518 | foreach ($trackers as $t) { |
||
| 519 | extract($t); |
||
| 520 | |||
| 521 | $html .= '<div class="tracker-form-element-artifactlink-trackerpanel">'; |
||
| 522 | |||
| 523 | $project_name = ''; |
||
| 524 | if ($project->getGroupId() != $this_project_id) { |
||
| 525 | $project_name = ' (<abbr title="'. $hp->purify($project->getPublicName(), CODENDI_PURIFIER_CONVERT_HTML) .'">'; |
||
| 526 | $project_name .= $hp->purify($project->getUnixName(), CODENDI_PURIFIER_CONVERT_HTML); |
||
| 527 | $project_name .= '</abbr>)'; |
||
| 528 | } |
||
| 529 | $html .= '<h2 class="tracker-form-element-artifactlink-tracker_'. $tracker->getId() .'">'; |
||
| 530 | $html .= $hp->purify($tracker->getName(), CODENDI_PURIFIER_CONVERT_HTML) . $project_name; |
||
| 531 | $html .= '</h2>'; |
||
| 532 | if ($from_aid == null) { |
||
| 533 | $html .= $renderer->fetchAsArtifactLink($matching_ids, $this->getId(), $read_only, $prefill_removed_values, false); |
||
| 534 | } else { |
||
| 535 | $html .= $renderer->fetchAsArtifactLink($matching_ids, $this->getId(), $read_only, $prefill_removed_values, false, $from_aid); |
||
| 536 | } |
||
| 537 | $html .= '</div>'; |
||
| 538 | } |
||
| 539 | } |
||
| 540 | } else { |
||
| 541 | $html .= $this->getNoValueLabel(); |
||
| 542 | } |
||
| 543 | $html .= '</div>'; |
||
| 544 | |||
| 545 | if ($reverse_artifact_links) { |
||
| 546 | $html .= '</div>'; |
||
| 547 | } |
||
| 548 | $html .= '</div>'; |
||
| 549 | |||
| 550 | return $html; |
||
| 551 | } |
||
| 552 | |||
| 553 | /** |
||
| 554 | * |
||
| 555 | * @param boolean $reverse_artifact_links |
||
| 556 | */ |
||
| 557 | private function getWidgetTitle($reverse_artifact_links) { |
||
| 558 | if ($reverse_artifact_links) { |
||
| 559 | return $GLOBALS['Language']->getText('plugin_tracker_artifact', 'formelement_artifactlink_reverse_title'); |
||
| 560 | } |
||
| 561 | |||
| 562 | return $GLOBALS['Language']->getText('plugin_tracker_artifact', 'formelement_artifactlink_title'); |
||
| 563 | |||
| 564 | } |
||
| 565 | |||
| 566 | /** |
||
| 567 | * Process the request |
||
| 568 | * |
||
| 569 | * @param Tracker_IDisplayTrackerLayout $layout Displays the page header and footer |
||
| 570 | * @param Codendi_Request $request The data coming from the user |
||
| 571 | * @param PFUser $current_user The user who mades the request |
||
| 572 | * |
||
| 573 | * @return void |
||
| 574 | */ |
||
| 575 | public function process(Tracker_IDisplayTrackerLayout $layout, $request, $current_user) { |
||
| 576 | switch ($request->get('func')) { |
||
| 577 | case 'fetch-artifacts': |
||
| 578 | $read_only = false; |
||
| 579 | $prefill_removed_values = array(); |
||
| 580 | $only_rows = true; |
||
| 581 | |||
| 582 | $this_project_id = $this->getTracker()->getProject()->getGroupId(); |
||
| 583 | $hp = Codendi_HTMLPurifier::instance(); |
||
| 584 | |||
| 585 | $ugroups = $current_user->getUgroups($this_project_id, array()); |
||
| 586 | |||
| 587 | $ids = $request->get('ids'); //2, 14, 15 |
||
| 588 | $tracker = array(); |
||
| 589 | $result = array(); |
||
| 590 | //We must retrieve the last changeset ids of each artifact id. |
||
| 591 | $dao = new Tracker_ArtifactDao(); |
||
| 592 | foreach($dao->searchLastChangesetIds($ids, $ugroups, $current_user->isSuperUser()) as $matching_ids) { |
||
| 593 | $tracker_id = $matching_ids['tracker_id']; |
||
| 594 | $tracker = $this->getTrackerFactory()->getTrackerById($tracker_id); |
||
| 595 | $project = $tracker->getProject(); |
||
| 596 | |||
| 597 | if ($tracker->userCanView()) { |
||
| 598 | $trf = Tracker_ReportFactory::instance(); |
||
| 599 | $report = $trf->getDefaultReportsByTrackerId($tracker->getId()); |
||
| 600 | if ($report) { |
||
| 601 | $renderers = $report->getRenderers(); |
||
| 602 | // looking for the first table renderer |
||
| 603 | foreach ($renderers as $renderer) { |
||
| 604 | if ($renderer->getType() === Tracker_Report_Renderer::TABLE) { |
||
| 605 | $key = $this->id .'_'. $report->id .'_'. $renderer->getId(); |
||
| 606 | $result[$key] = $renderer->fetchAsArtifactLink($matching_ids, $this->getId(), $read_only, $prefill_removed_values, $only_rows); |
||
| 607 | $head = '<div class="tracker-form-element-artifactlink-trackerpanel">'; |
||
| 608 | |||
| 609 | $project_name = ''; |
||
| 610 | if ($project->getGroupId() != $this_project_id) { |
||
| 611 | $project_name = ' (<abbr title="'. $hp->purify($project->getPublicName(), CODENDI_PURIFIER_CONVERT_HTML) .'">'; |
||
| 612 | $project_name .= $hp->purify($project->getUnixName(), CODENDI_PURIFIER_CONVERT_HTML); |
||
| 613 | $project_name .= '</abbr>)'; |
||
| 614 | } |
||
| 615 | $head .= '<h2 class="tracker-form-element-artifactlink-tracker_'. $tracker->getId() .'">'; |
||
| 616 | $head .= $hp->purify($tracker->getName(), CODENDI_PURIFIER_CONVERT_HTML) . $project_name; |
||
| 617 | $head .= '</h2>'; |
||
| 618 | //if ($artifact) { |
||
| 619 | // $title = $hp->purify('link a '. $tracker->getItemName(), CODENDI_PURIFIER_CONVERT_HTML); |
||
| 620 | // $head .= '<a href="'.TRACKER_BASE_URL.'/?tracker='.$tracker_id.'&func=new-artifact-link&id='.$artifact->getId().'" class="tracker-form-element-artifactlink-link-new-artifact">'. 'create a new '.$hp->purify($tracker->getItemName(), CODENDI_PURIFIER_CONVERT_HTML) .'</a>'; |
||
| 621 | //} |
||
| 622 | $result[$key]['head'] = $head . $result[$key]['head']; |
||
| 623 | break; |
||
| 624 | } |
||
| 625 | } |
||
| 626 | } |
||
| 627 | } |
||
| 628 | } |
||
| 629 | if ($result) { |
||
| 630 | $head = array(); |
||
| 631 | $rows = array(); |
||
| 632 | foreach($result as $key => $value) { |
||
| 633 | $head[$key] = $value["head"]; |
||
| 634 | $rows[$key] = $value["rows"]; |
||
| 635 | } |
||
| 636 | header('Content-type: application/json'); |
||
| 637 | echo json_encode(array('head' => $head, 'rows' => $rows)); |
||
| 638 | } |
||
| 639 | exit(); |
||
| 640 | break; |
||
| 641 | case 'fetch-aggregates': |
||
| 642 | $read_only = false; |
||
| 643 | $prefill_removed_values = array(); |
||
| 644 | $only_rows = true; |
||
| 645 | $only_one_column = false; |
||
| 646 | $extracolumn = Tracker_Report_Renderer_Table::EXTRACOLUMN_UNLINK; |
||
| 647 | $read_only = true; |
||
| 648 | $use_data_from_db = false; |
||
| 649 | |||
| 650 | $ugroups = $current_user->getUgroups($this->getTracker()->getGroupId(), array()); |
||
| 651 | $ids = $request->get('ids'); //2, 14, 15 |
||
| 652 | $tracker = array(); |
||
| 653 | $json = array('tabs' => array()); |
||
| 654 | $dao = new Tracker_ArtifactDao(); |
||
| 655 | foreach ($dao->searchLastChangesetIds($ids, $ugroups, $current_user->isSuperUser()) as $matching_ids) { |
||
| 656 | $tracker_id = $matching_ids['tracker_id']; |
||
| 657 | $tracker = $this->getTrackerFactory()->getTrackerById($tracker_id); |
||
| 658 | $project = $tracker->getProject(); |
||
| 659 | if ($tracker->userCanView()) { |
||
| 660 | $trf = Tracker_ReportFactory::instance(); |
||
| 661 | $report = $trf->getDefaultReportsByTrackerId($tracker->getId()); |
||
| 662 | if ($report) { |
||
| 663 | $renderers = $report->getRenderers(); |
||
| 664 | // looking for the first table renderer |
||
| 665 | foreach ($renderers as $renderer) { |
||
| 666 | if ($renderer->getType() === Tracker_Report_Renderer::TABLE) { |
||
| 667 | $key = $this->id . '_' . $report->id . '_' . $renderer->getId(); |
||
| 668 | $columns = $renderer->getTableColumns($only_one_column, $use_data_from_db); |
||
| 669 | $extracted_fields = $renderer->extractFieldsFromColumns($columns); |
||
| 670 | $json['tabs'][] = array( |
||
| 671 | 'key' => $key, |
||
| 672 | 'src' => $renderer->fetchAggregates($matching_ids, $extracolumn, $only_one_column,$columns, $extracted_fields, $use_data_from_db, $read_only), |
||
| 673 | ); |
||
| 674 | break; |
||
| 675 | } |
||
| 676 | } |
||
| 677 | } |
||
| 678 | } |
||
| 679 | } |
||
| 680 | header('Content-type: application/json'); |
||
| 681 | echo json_encode($json); |
||
| 682 | exit(); |
||
| 683 | break; |
||
| 684 | default: |
||
| 685 | parent::process($layout, $request, $current_user); |
||
| 686 | break; |
||
| 687 | } |
||
| 688 | } |
||
| 689 | |||
| 690 | /** |
||
| 691 | * Fetch the html widget for the field |
||
| 692 | * |
||
| 693 | * @param string $name The name, if any |
||
| 694 | * @param array $artifact_links The current artifact links |
||
| 695 | * @param string $prefill_new_values Prefill new values field (what the user has submitted, if any) |
||
| 696 | * @param bool $read_only True if the user can't add or remove links |
||
| 697 | * |
||
| 698 | * @return string html |
||
| 699 | */ |
||
| 700 | protected function fetchHtmlWidgetMasschange($name, $artifact_links, $prefill_new_values, $read_only) { |
||
| 701 | $html = ''; |
||
| 702 | $html_name_new = ''; |
||
| 703 | if ($name) { |
||
| 704 | $html_name_new = 'name="'. $name .'[new_values]"'; |
||
| 705 | } |
||
| 706 | $hp = Codendi_HTMLPurifier::instance(); |
||
| 707 | if (!$read_only) { |
||
| 708 | $html .= '<input type="text" |
||
| 709 | '. $html_name_new .' |
||
| 710 | value="'. $hp->purify($prefill_new_values, CODENDI_PURIFIER_CONVERT_HTML) .'" |
||
| 711 | title="' . $GLOBALS['Language']->getText('plugin_tracker_artifact', 'formelement_artifactlink_help') . '" />'; |
||
| 712 | $html .= '<br />'; |
||
| 713 | } |
||
| 714 | if ($artifact_links) { |
||
| 715 | $html .= '<ul class="tracker-form-element-artifactlink-list">'; |
||
| 716 | foreach ($artifact_links as $artifact_link_info) { |
||
| 717 | $html .= '<li>'; |
||
| 718 | $html .= $artifact_link_info->getUrl(); |
||
| 719 | $html .= '</li>'; |
||
| 720 | } |
||
| 721 | $html .= '</ul>'; |
||
| 722 | } |
||
| 723 | return $html; |
||
| 724 | } |
||
| 725 | |||
| 726 | /** |
||
| 727 | * Fetch the html code to display the field value in artifact |
||
| 728 | * |
||
| 729 | * @param Tracker_Artifact $artifact The artifact |
||
| 730 | * @param Tracker_Artifact_ChangesetValue $value The actual value of the field |
||
| 731 | * @param array $submitted_values The value already submitted by the user |
||
| 732 | * |
||
| 733 | * @return string |
||
| 734 | */ |
||
| 735 | protected function fetchArtifactValue(Tracker_Artifact $artifact, Tracker_Artifact_ChangesetValue $value = null, $submitted_values = array()) { |
||
| 736 | $links_tab = $this->fetchLinks($artifact, $value, $submitted_values); |
||
| 737 | $reverse_links_tab = $this->fetchReverseLinks($artifact); |
||
| 738 | |||
| 739 | return $links_tab . $reverse_links_tab; |
||
| 740 | } |
||
| 741 | |||
| 742 | private function fetchLinks(Tracker_Artifact $artifact, Tracker_Artifact_ChangesetValue $value = null, $submitted_values = array()) { |
||
| 743 | $artifact_links = array(); |
||
| 744 | if ($value != null) { |
||
| 745 | $artifact_links = $value->getValue(); |
||
| 746 | } |
||
| 747 | |||
| 748 | if (! empty($submitted_values) && isset($submitted_values[0]) && is_array($submitted_values[0]) && isset($submitted_values[0][$this->getId()])) { |
||
| 749 | $submitted_value = $submitted_values[0][$this->getId()]; |
||
| 750 | } |
||
| 751 | |||
| 752 | $prefill_new_values = ''; |
||
| 753 | if (isset($submitted_value['new_values'])) { |
||
| 754 | $prefill_new_values = $submitted_value['new_values']; |
||
| 755 | } |
||
| 756 | |||
| 757 | $prefill_removed_values = array(); |
||
| 758 | if (isset($submitted_value['removed_values'])) { |
||
| 759 | $prefill_removed_values = $submitted_value['removed_values']; |
||
| 760 | } |
||
| 761 | |||
| 762 | $read_only = false; |
||
| 763 | $name = 'artifact['. $this->id .']'; |
||
| 764 | $from_aid = $artifact->getId(); |
||
| 765 | $prefill_parent = ''; |
||
| 766 | |||
| 767 | return $this->fetchHtmlWidget( |
||
| 768 | $artifact, |
||
| 769 | $name, |
||
| 770 | $artifact_links, |
||
| 771 | $prefill_new_values, |
||
| 772 | $prefill_removed_values, |
||
| 773 | $read_only, |
||
| 774 | $prefill_parent, |
||
| 775 | $from_aid |
||
| 776 | ); |
||
| 777 | } |
||
| 778 | |||
| 779 | private function fetchReverseLinks(Tracker_Artifact $artifact) { |
||
| 780 | $reverse_links = $this->getReverseLinks($artifact->getId()); |
||
| 781 | |||
| 782 | return $this->fetchHtmlWidget( |
||
| 783 | $artifact, |
||
| 784 | '', |
||
| 785 | $reverse_links, |
||
| 786 | '', |
||
| 787 | '', |
||
| 788 | '', |
||
| 789 | true, |
||
| 790 | null, |
||
| 791 | true |
||
| 792 | ); |
||
| 793 | } |
||
| 794 | |||
| 795 | /** |
||
| 796 | * Fetch the html code to display the field value in artifact in read only mode |
||
| 797 | * |
||
| 798 | * @param Tracker_Artifact $artifact The artifact |
||
| 799 | * @param Tracker_Artifact_ChangesetValue $value The actual value of the field |
||
| 800 | * |
||
| 801 | * @return string |
||
| 802 | */ |
||
| 803 | public function fetchArtifactValueReadOnly(Tracker_Artifact $artifact, Tracker_Artifact_ChangesetValue $value = null) { |
||
| 804 | $links_tab_read_only = $this->fetchLinksReadOnly($artifact, $value); |
||
| 805 | $reverse_links_tab = $this->fetchReverseLinks($artifact); |
||
| 806 | |||
| 807 | return $links_tab_read_only . $reverse_links_tab; |
||
| 808 | } |
||
| 809 | |||
| 810 | public function fetchArtifactCopyMode(Tracker_Artifact $artifact, $submitted_values = array()) { |
||
| 813 | |||
| 814 | public function fetchArtifactValueWithEditionFormIfEditable(Tracker_Artifact $artifact, Tracker_Artifact_ChangesetValue $value = null, $submitted_values = array()) { |
||
| 817 | |||
| 818 | public function getHiddenArtifactValueForEdition(Tracker_Artifact $artifact, Tracker_Artifact_ChangesetValue $value = null) { |
||
| 821 | |||
| 822 | private function fetchLinksReadOnly(Tracker_Artifact $artifact, Tracker_Artifact_ChangesetValue $value = null) { |
||
| 823 | $artifact_links = array(); |
||
| 824 | |||
| 825 | if ($value != null) { |
||
| 826 | $artifact_links = $value->getValue(); |
||
| 827 | } |
||
| 828 | |||
| 829 | $read_only = true; |
||
| 830 | $name = ''; |
||
| 831 | $prefill_new_values = ''; |
||
| 832 | $prefill_removed_values = array(); |
||
| 833 | $prefill_parent = ''; |
||
| 834 | |||
| 835 | return $this->fetchHtmlWidget( |
||
| 836 | $artifact, |
||
| 837 | $name, |
||
| 838 | $artifact_links, |
||
| 839 | $prefill_new_values, |
||
| 840 | $prefill_removed_values, |
||
| 841 | $prefill_parent, |
||
| 842 | $read_only |
||
| 843 | ); |
||
| 844 | } |
||
| 845 | |||
| 846 | /** |
||
| 847 | * Fetch the html code to display the field value in new artifact submission form |
||
| 848 | * |
||
| 849 | * @param array $submitted_values the values already submitted |
||
| 850 | * |
||
| 851 | * @return string html |
||
| 852 | */ |
||
| 853 | protected function fetchSubmitValue($submitted_values = array()) { |
||
| 854 | $html = ''; |
||
| 855 | $prefill_new_values = ''; |
||
| 856 | if (isset($submitted_values[$this->getId()]['new_values'])) { |
||
| 857 | $prefill_new_values = $submitted_values[$this->getId()]['new_values']; |
||
| 858 | } else if ($this->hasDefaultValue()) { |
||
| 859 | $prefill_new_values = $this->getDefaultValue(); |
||
| 860 | } |
||
| 861 | $prefill_parent = ''; |
||
| 862 | if (isset($submitted_values[$this->getId()]['parent'])) { |
||
| 863 | $prefill_parent = $submitted_values[$this->getId()]['parent']; |
||
| 864 | } |
||
| 865 | $read_only = false; |
||
| 866 | $name = 'artifact['. $this->id .']'; |
||
| 867 | $prefill_removed_values = array(); |
||
| 868 | $artifact_links = array(); |
||
| 869 | |||
| 870 | // Well, shouldn't be here but API doesn't provide a Null Artifact on creation yet |
||
| 871 | // Here to avoid having to pass null arg for fetchHtmlWidget |
||
| 872 | $artifact = new Tracker_Artifact(-1, $this->tracker_id, $this->getCurrentUser()->getId(), 0, false); |
||
| 873 | |||
| 874 | return $this->fetchHtmlWidget($artifact, $name, $artifact_links, $prefill_new_values, $prefill_removed_values, $prefill_parent, $read_only); |
||
| 875 | } |
||
| 876 | |||
| 877 | /** |
||
| 878 | * Fetch the html code to display the field value in masschange submission form |
||
| 879 | * |
||
| 880 | * @param array $submitted_values the values already submitted |
||
| 881 | * |
||
| 882 | * @return string html |
||
| 883 | */ |
||
| 884 | protected function fetchSubmitValueMasschange() { |
||
| 885 | $html = ''; |
||
| 886 | $prefill_new_values = $GLOBALS['Language']->getText('global','unchanged'); |
||
| 887 | $read_only = false; |
||
| 888 | $name = 'artifact['. $this->id .']'; |
||
| 889 | $artifact_links = array(); |
||
| 890 | |||
| 891 | return $this->fetchHtmlWidgetMasschange($name, $artifact_links, $prefill_new_values, $read_only); |
||
| 892 | } |
||
| 893 | |||
| 894 | |||
| 895 | /** |
||
| 896 | * Fetch the html code to display the field value in tooltip |
||
| 897 | * |
||
| 898 | * @param Tracker_Artifact $artifact |
||
| 899 | * @param Tracker_Artifact_ChangesetValue $value The changeset value of the field |
||
| 900 | * |
||
| 901 | * @return string |
||
| 902 | */ |
||
| 903 | protected function fetchTooltipValue(Tracker_Artifact $artifact, Tracker_Artifact_ChangesetValue $value = null) { |
||
| 904 | $html = ''; |
||
| 905 | if ($value != null) { |
||
| 906 | $html = '<ul>'; |
||
| 907 | $artifact_links = $value->getValue(); |
||
| 908 | foreach($artifact_links as $artifact_link_info) { |
||
| 909 | $html .= '<li>' . $artifact_link_info->getLabel() . '</li>'; |
||
| 910 | } |
||
| 911 | $html .= '</ul>'; |
||
| 912 | } |
||
| 913 | return $html; |
||
| 914 | } |
||
| 915 | |||
| 916 | /** |
||
| 917 | * @return Tracker_FormElement_Field_Value_ArtifactLinkDao |
||
| 918 | */ |
||
| 919 | protected function getValueDao() { |
||
| 922 | |||
| 923 | /** |
||
| 924 | * Fetch the html code to display the field value in artifact |
||
| 925 | * |
||
| 926 | * @param Tracker_Artifact $artifact The artifact |
||
| 927 | * @param PFUser $user The user who will receive the email |
||
| 928 | * @param Tracker_Artifact_ChangesetValue $value The actual value of the field |
||
| 929 | * @param array $submitted_values The value already submitted by the user |
||
| 930 | * |
||
| 931 | * @return string |
||
| 932 | */ |
||
| 933 | public function fetchMailArtifactValue( |
||
| 934 | Tracker_Artifact $artifact, |
||
| 935 | PFUser $user, |
||
| 936 | Tracker_Artifact_ChangesetValue $value = null, |
||
| 937 | $format='text' |
||
| 938 | ) { |
||
| 939 | if ( empty($value) || !$value->getValue()) { |
||
| 940 | return '-'; |
||
| 941 | } |
||
| 942 | $output = ''; |
||
| 943 | switch($format) { |
||
| 944 | case 'html': |
||
| 945 | $artifactlink_infos = $value->getValue(); |
||
| 946 | $url = array(); |
||
| 947 | foreach ($artifactlink_infos as $artifactlink_info) { |
||
| 948 | if ($artifactlink_info->userCanView($user)) { |
||
| 949 | $url[] = $artifactlink_info->getUrl(); |
||
| 950 | } |
||
| 951 | } |
||
| 952 | return implode(' , ', $url); |
||
| 953 | default: |
||
| 954 | $output = PHP_EOL; |
||
| 955 | $artifactlink_infos = $value->getValue(); |
||
| 956 | foreach ($artifactlink_infos as $artifactlink_info) { |
||
| 957 | if ($artifactlink_info->userCanView($user)) { |
||
| 958 | $output .= $artifactlink_info->getLabel(); |
||
| 959 | $output .= PHP_EOL; |
||
| 960 | } |
||
| 961 | } |
||
| 962 | break; |
||
| 963 | } |
||
| 964 | return $output; |
||
| 965 | } |
||
| 966 | |||
| 967 | /** |
||
| 968 | * Fetch the value to display changes in followups |
||
| 969 | * |
||
| 970 | * @param Tracker_ $artifact |
||
| 971 | * @param array $from the value(s) *before* |
||
| 972 | * @param array $to the value(s) *after* |
||
| 973 | * |
||
| 974 | * @return string |
||
| 975 | */ |
||
| 976 | public function fetchFollowUp($artifact, $from, $to) { |
||
| 979 | |||
| 980 | /** |
||
| 981 | * Fetch the value in a specific changeset |
||
| 982 | * |
||
| 983 | * @param Tracker_Artifact_Changeset $changeset |
||
| 984 | * |
||
| 985 | * @return string |
||
| 986 | */ |
||
| 987 | public function fetchRawValueFromChangeset($changeset) { |
||
| 990 | |||
| 991 | /** |
||
| 992 | * Get the value of this field |
||
| 993 | * |
||
| 994 | * @param Tracker_Artifact_Changeset $changeset The changeset (needed in only few cases like 'lud' field) |
||
| 995 | * @param int $value_id The id of the value |
||
| 996 | * @param boolean $has_changed If the changeset value has changed from the rpevious one |
||
| 997 | * |
||
| 998 | * @return Tracker_Artifact_ChangesetValue or null if not found |
||
| 999 | */ |
||
| 1000 | public function getChangesetValue($changeset, $value_id, $has_changed) { |
||
| 1001 | $rows = $this->getValueDao()->searchById($value_id, $this->id); |
||
| 1002 | $artifact_links = $this->getArtifactLinkInfos($rows); |
||
| 1003 | $reverse_artifact_links = array(); |
||
| 1004 | |||
| 1005 | if ($changeset) { |
||
| 1006 | $reverse_artifact_links = $this->getReverseLinks($changeset->getArtifact()->getId()); |
||
| 1007 | } |
||
| 1008 | |||
| 1009 | return new Tracker_Artifact_ChangesetValue_ArtifactLink($value_id, $this, $has_changed, $artifact_links, $reverse_artifact_links); |
||
| 1010 | } |
||
| 1011 | |||
| 1012 | |||
| 1013 | private function getReverseLinks($artifact_id) { |
||
| 1014 | $links_data = $this->getValueDao()->searchReverseLinksById($artifact_id); |
||
| 1015 | |||
| 1016 | return $this->getArtifactLinkInfos($links_data); |
||
| 1017 | } |
||
| 1018 | |||
| 1019 | private function getArtifactLinkInfos($data) { |
||
| 1020 | $artifact_links = array(); |
||
| 1021 | while ($row = $data->getRow()) { |
||
| 1022 | $artifact_links[$row['artifact_id']] = new Tracker_ArtifactLinkInfo($row['artifact_id'], $row['keyword'], $row['group_id'], $row['tracker_id'], $row['last_changeset_id']); |
||
| 1023 | } |
||
| 1024 | |||
| 1025 | return $artifact_links; |
||
| 1026 | } |
||
| 1027 | |||
| 1028 | /** |
||
| 1029 | * @return array |
||
| 1030 | */ |
||
| 1031 | protected $artifact_links_by_changeset = array(); |
||
| 1032 | |||
| 1033 | /** |
||
| 1034 | * |
||
| 1035 | * @param Integer $changeset_id |
||
| 1036 | * |
||
| 1037 | * @return Tracker_ArtifactLinkInfo[] |
||
| 1038 | */ |
||
| 1039 | protected function getChangesetValues($changeset_id) { |
||
| 1040 | if (!isset($this->artifact_links_by_changeset[$changeset_id])) { |
||
| 1041 | $this->artifact_links_by_changeset[$changeset_id] = array(); |
||
| 1042 | |||
| 1043 | $da = CodendiDataAccess::instance(); |
||
| 1044 | $field_id = $da->escapeInt($this->id); |
||
| 1045 | $changeset_id = $da->escapeInt($changeset_id); |
||
| 1046 | $sql = "SELECT cv.changeset_id, cv.has_changed, val.*, a.tracker_id, a.last_changeset_id |
||
| 1047 | FROM tracker_changeset_value_artifactlink AS val |
||
| 1048 | INNER JOIN tracker_artifact AS a ON(a.id = val.artifact_id) |
||
| 1049 | INNER JOIN tracker_changeset_value AS cv |
||
| 1050 | ON ( val.changeset_value_id = cv.id |
||
| 1051 | AND cv.field_id = $field_id |
||
| 1052 | AND cv.changeset_id = $changeset_id |
||
| 1053 | ) |
||
| 1054 | ORDER BY val.artifact_id"; |
||
| 1055 | $dao = new DataAccessObject(); |
||
| 1056 | foreach ($dao->retrieve($sql) as $row) { |
||
| 1057 | $this->artifact_links_by_changeset[$row['changeset_id']][] = new Tracker_ArtifactLinkInfo( |
||
| 1058 | $row['artifact_id'], |
||
| 1059 | $row['keyword'], |
||
| 1060 | $row['group_id'], |
||
| 1061 | $row['tracker_id'], |
||
| 1062 | $row['last_changeset_id'] |
||
| 1063 | ); |
||
| 1064 | } |
||
| 1065 | } |
||
| 1066 | return $this->artifact_links_by_changeset[$changeset_id]; |
||
| 1067 | } |
||
| 1068 | |||
| 1069 | /** |
||
| 1070 | * Check if there are changes between old and new value for this field |
||
| 1071 | * |
||
| 1072 | * @param Tracker_Artifact_ChangesetValue $old_value The data stored in the db |
||
| 1073 | * @param array $new_value array of artifact ids |
||
| 1074 | * |
||
| 1075 | * @return bool true if there are differences |
||
| 1076 | */ |
||
| 1077 | public function hasChanges(Tracker_Artifact_ChangesetValue_ArtifactLink $old_value, $new_value) { |
||
| 1080 | |||
| 1081 | /** |
||
| 1082 | * @return the label of the field (mainly used in admin part) |
||
| 1083 | */ |
||
| 1084 | public static function getFactoryLabel() { |
||
| 1087 | |||
| 1088 | /** |
||
| 1089 | * @return the description of the field (mainly used in admin part) |
||
| 1090 | */ |
||
| 1091 | public static function getFactoryDescription() { |
||
| 1094 | |||
| 1095 | /** |
||
| 1096 | * @return the path to the icon |
||
| 1097 | */ |
||
| 1098 | public static function getFactoryIconUseIt() { |
||
| 1101 | |||
| 1102 | /** |
||
| 1103 | * @return the path to the icon |
||
| 1104 | */ |
||
| 1105 | public static function getFactoryIconCreate() { |
||
| 1108 | |||
| 1109 | /** |
||
| 1110 | * @return bool say if the field is a unique one |
||
| 1111 | */ |
||
| 1112 | public static function getFactoryUniqueField() { |
||
| 1115 | |||
| 1116 | /** |
||
| 1117 | * Say if the value is valid. If not valid set the internal has_error to true. |
||
| 1118 | * |
||
| 1119 | * @param Tracker_Artifact $artifact The artifact |
||
| 1120 | * @param array $value data coming from the request. |
||
| 1121 | * |
||
| 1122 | * @return bool true if the value is considered ok |
||
| 1123 | */ |
||
| 1124 | public function isValid(Tracker_Artifact $artifact, $value) { |
||
| 1125 | $this->has_errors = ! $this->validate($artifact, $value); |
||
| 1126 | |||
| 1127 | return ! $this->has_errors; |
||
| 1128 | } |
||
| 1129 | |||
| 1130 | /** |
||
| 1131 | * Validate a required field |
||
| 1132 | * |
||
| 1133 | * @param Tracker_Artifact $artifact The artifact to check |
||
| 1134 | * @param mixed $value The submitted value |
||
| 1135 | * |
||
| 1136 | * @return boolean true on success or false on failure |
||
| 1137 | */ |
||
| 1138 | public function isValidRegardingRequiredProperty(Tracker_Artifact $artifact, $value) { |
||
| 1139 | if ( (! is_array($value) || empty($value['new_values'])) && $this->isRequired()) { |
||
| 1140 | if ( ! $this->isEmpty($value, $artifact)) { |
||
| 1141 | // Field is required but there are values, so field is valid |
||
| 1142 | $this->has_errors = false; |
||
| 1143 | } else { |
||
| 1144 | $this->addRequiredError(); |
||
| 1145 | return false; |
||
| 1146 | } |
||
| 1147 | } |
||
| 1148 | |||
| 1149 | return true; |
||
| 1150 | } |
||
| 1151 | |||
| 1152 | /** |
||
| 1153 | * @return Array the ids |
||
| 1154 | */ |
||
| 1155 | private function getLastChangesetArtifactIds(Tracker_Artifact $artifact) { |
||
| 1156 | $lastChangeset = $artifact->getLastChangeset(); |
||
| 1157 | $ids = array(); |
||
| 1158 | if($lastChangeset) { |
||
| 1159 | $ids = $lastChangeset->getValue($this)->getArtifactIds(); |
||
| 1160 | } |
||
| 1161 | return $ids; |
||
| 1162 | } |
||
| 1163 | |||
| 1164 | /** |
||
| 1165 | * Say if the submitted value is empty |
||
| 1166 | * if no last changeset values and empty submitted values : empty |
||
| 1167 | * if not empty last changeset values and empty submitted values : not empty |
||
| 1168 | * if empty new values and not empty last changeset values and not empty removed values have the same size: empty |
||
| 1169 | * |
||
| 1170 | * @param array $submitted_value |
||
| 1171 | * @param Tracker_Artifact $artifact |
||
| 1172 | * |
||
| 1173 | * @return bool true if the submitted value is empty |
||
| 1174 | */ |
||
| 1175 | public function isEmpty($submitted_value, Tracker_Artifact $artifact) { |
||
| 1176 | $hasNoNewValues = empty($submitted_value['new_values']); |
||
| 1177 | $hasNoLastChangesetValues = true; |
||
| 1178 | $last_changeset_values = array(); |
||
| 1179 | $last_changeset = $this->getLastChangesetValue($artifact); |
||
| 1180 | |||
| 1181 | if ($last_changeset) { |
||
| 1182 | $last_changeset_values = $last_changeset->getArtifactIds(); |
||
| 1183 | $hasNoLastChangesetValues = empty($last_changeset_values); |
||
| 1184 | } |
||
| 1185 | |||
| 1186 | $hasLastChangesetValues = !$hasNoLastChangesetValues; |
||
| 1187 | |||
| 1188 | if (($hasNoLastChangesetValues && $hasNoNewValues) || |
||
| 1189 | ($hasLastChangesetValues && $hasNoNewValues |
||
| 1190 | && $this->allLastChangesetValuesRemoved($last_changeset_values, $submitted_value))) { |
||
| 1191 | return true; |
||
| 1192 | } |
||
| 1193 | return false; |
||
| 1194 | } |
||
| 1195 | |||
| 1196 | /** |
||
| 1197 | * Say if all values of the changeset have been removed |
||
| 1198 | * |
||
| 1199 | * @param array $last_changeset_values |
||
| 1200 | * @param array $submitted_value |
||
| 1201 | * |
||
| 1202 | * @return bool true if all values have been removed |
||
| 1203 | */ |
||
| 1204 | private function allLastChangesetValuesRemoved($last_changeset_values, $submitted_value) { |
||
| 1208 | |||
| 1209 | /** |
||
| 1210 | * Validate a value |
||
| 1211 | * |
||
| 1212 | * @param Tracker_Artifact $artifact The artifact |
||
| 1213 | * @param string $value data coming from the request. Should be artifact id separated by comma |
||
| 1214 | * |
||
| 1215 | * @return bool true if the value is considered ok |
||
| 1216 | */ |
||
| 1217 | protected function validate(Tracker_Artifact $artifact, $value) { |
||
| 1218 | $is_valid = true; |
||
| 1219 | if (! isset($value['new_values'])) { |
||
| 1220 | return $is_valid; |
||
| 1221 | } |
||
| 1222 | $new_values = $value['new_values']; |
||
| 1223 | if (trim($new_values) != '') { |
||
| 1224 | $r = $this->getRuleArtifactId(); |
||
| 1225 | $art_id_array = explode(',', $new_values); |
||
| 1226 | foreach ($art_id_array as $artifact_id) { |
||
| 1227 | $artifact_id = trim ($artifact_id); |
||
| 1228 | if ( ! $r->isValid($artifact_id)) { |
||
| 1229 | $is_valid = false; |
||
| 1230 | $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_tracker_common_artifact', 'error_artifactlink_value', array($this->getLabel(), $artifact_id))); |
||
| 1231 | } |
||
| 1232 | } |
||
| 1233 | } |
||
| 1234 | |||
| 1235 | return $is_valid; |
||
| 1236 | } |
||
| 1237 | |||
| 1238 | public function getRuleArtifactId() { |
||
| 1241 | |||
| 1242 | public function setArtifactFactory(Tracker_ArtifactFactory $artifact_factory) { |
||
| 1245 | |||
| 1246 | /** |
||
| 1247 | * @return Tracker_ArtifactFactory |
||
| 1248 | */ |
||
| 1249 | public function getArtifactFactory() { |
||
| 1250 | if (!$this->artifact_factory) { |
||
| 1251 | $this->artifact_factory = Tracker_ArtifactFactory::instance(); |
||
| 1252 | } |
||
| 1253 | return $this->artifact_factory; |
||
| 1254 | } |
||
| 1255 | |||
| 1256 | public function getTrackerFactory() { |
||
| 1259 | |||
| 1260 | protected function getTrackerChildrenFromHierarchy(Tracker $tracker) { |
||
| 1263 | |||
| 1264 | /** |
||
| 1265 | * @return Tracker_HierarchyFactory |
||
| 1266 | */ |
||
| 1267 | protected function getHierarchyFactory() { |
||
| 1270 | |||
| 1271 | /** |
||
| 1272 | * @see Tracker_FormElement_Field::postSaveNewChangeset() |
||
| 1273 | */ |
||
| 1274 | public function postSaveNewChangeset( |
||
| 1275 | Tracker_Artifact $artifact, |
||
| 1276 | PFUser $submitter, |
||
| 1277 | Tracker_Artifact_Changeset $new_changeset, |
||
| 1278 | Tracker_Artifact_Changeset $previous_changeset = null |
||
| 1279 | ) { |
||
| 1280 | $queue = new Tracker_FormElement_Field_ArtifactLink_PostSaveNewChangesetQueue(); |
||
| 1281 | $queue->add($this->getUpdateLinkingDirectionCommand()); |
||
| 1282 | $queue->add($this->getProcessChildrenTriggersCommand()); |
||
| 1283 | $queue->execute($artifact, $submitter, $new_changeset, $previous_changeset); |
||
| 1284 | } |
||
| 1285 | |||
| 1286 | /** |
||
| 1287 | * @protected for testing purpose |
||
| 1288 | */ |
||
| 1289 | protected function getProcessChildrenTriggersCommand() { |
||
| 1290 | return new Tracker_FormElement_Field_ArtifactLink_ProcessChildrenTriggersCommand( |
||
| 1291 | $this, |
||
| 1292 | $this->getWorkflowFactory()->getTriggerRulesManager() |
||
| 1293 | ); |
||
| 1294 | } |
||
| 1295 | |||
| 1296 | private function getUpdateLinkingDirectionCommand() { |
||
| 1299 | |||
| 1300 | /** |
||
| 1301 | * Return true if $artifact_to_check is "parent of" $artifact_reference |
||
| 1302 | * |
||
| 1303 | * @todo: take planning into account |
||
| 1304 | * |
||
| 1305 | * When $artifact_to_check is a Release |
||
| 1306 | * And $artifact_reference is a Sprint |
||
| 1307 | * And Release -> Sprint (in tracker hierarchy) |
||
| 1308 | * Then return True |
||
| 1309 | * |
||
| 1310 | * @param Tracker_Artifact $artifact_to_check |
||
| 1311 | * @param Tracker_Artifact $artifact_reference |
||
| 1312 | * |
||
| 1313 | * @return Boolean |
||
| 1314 | */ |
||
| 1315 | protected function isSourceOfAssociation(Tracker_Artifact $artifact_to_check, Tracker_Artifact $artifact_reference) { |
||
| 1319 | |||
| 1320 | /** |
||
| 1321 | * Save the value submitted by the user in the new changeset |
||
| 1322 | * |
||
| 1323 | * @param Tracker_Artifact $artifact The artifact |
||
| 1324 | * @param Tracker_Artifact_Changeset $old_changeset The old changeset. null if it is the first one |
||
| 1325 | * @param int $new_changeset_id The id of the new changeset |
||
| 1326 | * @param mixed $submitted_value The value submitted by the user |
||
| 1327 | * @param boolean $is_submission true if artifact submission, false if artifact update |
||
| 1328 | * |
||
| 1329 | * @return bool true if success |
||
| 1330 | */ |
||
| 1331 | public function saveNewChangeset(Tracker_Artifact $artifact, $old_changeset, $new_changeset_id, $submitted_value, PFUser $submitter, $is_submission = false, $bypass_permissions = false) { |
||
| 1335 | |||
| 1336 | /** |
||
| 1337 | * Verify (and update if needed) that the link between what submitted the user ($submitted_values) and |
||
| 1338 | * the current artifact is correct resp. the association definition. |
||
| 1339 | * |
||
| 1340 | * Given I defined following hierarchy: |
||
| 1341 | * Release |
||
| 1342 | * `-- Sprint |
||
| 1343 | * |
||
| 1344 | * If $artifact is a Sprint and I try to link a Release, this method detect |
||
| 1345 | * it and update the corresponding Release with a link toward current sprint |
||
| 1346 | * |
||
| 1347 | * @param Tracker_Artifact $artifact |
||
| 1348 | * @param Tracker_Artifact_Changeset $old_changeset |
||
| 1349 | * @param mixed $submitted_value |
||
| 1350 | * @param PFUser $submitter |
||
| 1351 | * |
||
| 1352 | * @return mixed The submitted value expurged from updated links |
||
| 1353 | */ |
||
| 1354 | protected function updateLinkingDirection(Tracker_Artifact $artifact, $old_changeset, $submitted_value, PFUser $submitter) { |
||
| 1355 | $previous_changesetvalue = $this->getPreviousChangesetValue($old_changeset); |
||
| 1356 | $artifacts = $this->getArtifactsFromChangesetValue($submitted_value, $previous_changesetvalue); |
||
| 1357 | $artifact_id_already_linked = array(); |
||
| 1358 | foreach ($artifacts as $artifact_to_add) { |
||
| 1359 | if ($this->isSourceOfAssociation($artifact_to_add, $artifact)) { |
||
| 1360 | $this->source_of_association[] = $artifact_to_add; |
||
| 1361 | $artifact_id_already_linked[] = $artifact_to_add->getId(); |
||
| 1362 | } |
||
| 1363 | } |
||
| 1364 | |||
| 1365 | return $this->removeArtifactsFromSubmittedValue($submitted_value, $artifact_id_already_linked); |
||
| 1366 | } |
||
| 1367 | |||
| 1368 | /** |
||
| 1369 | * Remove from user submitted artifact links the artifact ids that where already |
||
| 1370 | * linked after the direction checking |
||
| 1371 | * |
||
| 1372 | * Should be private to the class but almost impossible to test in the context |
||
| 1373 | * of saveNewChangeset. |
||
| 1374 | * |
||
| 1375 | * @param Array $submitted_value |
||
| 1376 | * @param Array $artifact_id_already_linked |
||
| 1377 | * |
||
| 1378 | * @return Array |
||
| 1379 | */ |
||
| 1380 | public function removeArtifactsFromSubmittedValue($submitted_value, array $artifact_id_already_linked) { |
||
| 1381 | $new_values = explode(',', $submitted_value['new_values']); |
||
| 1382 | $new_values = array_map('trim', $new_values); |
||
| 1383 | $new_values = array_diff($new_values, $artifact_id_already_linked); |
||
| 1384 | $submitted_value['new_values'] = implode(',', $new_values); |
||
| 1385 | return $submitted_value; |
||
| 1386 | } |
||
| 1387 | |||
| 1388 | protected function getArtifactsFromChangesetValue($value, $previous_changesetvalue = null) { |
||
| 1389 | $new_values = (string)$value['new_values']; |
||
| 1390 | $removed_values = isset($value['removed_values']) ? $value['removed_values'] : array(); |
||
| 1391 | // this array will be the one to save in the new changeset |
||
| 1392 | $artifact_ids = array(); |
||
| 1393 | if ($previous_changesetvalue != null) { |
||
| 1394 | $artifact_ids = $previous_changesetvalue->getArtifactIds(); |
||
| 1395 | // We remove artifact links that user wants to remove |
||
| 1396 | if (is_array($removed_values) && ! empty($removed_values)) { |
||
| 1397 | $artifact_ids = array_diff($artifact_ids, array_keys($removed_values)); |
||
| 1398 | } |
||
| 1399 | } |
||
| 1400 | |||
| 1401 | if (trim($new_values) != '') { |
||
| 1402 | $new_artifact_ids = array_diff(explode(',', $new_values), array_keys($removed_values)); |
||
| 1403 | // We add new links to existing ones |
||
| 1404 | foreach ($new_artifact_ids as $new_artifact_id) { |
||
| 1405 | if ( ! in_array($new_artifact_id, $artifact_ids)) { |
||
| 1406 | $artifact_ids[] = (int)$new_artifact_id; |
||
| 1407 | } |
||
| 1408 | } |
||
| 1409 | } |
||
| 1410 | |||
| 1411 | return $this->getArtifactFactory()->getArtifactsByArtifactIdList($artifact_ids); |
||
| 1412 | } |
||
| 1413 | |||
| 1414 | /** |
||
| 1415 | * Save the value and return the id |
||
| 1416 | * |
||
| 1417 | * @param Tracker_Artifact $artifact The artifact |
||
| 1418 | * @param int $changeset_value_id The id of the changeset_value |
||
| 1419 | * @param mixed $value The value submitted by the user |
||
| 1420 | * @param Tracker_Artifact_ChangesetValue $previous_changesetvalue The data previously stored in the db |
||
| 1421 | * |
||
| 1422 | */ |
||
| 1423 | protected function saveValue($artifact, $changeset_value_id, $value, Tracker_Artifact_ChangesetValue $previous_changesetvalue = null) { |
||
| 1424 | $dao = $this->getValueDao(); |
||
| 1425 | |||
| 1426 | foreach ($this->getArtifactIdsToLink($artifact, $value, $previous_changesetvalue) as $artifact_to_be_linked_by_tracker) { |
||
| 1427 | $tracker = $artifact_to_be_linked_by_tracker['tracker']; |
||
| 1428 | $dao->create( |
||
| 1429 | $changeset_value_id, |
||
| 1430 | $artifact_to_be_linked_by_tracker['ids'], |
||
| 1431 | $tracker->getItemName(), |
||
| 1432 | $tracker->getGroupId() |
||
| 1433 | ); |
||
| 1434 | } |
||
| 1435 | |||
| 1436 | return $this->updateCrossReferences($artifact, $value); |
||
| 1437 | } |
||
| 1438 | |||
| 1439 | /** @return array */ |
||
| 1440 | private function getArtifactIdsToLink(Tracker_Artifact $artifact, $value, Tracker_Artifact_ChangesetValue $previous_changesetvalue = null) { |
||
| 1441 | $all_artifacts_to_link = $this->getArtifactsFromChangesetValue( |
||
| 1442 | $value, |
||
| 1443 | $previous_changesetvalue |
||
| 1444 | ); |
||
| 1445 | |||
| 1446 | $all_artifact_to_be_linked = array(); |
||
| 1447 | foreach ($all_artifacts_to_link as $artifact_to_link) { |
||
| 1448 | if ($this->canLinkArtifacts($artifact, $artifact_to_link)) { |
||
| 1449 | $tracker = $artifact_to_link->getTracker(); |
||
| 1450 | |||
| 1451 | if (! isset($all_artifact_to_be_linked[$tracker->getId()])) { |
||
| 1452 | $all_artifact_to_be_linked[$tracker->getId()] = array( |
||
| 1453 | 'tracker' => $tracker, |
||
| 1454 | 'ids' => array() |
||
| 1455 | ); |
||
| 1456 | } |
||
| 1457 | |||
| 1458 | $all_artifact_to_be_linked[$tracker->getId()]['ids'][] = $artifact_to_link->getId(); |
||
| 1459 | } |
||
| 1460 | } |
||
| 1461 | |||
| 1462 | return $all_artifact_to_be_linked; |
||
| 1463 | } |
||
| 1464 | |||
| 1465 | private function canLinkArtifacts(Tracker_Artifact $src_artifact, Tracker_Artifact $artifact_to_link) { |
||
| 1468 | |||
| 1469 | /** |
||
| 1470 | * Update cross references of this field |
||
| 1471 | * |
||
| 1472 | * @param Tracker_Artifact $artifact the artifact that is currently updated |
||
| 1473 | * @param array $values the array of added and removed artifact links ($values['added_values'] is a string and $values['removed_values'] is an array of artifact ids |
||
| 1474 | * |
||
| 1475 | * @return boolean |
||
| 1476 | */ |
||
| 1477 | protected function updateCrossReferences(Tracker_Artifact $artifact, $values) { |
||
| 1478 | $update_ok = true; |
||
| 1479 | |||
| 1480 | foreach ($this->getAddedArtifactIds($values) as $added_artifact_id) { |
||
| 1481 | $update_ok = $update_ok && $this->insertCrossReference($artifact, $added_artifact_id); |
||
| 1482 | } |
||
| 1483 | foreach ($this->getRemovedArtifactIds($values) as $removed_artifact_id) { |
||
| 1484 | $update_ok = $update_ok && $this->removeCrossReference($artifact, $removed_artifact_id); |
||
| 1485 | } |
||
| 1486 | |||
| 1487 | return $update_ok; |
||
| 1488 | } |
||
| 1489 | |||
| 1490 | private function getAddedArtifactIds(array $values) { |
||
| 1491 | if (array_key_exists('new_values', $values)) { |
||
| 1492 | if (trim($values['new_values']) != '') { |
||
| 1493 | return array_map('intval', explode(',', $values['new_values'])); |
||
| 1494 | } |
||
| 1495 | } |
||
| 1496 | return array(); |
||
| 1497 | } |
||
| 1498 | |||
| 1499 | private function getRemovedArtifactIds(array $values) { |
||
| 1500 | if (array_key_exists('removed_values', $values)) { |
||
| 1501 | return array_map('intval', array_keys($values['removed_values'])); |
||
| 1502 | } |
||
| 1503 | return array(); |
||
| 1504 | } |
||
| 1505 | |||
| 1506 | private function insertCrossReference(Tracker_Artifact $source_artifact, $target_artifact_id) { |
||
| 1507 | return $this->getTrackerReferenceManager()->insertBetweenTwoArtifacts( |
||
| 1508 | $source_artifact, |
||
| 1509 | $this->getArtifactFactory()->getArtifactById($target_artifact_id), |
||
| 1510 | $this->getCurrentUser() |
||
| 1511 | ); |
||
| 1512 | } |
||
| 1513 | |||
| 1514 | private function removeCrossReference(Tracker_Artifact $source_artifact, $target_artifact_id) { |
||
| 1515 | return $this->getTrackerReferenceManager()->removeBetweenTwoArtifacts( |
||
| 1516 | $source_artifact, |
||
| 1517 | $this->getArtifactFactory()->getArtifactById($target_artifact_id), |
||
| 1518 | $this->getCurrentUser() |
||
| 1519 | ); |
||
| 1520 | } |
||
| 1521 | |||
| 1522 | protected function getTrackerReferenceManager() { |
||
| 1523 | return new Tracker_ReferenceManager( |
||
| 1524 | ReferenceManager::instance(), |
||
| 1525 | Tracker_ArtifactFactory::instance() |
||
| 1526 | ); |
||
| 1527 | } |
||
| 1528 | |||
| 1529 | /** |
||
| 1530 | * Retrieve linked artifacts according to user's permissions |
||
| 1531 | * |
||
| 1532 | * @param Tracker_Artifact_Changeset $changeset The changeset you want to retrieve artifact from |
||
| 1533 | * @param PFUser $user The user who will see the artifacts |
||
| 1534 | * |
||
| 1535 | * @return Tracker_Artifact[] |
||
| 1536 | */ |
||
| 1537 | public function getLinkedArtifacts(Tracker_Artifact_Changeset $changeset, PFUser $user) { |
||
| 1538 | $artifacts = array(); |
||
| 1539 | $changeset_value = $changeset->getValue($this); |
||
| 1540 | if ($changeset_value) { |
||
| 1541 | foreach ($changeset_value->getArtifactIds() as $id) { |
||
| 1542 | $this->addArtifactUserCanViewFromId($artifacts, $id, $user); |
||
| 1543 | } |
||
| 1544 | } |
||
| 1545 | return $artifacts; |
||
| 1546 | } |
||
| 1547 | |||
| 1548 | |||
| 1549 | |||
| 1550 | |||
| 1551 | /** |
||
| 1552 | * Retrieve sliced linked artifacts according to user's permissions |
||
| 1553 | * |
||
| 1554 | * This is nearly the same as a paginated list however, for performance |
||
| 1555 | * reasons, the total size may be different than the sum of total paginated |
||
| 1556 | * artifacts. |
||
| 1557 | * |
||
| 1558 | * Example to illustrate the difference between paginated and sliced: |
||
| 1559 | * |
||
| 1560 | * Given that artifact links are [12, 13, 24, 39, 65, 69] |
||
| 1561 | * And that the user cannot see artifact #39 |
||
| 1562 | * When I request linked artifacts by bunchs of 2 |
||
| 1563 | * Then I get [[12, 13], [24], [65, 69]] # instead of [[12, 13], [24, 65], [69]] |
||
| 1564 | * And total size will be 6 # instead of 5 |
||
| 1565 | * |
||
| 1566 | * @param Tracker_Artifact_Changeset $changeset The changeset you want to retrieve artifact from |
||
| 1567 | * @param PFUser $user The user who will see the artifacts |
||
| 1568 | * @param int $limit The number of artifact to fetch |
||
| 1569 | * @param int $offset The offset |
||
| 1570 | * |
||
| 1571 | * @return Tracker_Artifact_PaginatedArtifacts |
||
| 1572 | */ |
||
| 1573 | public function getSlicedLinkedArtifacts(Tracker_Artifact_Changeset $changeset, PFUser $user, $limit, $offset) { |
||
| 1574 | $changeset_value = $changeset->getValue($this); |
||
| 1575 | if (! $changeset_value) { |
||
| 1576 | return new Tracker_Artifact_PaginatedArtifacts(array(), 0); |
||
| 1577 | } |
||
| 1578 | |||
| 1579 | $artifact_ids = $changeset_value->getArtifactIds(); |
||
| 1580 | $size = count($artifact_ids); |
||
| 1581 | |||
| 1582 | $artifacts = array(); |
||
| 1583 | foreach (array_slice($artifact_ids, $offset, $limit) as $id) { |
||
| 1584 | $this->addArtifactUserCanViewFromId($artifacts, $id, $user); |
||
| 1585 | } |
||
| 1586 | |||
| 1587 | return new Tracker_Artifact_PaginatedArtifacts($artifacts, $size); |
||
| 1588 | } |
||
| 1589 | |||
| 1590 | /** @return Tracker_Artifact|null */ |
||
| 1591 | private function addArtifactUserCanViewFromId(array &$artifacts, $id, PFUser $user) { |
||
| 1592 | $artifact = $this->getArtifactFactory()->getArtifactById($id); |
||
| 1593 | if ($artifact && $artifact->userCanView($user)) { |
||
| 1594 | $artifacts[] = $artifact; |
||
| 1595 | } |
||
| 1596 | } |
||
| 1597 | |||
| 1598 | /** |
||
| 1599 | * If request come with a 'parent', it should be automagically transformed as |
||
| 1600 | * 'new_values'. |
||
| 1601 | * Please note that it only work on artifact creation. |
||
| 1602 | * |
||
| 1603 | * @param type $fields_data |
||
| 1604 | */ |
||
| 1605 | public function augmentDataFromRequest(&$fields_data) { |
||
| 1621 | |||
| 1622 | public function accept(Tracker_FormElement_FieldVisitor $visitor) { |
||
| 1625 | } |
||
| 1626 |
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.