1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8/** 9 * Handler class for ItemsList 10 * 11 * Letter key: ~l~ 12 * 13 */ 14class Tracker_Field_ItemsList extends Tracker_Field_Abstract implements Tracker_Field_Exportable 15{ 16 public static function getTypes() 17 { 18 return [ 19 'l' => [ 20 'name' => tr('Items List'), 21 'description' => tr('Display a list of field values from another tracker that has a relation with this tracker.'), 22 'readonly' => true, 23 'help' => 'Items List and Item Link Tracker Fields', 24 'prefs' => ['trackerfield_itemslist'], 25 'tags' => ['advanced'], 26 'default' => 'n', 27 'params' => [ 28 'trackerId' => [ 29 'name' => tr('Tracker ID'), 30 'description' => tr('Tracker from which to list items'), 31 'filter' => 'int', 32 'legacy_index' => 0, 33 'profile_reference' => 'tracker', 34 ], 35 'fieldIdThere' => [ 36 'name' => tr('Link Field ID'), 37 'description' => tr('Field ID from the other tracker containing an item link pointing to the item in this tracker or some other value to be matched.'), 38 'filter' => 'int', 39 'legacy_index' => 1, 40 'profile_reference' => 'tracker_field', 41 'parent' => 'trackerId', 42 'parentkey' => 'tracker_id', 43 'sort_order' => 'position_nasc', 44 ], 45 'fieldIdHere' => [ 46 'name' => tr('Value Field ID'), 47 'description' => tr('Field ID from this tracker matching the value in the link field ID from the other tracker if the field above is not an item link. If the field chosen here is an ItemLink, Link Field ID above can be left empty.'), 48 'filter' => 'int', 49 'legacy_index' => 2, 50 'profile_reference' => 'tracker_field', 51 'parent' => 'input[name=trackerId]', 52 'parentkey' => 'tracker_id', 53 'sort_order' => 'position_nasc', 54 ], 55 'displayFieldIdThere' => [ 56 'name' => tr('Fields to display'), 57 'description' => tr('Display alternate fields from the other tracker instead of the item title'), 58 'filter' => 'int', 59 'separator' => '|', 60 'legacy_index' => 3, 61 'profile_reference' => 'tracker_field', 62 'parent' => 'trackerId', 63 'parentkey' => 'tracker_id', 64 'sort_order' => 'position_nasc', 65 ], 66 'displayFieldIdThereFormat' => [ 67 'name' => tr('Format for customising fields to display'), 68 'description' => tr('Uses the translate function to replace %0 etc with the field values. E.g. "%0 any text %1"'), 69 'filter' => 'text', 70 ], 71 'sortField' => [ 72 'name' => tr('Sort Fields'), 73 'description' => tr('Order results by one or more fields from the other tracker.'), 74 'filter' => 'int', 75 'separator' => '|', 76 'legacy_index' => 6, 77 'profile_reference' => 'tracker_field', 78 'parent' => 'trackerId', 79 'parentkey' => 'tracker_id', 80 'sort_order' => 'position_nasc', 81 ], 82 'linkToItems' => [ 83 'name' => tr('Display'), 84 'description' => tr('How the link to the items should be rendered'), 85 'filter' => 'int', 86 'options' => [ 87 0 => tr('Value'), 88 1 => tr('Link'), 89 ], 90 'legacy_index' => 4, 91 ], 92 'status' => [ 93 'name' => tr('Status Filter'), 94 'description' => tr('Limit the available items to a selected set'), 95 'filter' => 'alpha', 96 'options' => [ 97 'opc' => tr('all'), 98 'o' => tr('open'), 99 'p' => tr('pending'), 100 'c' => tr('closed'), 101 'op' => tr('open, pending'), 102 'pc' => tr('pending, closed'), 103 ], 104 'legacy_index' => 5, 105 ], 106 'editable' => [ 107 'name' => tr('Add, Edit, Remove Items'), 108 'description' => tr('Master switch for enabling Add, Edit, Remove Items'), 109 'filter' => 'int', 110 'options' => [ 111 0 => tr('No'), 112 1 => tr('Yes'), 113 ], 114 ], 115 'addItemText' => [ // having text switches adding on/off 116 'name' => tr('Add Item text'), 117 'description' => tr('Text to show on a button to add new items. Also enables adding items.'), 118 'filter' => 'text', 119 'depends' => [ 120 'field' => 'editable' 121 ], 122 ], 123 'editItem' => [ 124 'name' => tr('Edit Item'), 125 'description' => tr('Enable editing items'), 126 'filter' => 'int', 127 'options' => [ 128 0 => tr('No'), 129 1 => tr('Yes'), 130 ], 131 'depends' => [ 132 'field' => 'editable' 133 ], 134 ], 135 'deleteItem' => [ 136 'name' => tr('Delete Item'), 137 'description' => tr('Allow deleting items'), 138 'filter' => 'int', 139 'options' => [ 140 0 => tr('No'), 141 1 => tr('Yes'), 142 ], 143 'depends' => [ 144 'field' => 'editable' 145 ], 146 ], 147 'editInViewMode' => [ 148 'name' => tr('Edit in View Mode'), 149 'description' => tr('Whether the edit buttons and icons also appear when viewing an item'), 150 'filter' => 'int', 151 'options' => [ 152 0 => tr('No'), 153 1 => tr('Yes'), 154 ], 155 'depends' => [ 156 'field' => 'editable' 157 ], 158 ], 159 // TODO: 160 /*'addItemWikiTpl' => [ 161 'name' => tr('Add Item Template Page'), 162 'description' => tr('Wiki page to use as a Pretty Tracker template'), 163 'filter' => 'pagename', 164 'profile_reference' => 'wiki_page', 165 'depends' => [ 166 'field' => 'editable' 167 ], 168 ],*/ 169 ], 170 ], 171 ]; 172 } 173 174 175 /** 176 * Get field data 177 * @see Tracker_Field_Interface::getFieldData() 178 * 179 */ 180 function getFieldData(array $requestData = []) 181 { 182 $items = $this->getItemIds(); 183 $list = $this->getItemLabels($items); 184 185 $ret = [ 186 'value' => '', 187 'items' => $list, 188 ]; 189 190 return $ret; 191 } 192 193 function renderInput($context = []) 194 { 195 if (empty($this->getOption('fieldIdHere'))) { 196 return $this->renderOutput(['render_input' => 'y']); 197 } else { 198 TikiLib::lib('header')->add_jq_onready( 199 ' 200$("input[name=ins_' . $this->getOption('fieldIdHere') . '], select[name=ins_' . $this->getOption('fieldIdHere') . ']").change(function(e, initial) { 201 if(initial == "initial" && $(this).data("triggered-' . $this->getInsertId() . '")) { 202 return; 203 } 204 $(this).data("triggered-' . $this->getInsertId() . '", true); 205 $.getJSON( 206 "tiki-ajax_services.php", 207 { 208 controller: "tracker", 209 action: "itemslist_output", 210 field: "' . $this->getConfiguration('fieldId') . '", 211 fieldIdHere: "' . $this->getOption('fieldIdHere') . '", 212 value: $(this).val() 213 }, 214 function(data, status) { 215 $ddl = $("div[name=' . $this->getInsertId() . ']"); 216 $ddl.html(data); 217 if (jqueryTiki.chosen) { 218 $ddl.trigger("chosen:updated"); 219 } 220 $ddl.trigger("change"); 221 } 222 ); 223}); 224 ' 225 ); 226 // this is smart enough to attach only once even if multiple fields attach the same code 227 TikiLib::lib('header')->add_jq_onready(' 228$("input[name=ins_' . $this->getOption('fieldIdHere') . '], select[name=ins_' . $this->getOption('fieldIdHere') . ']").trigger("change", "initial"); 229', 1); 230 return '<div name="' . $this->getInsertId() . '"></div>'; 231 } 232 } 233 234 function renderOutput($context = []) 235 { 236 if (isset($context['search_render']) && $context['search_render'] == 'y') { 237 $itemIds = $this->getData($this->getConfiguration('fieldId')); 238 } else { 239 $itemIds = $this->getItemIds(); 240 } 241 242 $list = $this->getItemLabels($itemIds, $context); 243 244 // if nothing found check definition for previous list (used for output render) 245 if (empty($list)) { 246 $list = $this->getConfiguration('items', []); 247 $itemIds = array_keys($list); 248 } 249 250 if (isset($context['list_mode']) && $context['list_mode'] === 'csv') { 251 return implode('%%%', $list); 252 } else { 253 $data = [ 254 'links' => (bool)$this->getOption('linkToItems'), 255 'raw' => (bool)$this->getOption('displayFieldIdThere'), 256 'itemIds' => implode(',', $itemIds), 257 'items' => $list, 258 'num' => count($list), 259 'itemPermissions' => [], 260 'addItemText' => '', 261 'otherFieldPermName' => '', 262 'parentItemId' => 0, 263 ]; 264 // Either it has been called from renderInput (edit mode) or we're rendering it as well in view mode 265 if ((isset($context['render_input']) && $context['render_input'] === 'y') || $this->getOption('editInViewMode')) { 266 $editmode = true; 267 } else { 268 $editmode = false; 269 } 270 if ($this->getOption('editable') && $editmode) { 271 $trackerThere = Tracker_Definition::get($this->getOption('trackerId')); 272 $fieldThere = $trackerThere->getField($this->getOption('fieldIdThere')); 273 // trackerPerms overide field Options 274 $trackerPerms = Perms::get('tracker', $this->getOption('trackerId')); 275 $canCreate = $trackerPerms->create_tracker_items; // tracker/global permission; However canEdit and canRemove are Item permissions, see below 276 $itemPermissions = []; 277 foreach ($itemIds as $itemId) { 278 $item = Tracker_Item::fromId($itemId); 279 $itemPermissions[$itemId]['can_remove'] = $item->canRemove(); 280 $itemPermissions[$itemId]['can_modify'] = $item->canModify(); 281 } 282 $data['itemPermissions'] = $itemPermissions; 283 $data['addItemText'] = $canCreate ? $this->getOption('addItemText') : ''; 284 $data['otherFieldPermName'] = $fieldThere['permName']; 285 $data['parentItemId'] = $this->getItemId(); 286 } 287 return $this->renderTemplate( 288 'trackeroutput/itemslist.tpl', 289 $context, 290 $data 291 ); 292 } 293 } 294 295 function itemsRequireRefresh($trackerId, $modifiedFields) 296 { 297 if ($this->getOption('trackerId') != $trackerId) { 298 return false; 299 } 300 301 $displayFields = $this->getOption('displayFieldIdThere'); 302 if (! is_array($displayFields)) { 303 $displayFields = [$displayFields]; 304 } 305 306 $usedFields = array_merge( 307 [$this->getOption('fieldIdThere')], 308 $displayFields 309 ); 310 311 $intersect = array_intersect($usedFields, $modifiedFields); 312 313 return count($intersect) > 0; 314 } 315 316 function watchCompare($old, $new) 317 { 318 $o = ''; 319 $items = $this->getItemIds(); 320 $n = $this->getItemLabels($items); 321 322 return parent::watchCompare($o, $n); // then compare as text 323 } 324 325 function getDocumentPart(Search_Type_Factory_Interface $typeFactory) 326 { 327 $baseKey = $this->getBaseKey(); 328 $items = $this->getItemIds(); 329 330 $list = $this->getItemLabels($items); 331 $listtext = implode(' ', $list); 332 333 return [ 334 $baseKey => $typeFactory->multivalue($items), 335 "{$baseKey}_text" => $typeFactory->sortable($listtext), 336 ]; 337 } 338 339 function getProvidedFields() 340 { 341 $baseKey = $this->getBaseKey(); 342 return [ 343 $baseKey, 344 "{$baseKey}_text", 345 ]; 346 } 347 348 function getGlobalFields() 349 { 350 return []; 351 } 352 353 function getTabularSchema() 354 { 355 $schema = new Tracker\Tabular\Schema($this->getTrackerDefinition()); 356 $permName = $this->getConfiguration('permName'); 357 $name = $this->getConfiguration('name'); 358 359 $schema->addNew($permName, 'multi-id') 360 ->setLabel($name) 361 ->setReadOnly(true) 362 ->setRenderTransform(function ($value) { 363 return implode(';', $value); 364 }) 365 ->setParseIntoTransform(function (& $info, $value) use ($permName) { 366 $info['fields'][$permName] = $value; 367 }); 368 369 $schema->addNew($permName, 'multi-name') 370 ->setLabel($name) 371 ->addQuerySource('itemId', 'object_id') 372 ->setReadOnly(true) 373 ->setRenderTransform(function ($value, $extra) { 374 375 if (is_string($value) && empty($value)) { 376 // ItemsLists have no stored value, so when called from \Tracker\Tabular\Source\TrackerSourceEntry... 377 // we have to: get a copy of this field 378 $field = $this->getTrackerDefinition()->getFieldFromPermName($this->getConfiguration('permName')); 379 // get a new handler for it 380 $factory = $this->getTrackerDefinition()->getFieldFactory(); 381 $handler = $factory->getHandler($field, ['itemId' => $extra['itemId']]); 382 // for which we can then get the itemIds array of the "linked" items 383 $value = $handler->getItemIds(); 384 // and then get the labels from the id's we've now found as if they were the field's data 385 } 386 387 $labels = $this->getItemLabels($value, ['list_mode' => 'csv']); 388 return implode(';', $labels); 389 }) 390 ->setParseIntoTransform(function (& $info, $value) use ($permName) { 391 $info['fields'][$permName] = $value; 392 }); 393 394 // json format for export and import (which will recreate missing linked items) 395 396 $fieldIdHere = $this->getOption('fieldIdHere'); 397 $definition = $this->getTrackerDefinition(); 398 $fieldHere = $definition->getField($fieldIdHere); 399 $extraFieldName = "tracker_field_{$fieldHere['permName']}"; 400 401 $fieldIdThere = $this->getOption('fieldIdThere'); 402 $trackerIdThere = $this->getOption('trackerId'); 403 $trackerThere = Tracker_Definition::get($trackerIdThere); 404 $fieldThere = $trackerThere->getField($fieldIdThere); 405 $queryFieldName = "tracker_field_{$fieldThere['permName']}"; 406 407 if ($fieldHere['type'] === 'r' && $fieldThere['type'] !== 'r') { 408 $extraFieldName .= '_text'; 409 } 410 411 // cache the other tracker's items to test when importing 412 $itemsThereLookup = new Tracker\Tabular\Schema\CachedLookupHelper(); 413 $tiki_tracker_items = TikiDb::get()->table('tiki_tracker_items'); 414 $itemsThereLookup->setInit( 415 function ($count) use ($tiki_tracker_items, $trackerIdThere) { 416 return $tiki_tracker_items->fetchMap( 417 'itemId', 'status', 418 [ 419 'trackerId' => $trackerIdThere, 420 ], 421 $count, 0 422 ); 423 } 424 ); 425 $itemsThereLookup->setLookup( 426 function ($value) use ($tiki_tracker_items, $trackerIdThere) { 427 return $tiki_tracker_items->fetchOne( 428 'itemId', [ 429 'trackerId' => $trackerIdThere, 430 'itemId' => $value, 431 ] 432 ); 433 } 434 ); 435 436 $attributelib = TikiLib::lib('attribute'); 437 $unifiedsearchlib = TikiLib::lib('unifiedsearch'); 438 $trackerUtilities = new Services_Tracker_Utilities; 439 440 $schema->addNew($permName, 'multi-json') 441 ->setLabel($name) 442 // these query sources appear in the $extra array in the render transform fn 443 ->addQuerySource('itemId', 'object_id') 444 ->addQuerySource('fieldIdHere', $extraFieldName) 445 ->setRenderTransform( 446 function ($value, $extra) use ($trackerIdThere, $queryFieldName, $unifiedsearchlib) { 447 448 if (! empty($extra['fieldIdHere'])) { 449 $content = $extra['fieldIdHere']; 450 } else { 451 $content = (string)$extra['itemId']; 452 } 453 454 $query = $unifiedsearchlib->buildQuery( 455 [ 456 'type' => 'trackeritem', 457 'tracker_id' => (string)$trackerIdThere, 458 $queryFieldName => $content, 459 ] 460 ); 461 462 $result = $query->search($unifiedsearchlib->getIndex()); 463 $out = []; 464 465 if ($result->count()) { 466 foreach ($result as $entry) { 467 $item = Tracker_Item::fromId($entry['object_id']); 468 $data = $item->getData(); 469 $data['fields'] = array_filter($data['fields']); 470 $out[] = $data; 471 } 472 } 473 474 if ($out) { 475 $out = json_encode($out); 476 } 477 478 return $out; 479 } 480 ) 481 ->setParseIntoTransform( 482 function (& $info, $value) use ($permName, $trackerUtilities, $trackerThere, $itemsThereLookup, $attributelib, $fieldThere, $schema) { 483 static $newItemsThereCreated = []; 484 485 $data = json_decode($value, true); 486 487 if ($data && is_array($data)) { 488 489 foreach ($data as $row) { 490 if (! empty($row['itemId'])) { 491 492 // check the old itemId as an attribute to avoid repeat imports 493 $attr = $attributelib->find_objects_with('tiki.trackeritem.olditemid', $row['itemId']); 494 495 // not done this time? 496 if (! isset($newItemsThereCreated[$row['itemId']])) { 497 498 if (! isset($row['created']) && ! empty($row['creation_date'])) { 499 $row['created'] = $row['creation_date']; // convert from index to database field name 500 } 501 502 $item = Tracker_Item::fromInfo($row); 503 504 // FIXME $schema here doesn't know if it's a transaction type so this never executes 505 if ($schema->isImportTransaction()) { 506 $trackerThereId = $trackerThere->getConfiguration('trackerId'); 507 if (! $item->canModify()) { 508 throw new \Tracker\Tabular\Exception\Exception(tr( 509 'Permission denied importing into linked tracker %0', 510 $trackerThereId 511 )); 512 } 513 $errors = $trackerUtilities->validateItem($trackerThere, $item->getData()); 514 if ($errors) { 515 throw new \Tracker\Tabular\Exception\Exception(tr( 516 'Errors occurred importing into linked tracker %0', 517 $trackerThereId 518 )); 519 } 520 } 521 522 $itemData = $item->getData(); 523 524 // no item with this itemId and we didn't create it before? so let's make one! 525 if (! $itemsThereLookup->get($row['itemId']) && empty($attr)) { 526 527 // needs to be done after the new main item has been created 528 if (! isset($info['postprocess'])) { 529 $info['postprocess'] = []; 530 } 531 $info['postprocess'][] = function ($newMainItemId) use ($trackerUtilities, $trackerThere, $itemData, $fieldThere, $attributelib) { 532 533 // fix the ItemLink there to point at our new item 534 if ($fieldThere['type'] === 'r') { 535 $itemData['fields'][$fieldThere['permName']] = $newMainItemId; 536 } 537 538 $newItemId = $trackerUtilities->insertItem($trackerThere, $itemData); 539 540 if ($newItemId) { 541 $newItemsThereCreated[$itemData['itemId']] = $newItemId; 542 // store the old itemId as an attribute of this item so we don't import it again 543 $attributelib->set_attribute( 544 'trackeritem', 545 $newItemId, 546 'tiki.trackeritem.olditemid', 547 $itemData['itemId'] 548 ); 549 550 } else { 551 Feedback::error( 552 tr( 553 'Creating replacement linked item for itemId %0 for ItemsList field "%1" import failed on item #%2', 554 $itemData['itemId'], $this->getConfiguration('permName'), $this->getItemId() 555 ) 556 ); 557 } 558 }; 559 560 } else if ($itemsThereLookup->get($row['itemId'])) { // linked item exists, so update it 561 $item = Tracker_Item::fromInfo($row); 562 $itemData = $item->getData(); 563 $result = $trackerUtilities->updateItem($trackerThere, $itemData); 564 if (! $result) { 565 Feedback::error( 566 tr( 567 'Updating linked item for itemId %0 for ItemsList field "%1" import failed on item #%2', 568 $itemData['itemId'], $this->getConfiguration('permName'), $this->getItemId() 569 ) 570 ); 571 } 572 } 573 574 } 575 576 } 577 } 578 } 579 $info['fields'][$permName] = ''; 580 } 581 ); 582 583 return $schema; 584 } 585 586 587 588 private function getItemIds() 589 { 590 $trklib = TikiLib::lib('trk'); 591 $trackerId = (int) $this->getOption('trackerId'); 592 593 $filterFieldIdHere = (int) $this->getOption('fieldIdHere'); 594 $filterFieldIdThere = (int) $this->getOption('fieldIdThere'); 595 596 $filterFieldHere = $this->getTrackerDefinition()->getField($filterFieldIdHere); 597 $filterFieldThere = $trklib->get_tracker_field($filterFieldIdThere); 598 599 $sortFieldIds = $this->getOption('sortField'); 600 if (is_array($sortFieldIds)) { 601 $sortFieldIds = array_filter($sortFieldIds); 602 } else { 603 $sortFieldIds = []; 604 } 605 $status = $this->getOption('status', 'opc'); 606 $tracker = Tracker_Definition::get($trackerId); 607 608 609 610 // note: if itemlink or dynamic item list is used, than the final value to compare with must be calculated based on the current itemid 611 612 $technique = 'value'; 613 614 // not sure this is working 615 // r = item link 616 if ($tracker && $filterFieldThere && (! $filterFieldIdHere || $filterFieldThere['type'] === 'r' || $filterFieldThere['type'] === 'w')) { 617 if ($filterFieldThere['type'] === 'r' || $filterFieldThere['type'] === 'w') { 618 $technique = 'id'; 619 } 620 } 621 622 // not sure this is working 623 // q = Autoincrement 624 if ($filterFieldHere['type'] == 'q' && isset($filterFieldHere['options_array'][3]) && $filterFieldHere['options_array'][3] == 'itemId') { 625 $technique = 'id'; 626 } 627 628 if ($technique == 'id') { 629 $itemId = $this->getItemId(); 630 if (! $itemId) { 631 $items = []; 632 } else { 633 $items = $trklib->get_items_list($trackerId, $filterFieldIdThere, $itemId, $status, false, $sortFieldIds); 634 } 635 } else { 636 // when this is an item link or dynamic item list field, localvalue contains the target itemId 637 $localValue = $this->getData($filterFieldIdHere); 638 if (! $localValue) { 639 // in some cases e.g. pretty tracker $this->getData($filterFieldIdHere) is not reliable as the info is not there 640 // Note: this fix only works if the itemId is passed via the template 641 $itemId = $this->getItemId(); 642 $localValue = $trklib->get_item_value($trackerId, $itemId, $filterFieldIdHere); 643 } 644 if (! $filterFieldThere && $filterFieldHere && ( $filterFieldHere['type'] === 'r' || $filterFieldHere['type'] === 'w' ) && $localValue) { 645 // itemlink/dynamic item list field in this tracker pointing directly to an item in the other tracker 646 return [$localValue]; 647 } 648 // r = item link - not sure this is working 649 if ($filterFieldHere['type'] == 'r' && isset($filterFieldHere['options_array'][0]) && isset($filterFieldHere['options_array'][1])) { 650 $localValue = $trklib->get_item_value($filterFieldHere['options_array'][0], $localValue, $filterFieldHere['options_array'][1]); 651 } 652 653 // w = dynamic item list - localvalue is the itemid of the target item. so rewrite. 654 if ($filterFieldHere['type'] == 'w') { 655 $localValue = $trklib->get_item_value($trackerId, $localValue, $filterFieldIdThere); 656 } 657 // u = user selector, might be mulitple users so need to find multiple values 658 if ($filterFieldHere['type'] == 'u' && ! empty($filterFieldHere['options_map']['multiple']) 659 && $localValue 660 ) { 661 if (! is_array($localValue)) { 662 $theUsers = explode(',', $localValue); 663 } else { 664 $theUsers = $localValue; 665 } 666 $items = []; 667 foreach ($theUsers as $theUser) { 668 $items = array_merge( 669 $items, 670 $trklib->get_items_list($trackerId, $filterFieldIdThere, $theUser, $status, false, $sortFieldIds) 671 ); 672 } 673 674 return $items; 675 } 676 // Skip nulls 677 if ($localValue) { 678 $items = $trklib->get_items_list($trackerId, $filterFieldIdThere, $localValue, $status, false, $sortFieldIds); 679 } else { 680 $items = []; 681 } 682 } 683 684 return $items; 685 } 686 687 /** 688 * Get value of displayfields from given array of itemIds 689 * @param array $items 690 * @param array $context 691 * @return array array of values by itemId 692 */ 693 private function getItemLabels($items, $context = ['list_mode' => '']) 694 { 695 $displayFields = $this->getOption('displayFieldIdThere'); 696 $trackerId = (int) $this->getOption('trackerId'); 697 $status = $this->getOption('status', 'opc'); 698 699 $definition = Tracker_Definition::get($trackerId); 700 if (! $definition) { 701 return []; 702 } 703 704 $list = []; 705 $trklib = TikiLib::lib('trk'); 706 foreach ($items as $itemId) { 707 if ($displayFields && $displayFields[0]) { 708 $list[$itemId] = $trklib->concat_item_from_fieldslist( 709 $trackerId, 710 $itemId, 711 $displayFields, 712 $status, 713 ' ', 714 isset($context['list_mode']) ? $context['list_mode'] : '', 715 $this->getOption('linkToItems'), 716 $this->getOption('displayFieldIdThereFormat'), 717 $trklib->get_tracker_item($itemId) 718 ); 719 } else { 720 $list[$itemId] = $trklib->get_isMain_value($trackerId, $itemId); 721 } 722 } 723 724 return $list; 725 } 726 727 /** 728 * Get remote items' values in an array as opposed to a string label. 729 * Useful in Math calculations where individual field values are needed. 730 * @return array associated array of field names and values 731 */ 732 public function getItemValues() 733 { 734 $displayFields = $this->getOption('displayFieldIdThere'); 735 $trackerId = (int) $this->getOption('trackerId'); 736 737 $definition = Tracker_Definition::get($trackerId); 738 if (! $definition) { 739 return []; 740 } 741 742 $itemsValues = []; 743 744 $items = $this->getItemIds(); 745 foreach ($items as $itemId) { 746 $item = TikiLib::lib('trk')->get_tracker_item($itemId); 747 $itemValues = []; 748 if ($displayFields) { 749 foreach ($displayFields as $fieldId) { 750 $field = $definition->getField($fieldId); 751 if (isset($item[$fieldId])) { 752 if ($field['type'] == 'l') { 753 $factory = $definition->getFieldFactory(); 754 $handler = $factory->getHandler($field, $item); 755 $itemValues[$field['permName']] = $handler->renderOutput(['list_mode' => 'csv']); 756 } else { 757 $itemValues[$field['permName']] = $item[$fieldId]; 758 } 759 } else { 760 $itemValues[$field['permName']] = ''; 761 } 762 } 763 } 764 $itemsValues[] = $itemValues; 765 } 766 767 return $itemsValues; 768 } 769} 770