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 8class Services_Tracker_Controller 9{ 10 /** 11 * @var Services_Tracker_Utilities 12 */ 13 private $utilities; 14 15 function setUp() 16 { 17 global $prefs; 18 $this->utilities = new Services_Tracker_Utilities; 19 20 Services_Exception_Disabled::check('feature_trackers'); 21 } 22 23 function action_view($input) 24 { 25 $item = Tracker_Item::fromId($input->id->int()); 26 27 if (! $item) { 28 throw new Services_Exception_NotFound(tr('Item not found')); 29 } 30 31 if (! $item->canView()) { 32 throw new Services_Exception_Denied(tr('Permission denied')); 33 } 34 35 $definition = $item->getDefinition(); 36 37 $fields = $item->prepareOutput(new JitFilter([])); 38 39 $info = TikiLib::lib('trk')->get_item_info($item->getId()); 40 41 return [ 42 'title' => TikiLib::lib('object')->get_title('trackeritem', $item->getId()), 43 'format' => $input->format->word(), 44 'itemId' => $item->getId(), 45 'trackerId' => $definition->getConfiguration('trackerId'), 46 'fields' => $fields, 47 'canModify' => $item->canModify(), 48 'item_info' => $info, 49 'info' => $info, 50 ]; 51 } 52 53 function action_add_field($input) 54 { 55 $modal = $input->modal->int(); 56 $trackerId = $input->trackerId->int(); 57 58 $perms = Perms::get('tracker', $trackerId); 59 if (! $perms->admin_trackers) { 60 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 61 } 62 63 $trklib = TikiLib::lib('trk'); 64 $definition = Tracker_Definition::get($trackerId); 65 66 if (! $definition) { 67 throw new Services_Exception_NotFound; 68 } 69 70 $name = $input->name->text(); 71 72 $permName = $trklib::generatePermName($definition, $input->permName->word()); 73 74 $type = $input->type->text(); 75 $description = $input->description->text(); 76 $wikiparse = $input->description_parse->int(); 77 $adminOnly = $input->adminOnly->int(); 78 $fieldId = 0; 79 80 $types = $this->utilities->getFieldTypes(); 81 82 if (empty($type)) { 83 $type = 't'; 84 } 85 86 if (! isset($types[$type])) { 87 throw new Services_Exception(tr('Type does not exist'), 400); 88 } 89 90 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $input->type->word()) { 91 if (empty($name)) { 92 throw new Services_Exception_MissingValue('name'); 93 } 94 95 if ($definition->getFieldFromName($name)) { 96 throw new Services_Exception_DuplicateValue('name', $name); 97 } 98 99 if ($definition->getFieldFromPermName($permName)) { 100 throw new Services_Exception_DuplicateValue('permName', $permName); 101 } 102 103 $fieldId = $this->utilities->createField( 104 [ 105 'trackerId' => $trackerId, 106 'name' => $name, 107 'permName' => $permName, 108 'type' => $type, 109 'description' => $description, 110 'descriptionIsParsed' => $wikiparse, 111 'isHidden' => $adminOnly ? 'y' : 'n', 112 ] 113 ); 114 115 if ($input->submit_and_edit->none() || $input->next->word() === 'edit') { 116 return [ 117 'FORWARD' => [ 118 'action' => 'edit_field', 119 'fieldId' => $fieldId, 120 'trackerId' => $trackerId, 121 'modal' => $modal, 122 ], 123 ]; 124 } 125 } 126 127 return [ 128 'title' => tr('Add Field'), 129 'trackerId' => $trackerId, 130 'fieldId' => $fieldId, 131 'name' => $name, 132 'permName' => $permName, 133 'type' => $type, 134 'types' => $types, 135 'description' => $description, 136 'descriptionIsParsed' => $wikiparse, 137 'modal' => $modal, 138 'fieldPrefix' => $definition->getConfiguration('fieldPrefix'), 139 ]; 140 } 141 142 function action_list_fields($input) 143 { 144 global $prefs; 145 146 $trackerId = $input->trackerId->int(); 147 $perms = Perms::get('tracker', $trackerId); 148 149 if (! $perms->view_trackers) { 150 throw new Services_Exception_Denied(tr("You don't have permission to view the tracker")); 151 } 152 153 $definition = Tracker_Definition::get($trackerId); 154 155 if (! $definition) { 156 throw new Services_Exception_NotFound; 157 } 158 159 $fields = $definition->getFields(); 160 $types = $this->utilities->getFieldTypes(); 161 $typesDisabled = []; 162 163 if ($perms->admin_trackers) { 164 $typesDisabled = $this->utilities->getFieldTypesDisabled(); 165 } 166 167 $missing = []; 168 $duplicates = []; 169 170 foreach ($fields as $field) { 171 if (! array_key_exists($field['type'], $types) && ! in_array($field['type'], $missing)) { 172 $missing[] = $field['type']; 173 } 174 if ($prefs['unified_engine'] === 'elastic') { 175 $tracker_fields = TikiLib::lib('tiki')->table('tiki_tracker_fields'); 176 $dupeFields = $tracker_fields->fetchAll( 177 [ 178 'fieldId', 179 'trackerId', 180 'name', 181 'permName', 182 'type', 183 ], 184 [ 185 'fieldId' => $tracker_fields->not($field['fieldId']), 186 'type' => $tracker_fields->not($field['type']), 187 'permName' => $field['permName'], 188 ] 189 ); 190 if ($dupeFields) { 191 foreach ($dupeFields as & $df) { 192 $df['message'] = tr('Warning: There is a conflict in permanent names, which can cause indexing errors.') . 193 '<br><a href="tiki-admin_tracker_fields.php?trackerId=' . $df['trackerId'] . '">' . 194 tr( 195 'Field #%0 "%1" of type "%2" also found in tracker #%3 with perm name %4', 196 $df['fieldId'], 197 $df['name'], 198 $types[$df['type']]['name'], 199 $df['trackerId'], 200 $df['permName'] 201 ) . 202 '</a>'; 203 } 204 $duplicates[$field['fieldId']] = $dupeFields; 205 } 206 } 207 if ($field['type'] == 'i' && $prefs['tracker_legacy_insert'] !== 'y') { 208 Feedback::error(tr('You are using the image field type, which is deprecated. It is recommended to activate \'Use legacy tracker insertion screen\' found on the <a href="%0">trackers admin configuration</a> screen.', 'tiki-admin.php?page=trackers')); 209 } 210 } 211 if (! empty($missing)) { 212 Feedback::error(tr('Warning: Required field types not enabled: %0', implode(', ', $missing))); 213 } 214 215 return [ 216 'fields' => $fields, 217 'types' => $types, 218 'typesDisabled' => $typesDisabled, 219 'duplicates' => $duplicates, 220 ]; 221 } 222 223 function action_save_fields($input) 224 { 225 $trackerId = $input->trackerId->int(); 226 227 $perms = Perms::get('tracker', $trackerId); 228 if (! $perms->admin_trackers) { 229 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 230 } 231 232 $definition = Tracker_Definition::get($trackerId); 233 234 if (! $definition) { 235 throw new Services_Exception_NotFound; 236 } 237 238 $hasList = false; 239 $hasLink = false; 240 241 $tx = TikiDb::get()->begin(); 242 243 $fields = []; 244 foreach ($input->field as $key => $value) { 245 $fieldId = (int) $key; 246 $isMain = $value->isMain->int(); 247 $isTblVisible = $value->isTblVisible->int(); 248 249 $fields[$fieldId] = [ 250 'position' => $value->position->int(), 251 'isTblVisible' => $isTblVisible ? 'y' : 'n', 252 'isMain' => $isMain ? 'y' : 'n', 253 'isSearchable' => $value->isSearchable->int() ? 'y' : 'n', 254 'isPublic' => $value->isPublic->int() ? 'y' : 'n', 255 'isMandatory' => $value->isMandatory->int() ? 'y' : 'n', 256 ]; 257 258 $this->utilities->updateField($trackerId, $fieldId, $fields[$fieldId]); 259 260 $hasList = $hasList || $isTblVisible; 261 $hasLink = $hasLink || $isMain; 262 } 263 264 if (! $hasList) { 265 Feedback::error(tr('Tracker contains no listed field, no meaningful information will be provided in the default list.'), true); 266 } 267 268 if (! $hasLink) { 269 Feedback::error(tr('The tracker contains no field in the title, so no link will be generated.'), true); 270 } 271 272 $tx->commit(); 273 274 return [ 275 'fields' => $fields, 276 ]; 277 } 278 279 /** 280 * @param JitFilter $input 281 * @return array 282 * @throws Services_Exception 283 * @throws Services_Exception_Denied 284 * @throws Services_Exception_DuplicateValue 285 * @throws Services_Exception_NotFound 286 */ 287 function action_edit_field($input) 288 { 289 global $prefs; 290 291 $trackerId = $input->trackerId->int(); 292 293 $perms = Perms::get('tracker', $trackerId); 294 if (! $perms->admin_trackers) { 295 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 296 } 297 298 $fieldId = $input->fieldId->int(); 299 $definition = Tracker_Definition::get($trackerId); 300 301 if (! $definition) { 302 throw new Services_Exception_NotFound; 303 } 304 305 $field = $definition->getField($fieldId); 306 if (! $field) { 307 throw new Services_Exception_NotFound; 308 } 309 310 $types = $this->utilities->getFieldTypes(); 311 $typeInfo = $types[$field['type']]; 312 if ($prefs['tracker_change_field_type'] !== 'y') { 313 if (empty($typeInfo['supported_changes'])) { 314 $types = []; 315 } else { 316 $types = $this->utilities->getFieldTypes($typeInfo['supported_changes']); 317 } 318 } 319 320 $permName = $input->permName->word(); 321 if ($field['permName'] != $permName) { 322 if ($definition->getFieldFromPermName($permName)) { 323 throw new Services_Exception_DuplicateValue('permName', $permName); 324 } 325 } 326 327 if (strlen($permName) > Tracker_Item::PERM_NAME_MAX_ALLOWED_SIZE) { 328 throw new Services_Exception(tr('Tracker Field permanent name cannot contain more than %0 characters', Tracker_Item::PERM_NAME_MAX_ALLOWED_SIZE), 400); 329 } 330 331 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $input->name->text()) { 332 $input->replaceFilters( 333 [ 334 'visible_by' => 'groupname', 335 'editable_by' => 'groupname', 336 ] 337 ); 338 $visibleBy = $input->asArray('visible_by', ','); 339 $editableBy = $input->asArray('editable_by', ','); 340 341 $options = $this->utilities->buildOptions(new JitFilter($input->option), $typeInfo); 342 343 $trklib = TikiLib::lib('trk'); 344 $handler = $trklib->get_field_handler($field); 345 if (! $handler) { 346 throw new Services_Exception(tr('Field handler not found'), 400); 347 } 348 if (method_exists($handler, 'validateFieldOptions')) { 349 try { 350 $params = $this->utilities->parseOptions($options, $typeInfo); 351 $handler->validateFieldOptions($params); 352 } catch (Exception $e) { 353 throw new Services_Exception($e->getMessage(), 400); 354 } 355 } 356 357 if (! empty($types)) { 358 $type = $input->type->text(); 359 if ($field['type'] !== $type) { 360 if (! isset($types[$type])) { 361 throw new Services_Exception(tr('Type does not exist'), 400); 362 } 363 $oldTypeInfo = $typeInfo; 364 $typeInfo = $types[$type]; 365 if (! empty($oldTypeInfo['supported_changes']) && in_array($type, $oldTypeInfo['supported_changes'])) { 366 // changing supported types should not clear all options but only the ones that are not available in the new type 367 $options = Tracker_Options::fromInput(new JitFilter($input->option), $oldTypeInfo); 368 $params = $options->getAllParameters(); 369 foreach (array_keys($params) as $param) { 370 if (empty($typeInfo['params'][$param])) { 371 unset($params[$param]); 372 } 373 } 374 // convert underneath data if field type supports it 375 if (method_exists($handler, 'convertFieldTo')) { 376 $convertedOptions = $handler->convertFieldTo($type); 377 $params = array_merge($params, $convertedOptions); 378 } 379 // prepare options 380 $options = json_encode($params); 381 } else { 382 // clear options for unsupported field type changes 383 $options = json_encode([]); 384 } 385 } elseif (method_exists($handler, 'convertFieldOptions')) { 386 $params = $this->utilities->parseOptions($options, $typeInfo); 387 $handler->convertFieldOptions($params); 388 } 389 } else { 390 $type = $field['type']; 391 } 392 393 $rules = ''; 394 if ($input->conditions->text()) { 395 $actions = json_decode($input->actions->text()); 396 $else = json_decode($input->else->text()); 397 // filter out empty defaults - TODO work out how to remove rules in Vue 398 if ($actions->predicates[0]->target_id !== 'NoTarget' && $else->predicates[0]->target_id !== 'NoTarget') { 399 $rules = json_encode([ 400 'conditions' => json_decode($input->conditions->text()), 401 'actions' => $actions, 402 'else' => $else, 403 ]); 404 } 405 } 406 407 $data = [ 408 'name' => $input->name->text(), 409 'description' => $input->description->text(), 410 'descriptionIsParsed' => $input->description_parse->int() ? 'y' : 'n', 411 'options' => $options, 412 'validation' => $input->validation_type->word(), 413 'validationParam' => $input->validation_parameter->none(), 414 'validationMessage' => $input->validation_message->text(), 415 'isMultilingual' => $input->multilingual->int() ? 'y' : 'n', 416 'visibleBy' => array_filter(array_map('trim', $visibleBy)), 417 'editableBy' => array_filter(array_map('trim', $editableBy)), 418 'isHidden' => $input->visibility->alpha(), 419 'errorMsg' => $input->error_message->text(), 420 'permName' => $permName, 421 'type' => $type, 422 'rules' => $rules, 423 ]; 424 425 $this->utilities->updateField( 426 $trackerId, 427 $fieldId, 428 $data 429 ); 430 431 // run field specific post save function 432 $handler = TikiLib::lib('trk')->get_field_handler($field); 433 if ($handler && method_exists($handler, 'handleFieldSave')) { 434 $handler->handleFieldSave($data); 435 } 436 } 437 438 array_walk($typeInfo['params'], function (& $param) { 439 if (isset($param['profile_reference'])) { 440 $lib = TikiLib::lib('object'); 441 $param['selector_type'] = $lib->getSelectorType($param['profile_reference']); 442 if (isset($param['parent'])) { 443 if (! preg_match('/[\[\]#\.]/', $param['parent'])) { 444 $param['parent'] = "#option-{$param['parent']}"; 445 } 446 } else { 447 $param['parent'] = null; 448 } 449 $param['parentkey'] = isset($param['parentkey']) ? $param['parentkey'] : null; 450 $param['sort_order'] = isset($param['sort_order']) ? $param['sort_order'] : null; 451 $param['format'] = isset($param['format']) ? $param['format'] : null; 452 } else { 453 $param['selector_type'] = null; 454 } 455 }); 456 457 return [ 458 'title' => tr('Edit') . " " . tr('%0', $field['name']), 459 'field' => $field, 460 'info' => $typeInfo, 461 'options' => $this->utilities->parseOptions($field['options'], $typeInfo), 462 'validation_types' => [ 463 '' => tr('None'), 464 'captcha' => tr('CAPTCHA'), 465 'distinct' => tr('Distinct'), 466 'pagename' => tr('Page Name'), 467 'password' => tr('Password'), 468 'regex' => tr('Regular Expression (Pattern)'), 469 'username' => tr('Username'), 470 ], 471 'types' => $types, 472 'permNameMaxAllowedSize' => Tracker_Item::PERM_NAME_MAX_ALLOWED_SIZE, 473 'fields' => $definition->getFields(), 474 ]; 475 } 476 477 function action_remove_fields($input) 478 { 479 $trackerId = $input->trackerId->int(); 480 481 $perms = Perms::get('tracker', $trackerId); 482 if (! $perms->admin_trackers) { 483 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 484 } 485 486 $fields = $input->fields->int(); 487 488 $definition = Tracker_Definition::get($trackerId); 489 490 if (! $definition) { 491 throw new Services_Exception_NotFound; 492 } 493 494 foreach ($fields as $fieldId) { 495 if (! $definition->getField($fieldId)) { 496 throw new Services_Exception_NotFound; 497 } 498 } 499 500 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $input->confirm->int()) { 501 $trklib = TikiLib::lib('trk'); 502 $tx = TikiDb::get()->begin(); 503 foreach ($fields as $fieldId) { 504 $trklib->remove_tracker_field($fieldId, $trackerId); 505 } 506 $tx->commit(); 507 508 return [ 509 'status' => 'DONE', 510 'trackerId' => $trackerId, 511 'fields' => $fields, 512 ]; 513 } else { 514 return [ 515 'trackerId' => $trackerId, 516 'fields' => $fields, 517 ]; 518 } 519 } 520 521 function action_export_fields($input) 522 { 523 $trackerId = $input->trackerId->int(); 524 525 $perms = Perms::get('tracker', $trackerId); 526 if (! $perms->admin_trackers) { 527 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 528 } 529 530 $fields = $input->fields->int(); 531 532 $definition = Tracker_Definition::get($trackerId); 533 534 if (! $definition) { 535 throw new Services_Exception_NotFound; 536 } 537 538 if ($fields) { 539 $fields = $this->utilities->getFieldsFromIds($definition, $fields); 540 } else { 541 $fields = $definition->getFields(); 542 } 543 544 $data = ""; 545 foreach ($fields as $field) { 546 $data .= $this->utilities->exportField($field); 547 } 548 549 return [ 550 'title' => tr('Export Fields'), 551 'trackerId' => $trackerId, 552 'fields' => $fields, 553 'export' => $data, 554 ]; 555 } 556 557 function action_import_fields($input) 558 { 559 if (! Perms::get()->admin_trackers) { 560 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 561 } 562 563 $trackerId = $input->trackerId->int(); 564 $definition = Tracker_Definition::get($trackerId); 565 566 if (! $definition) { 567 throw new Services_Exception_NotFound; 568 } 569 570 $raw = $input->raw->none(); 571 $preserve = $input->preserve_ids->int(); 572 $last_position = $input->last_position->int(); 573 574 $data = TikiLib::lib('tiki')->read_raw($raw, $preserve); 575 576 if ($_SERVER['REQUEST_METHOD'] == 'POST') { 577 if (! $data) { 578 throw new Services_Exception(tr('Invalid data provided'), 400); 579 } 580 581 $trklib = TikiLib::lib('trk'); 582 583 foreach ($data as $info) { 584 $info['permName'] = $trklib::generatePermName($definition, $info['permName']); 585 586 $this->utilities->importField($trackerId, new JitFilter($info), $preserve, $last_position); 587 } 588 } 589 590 return [ 591 'title' => tr('Import Tracker Fields'), 592 'trackerId' => $trackerId, 593 ]; 594 } 595 596 function action_list_trackers($input) 597 { 598 if (! Perms::get()->admin_trackers) { 599 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 600 } 601 602 $trklib = TikiLib::lib('trk'); 603 return $trklib->list_trackers(); 604 } 605 606 function action_list_items($input) 607 { 608 // TODO : Eventually, this method should filter according to the actual permissions, but because 609 // it is only to be used for tracker sync at this time, admin privileges are just fine. 610 611 if (! Perms::get()->admin_trackers) { 612 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 613 } 614 615 $trackerId = $input->trackerId->int(); 616 $offset = $input->offset->int(); 617 $maxRecords = $input->maxRecords->int(); 618 $status = $input->status->word(); 619 $format = $input->format->word(); 620 $modifiedSince = $input->modifiedSince->int(); 621 622 $definition = Tracker_Definition::get($trackerId); 623 624 if (! $definition) { 625 throw new Services_Exception_NotFound; 626 } 627 628 $items = $this->utilities->getItems( 629 [ 630 'trackerId' => $trackerId, 631 'status' => $status, 632 'modifiedSince' => $modifiedSince, 633 ], 634 $maxRecords, 635 $offset 636 ); 637 638 if ($format !== 'raw') { 639 foreach ($items as & $item) { 640 $item = $this->utilities->processValues($definition, $item); 641 } 642 } 643 644 return [ 645 'trackerId' => $trackerId, 646 'offset' => $offset, 647 'maxRecords' => $maxRecords, 648 'result' => $items, 649 ]; 650 } 651 652 /** 653 * @param JitFilter $input 654 * @return mixed 655 * @throws Services_Exception_Denied 656 * @throws Services_Exception_NotFound 657 */ 658 function action_get_item_inputs($input) 659 { 660 $trackerId = $input->trackerId->int(); 661 $trackerName = $input->trackerName->text(); 662 $itemId = $input->itemId->int(); 663 $byName = $input->byName->bool(); 664 $defaults = $input->asArray('defaults'); 665 666 $this->trackerNameAndId($trackerId, $trackerName); 667 668 $definition = Tracker_Definition::get($trackerId); 669 670 if (! $definition) { 671 throw new Services_Exception_NotFound; 672 } 673 674 $itemObject = Tracker_Item::newItem($trackerId); 675 676 if (! $itemObject->canModify()) { 677 throw new Services_Exception_Denied; 678 } 679 680 $query = Tracker_Query::tracker($byName ? $trackerName : $trackerId) 681 ->itemId($itemId); 682 683 if ($input > 0) { 684 $query->byName(); 685 } 686 if (! empty($defaults)) { 687 $query->inputDefaults($defaults); 688 } 689 690 $inputs = $query 691 ->queryInput(); 692 693 return $inputs; 694 } 695 696 function action_clone_item($input) 697 { 698 global $prefs; 699 700 Services_Exception_Disabled::check('tracker_clone_item'); 701 702 $trackerId = $input->trackerId->int(); 703 $definition = Tracker_Definition::get($trackerId); 704 705 if (! $definition) { 706 throw new Services_Exception_NotFound; 707 } 708 709 $itemId = $input->itemId->int(); 710 if (! $itemId) { 711 throw new Services_Exception_Denied(tr('No item to clone')); 712 } 713 714 $itemObject = Tracker_Item::fromId($itemId); 715 716 if (! $itemObject->canView()) { 717 throw new Services_Exception_Denied(tr("The item to clone isn't visible")); 718 } 719 720 $newItem = Tracker_Item::newItem($trackerId); 721 722 if (! $newItem->canModify()) { 723 throw new Services_Exception_Denied(tr("You don't have permission to create new items")); 724 } 725 726 global $prefs; 727 if ($prefs['feature_jquery_validation'] === 'y') { 728 $_REQUEST['itemId'] = 0; // let the validation code know this will be a new item 729 $validationjs = TikiLib::lib('validators')->generateTrackerValidateJS( 730 $definition->getFields(), 731 'ins_', 732 '', 733 '', 734 // not custom submit handler that is only needed when called by this service 735 'submitHandler: function(form, event){return process_submit(form, event);}' 736 ); 737 TikiLib::lib('header')->add_jq_onready('$("#cloneItemForm' . $trackerId . '").validate({' . $validationjs . $this->get_validation_options()); 738 } 739 740 $itemObject->asNew(); 741 $itemData = $itemObject->getData($input); 742 $processedFields = []; 743 744 $id = 0; 745 if ($_SERVER['REQUEST_METHOD'] == 'POST') { 746 $itemObject = $this->utilities->cloneItem($definition, $itemData, $itemId); 747 $id = $itemObject->getId(); 748 749 $processedItem = $this->utilities->processValues($definition, $itemData); 750 $processedFields = $processedItem['fields']; 751 } 752 753 return [ 754 'title' => tr('Duplicate Item'), 755 'trackerId' => $trackerId, 756 'itemId' => $itemId, 757 'created' => $id, 758 'data' => $itemData['fields'], 759 'fields' => $itemObject->prepareInput($input), 760 'processedFields' => $processedFields, 761 ]; 762 } 763 764 function action_insert_item($input) 765 { 766 $processedFields = []; 767 768 $trackerId = $input->trackerId->int(); 769 770 if (! $trackerId) { 771 return [ 772 'FORWARD' => ['controller' => 'tracker', 'action' => 'select_tracker'], 773 ]; 774 } 775 776 $trackerName = $this->trackerName($trackerId); 777 $definition = Tracker_Definition::get($trackerId); 778 779 if (! $definition) { 780 throw new Services_Exception_NotFound; 781 } 782 783 $itemObject = Tracker_Item::newItem($trackerId); 784 785 if (! $itemObject->canModify()) { 786 throw new Services_Exception_Denied; 787 } 788 789 $fields = $input->fields->none(); 790 $forced = $input->forced->none(); 791 $processedFields = $itemObject->prepareInput($input); 792 $suppressFeedback = $input->suppressFeedback->bool(); 793 $toRemove = []; 794 795 if (empty($fields)) { 796 797 $fields = []; 798 foreach ($processedFields as $k => $f) { 799 $permName = $f['permName']; 800 $fields[$permName] = $f['value']; 801 802 if (isset($forced[$permName])) { 803 $toRemove[$permName] = $k; 804 } 805 } 806 807 foreach ($toRemove as $permName => $key) { 808 unset($fields[$permName]); 809 unset($processedFields[$key]); 810 } 811 } else { 812 $out = []; 813 foreach ($fields as $key => $value) { 814 if ($itemObject->canModifyField($key)) { 815 $out[$key] = $value; 816 } 817 } 818 $fields = $out; 819 820 // if fields are specified in the form creation url then use only those ones 821 if (! empty($fields) && $_SERVER['REQUEST_METHOD'] !== 'POST') { 822 foreach ($processedFields as $k => $f) { 823 $permName = $f['permName']; 824 825 if (! isset($fields[$permName])) { 826 $toRemove[$permName] = $k; 827 } 828 } 829 830 foreach ($toRemove as $permName => $key) { 831 unset($processedFields[$key]); 832 } 833 } 834 } 835 836 global $prefs; 837 if ($prefs['feature_jquery_validation'] === 'y') { 838 $validationjs = TikiLib::lib('validators')->generateTrackerValidateJS( 839 $definition->getFields(), 840 'ins_', 841 '', 842 '', 843 // not custom submit handler that is only needed when called by this service 844 'submitHandler: function(form, event){return process_submit(form, event);}' 845 ); 846 TikiLib::lib('header')->add_jq_onready('$("#insertItemForm' . $trackerId . '").validate({' . $validationjs . $this->get_validation_options('#insertItemForm' . $trackerId)); 847 } 848 849 if ($prefs['tracker_field_rules'] === 'y') { 850 $js = TikiLib::lib('vuejs')->generateTrackerRulesJS($definition->getFields()); 851 TikiLib::lib('header')->add_jq_onready($js); 852 } 853 854 $itemId = 0; 855 $util = new Services_Utilities(); 856 if (! empty($fields) && $util->isActionPost()) { 857 foreach ($forced as $key => $value) { 858 if ($itemObject->canModifyField($key)) { 859 $fields[$key] = $value; 860 } 861 } 862 863 // test if one item per user 864 if ($definition->getConfiguration('oneUserItem', 'n') == 'y') { 865 $perms = Perms::get('tracker', $trackerId); 866 867 if ($perms->admin_trackers) { // tracker admins can make items for other users 868 $field = $definition->getField($definition->getUserField()); 869 $theUser = isset($fields[$field['permName']]) ? $fields[$field['permName']] : null; // setup error? 870 } else { 871 $theUser = null; 872 } 873 874 $tmp = TikiLib::lib('trk')->get_user_item($trackerId, $definition->getInformation(), $theUser); 875 if ($tmp > 0) { 876 throw new Services_Exception(tr('Item could not be created. Only one item per user is allowed.'), 400); 877 } 878 } 879 880 $itemId = $this->utilities->insertItem( 881 $definition, 882 [ 883 'status' => $input->status->word(), 884 'fields' => $fields, 885 ] 886 ); 887 888 if ($itemId) { 889 TikiLib::lib('unifiedsearch')->processUpdateQueue(); 890 TikiLib::events()->trigger('tiki.process.redirect'); // wait for indexing to complete before loading of next request to ensure updated info shown 891 892 if ($next = $input->next->url()) { 893 $access = TikiLib::lib('access'); 894 $access->redirect($next, tr('Item created')); 895 } 896 897 $item = $this->utilities->getItem($trackerId, $itemId); 898 $item['itemTitle'] = $this->utilities->getTitle($definition, $item); 899 $processedItem = $this->utilities->processValues($definition, $item); 900 $item['processedFields'] = $processedItem['fields']; 901 902 if ($suppressFeedback !== true) { 903 if ($input->ajax->bool()) { 904 $trackerinfo = $definition->getInformation(); 905 $trackername = tr($trackerinfo['name']); 906 $msg = tr('New "%0" item successfully created.', $trackername); 907 Feedback::success($msg); 908 Feedback::send_headers(); 909 } else { 910 Feedback::success(tr('New tracker item %0 successfully created.', $itemId)); 911 } 912 } 913 914 return $item; 915 } else { 916 throw new Services_Exception(tr('Tracker item could not be created.'), 400); 917 } 918 } 919 920 $editableFields = $input->editable->none(); 921 if (empty($editableFields)) { 922 //if editable fields, show all fields in the form (except the ones from forced which have been removed). 923 $displayedFields = $processedFields; 924 } else { 925 // if editableFields is set, only add the field if found in the editableFields array 926 $displayedFields = []; 927 foreach ($processedFields as $k => $f) { 928 $permName = $f['permName']; 929 if (in_array($permName, $editableFields)) { 930 $displayedFields[] = $f; 931 } 932 } 933 } 934 $status = $input->status->word(); 935 if ($status === null) { // '=== null' means status was not set. if status is set to "", it skips the status and uses the default 936 $status = $itemObject->getDisplayedStatus(); 937 } else { 938 $status = $input->status->word(); 939 } 940 941 $title = $input->title->none(); 942 if (empty($title)) { // '=== null' means status was not set. if status is set to "", it skips the status and uses the default 943 $title = tr('Create Item'); 944 } else { 945 $title = $title; 946 } 947 948 if ($input->format->word()) { 949 $format = $input->format->word(); 950 } else { 951 $format = $definition->getConfiguration('sectionFormat'); 952 } 953 954 $editItemPretty = ''; 955 if ($format === 'config') { 956 $editItemPretty = $definition->getConfiguration('editItemPretty'); 957 } 958 959 return [ 960 'title' => $title, 961 'trackerId' => $trackerId, 962 'trackerName' => $trackerName, 963 'itemId' => $itemId, 964 'fields' => $displayedFields, 965 'forced' => $forced, 966 'trackerLogo' => $definition->getConfiguration('logo'), 967 'modal' => $input->modal->int(), 968 'status' => $status, 969 'format' => $format, 970 'editItemPretty' => $editItemPretty, 971 'next' => $input->next->url(), 972 'suppressFeedback' => $suppressFeedback, 973 ]; 974 } 975 976 /** 977 * @param $input JitFilter 978 * - "trackerId" required 979 * - "itemId" required 980 * - "editable" optional. array of field names. e.g. ['title', 'description', 'user']. If not set, all fields 981 * all fields will be editable 982 * - "forced" optional. associative array of fields where the value is 'forced'. Commonly used with skip_form. 983 * e.g ['isArchived'=>'y']. For example, this can be used to create a button that allows you to set the 984 * trackeritem to "Closed", or to set a field to a pre-determined value. 985 * - "skip_form" - Allows users to skip the input form. This must be used with "forced" or "status" otherwise nothing would change 986 * - "status" - sets a status for the object to be set to. Often used with skip_form 987 * 988 * Formatting the edit screen 989 * - "title" optional. Sets a title for the edit screen. 990 * - "skip_form_confirm_message" optional. Used with skip_form. E.g. "Are you sure you want to set this item to 'Closed'". 991 * - "button_label" optional. Used to override the label for the Update/Save button. 992 * - "redirect" set a url to which a user should be redirected, if any. 993 * 994 * @return array 995 * @throws Exception 996 * @throws Services_Exception 997 * @throws Services_Exception_Denied 998 * @throws Services_Exception_MissingValue 999 * @throws Services_Exception_NotFound 1000 * 1001 */ 1002 function action_update_item($input) 1003 { 1004 $trackerId = $input->trackerId->int(); 1005 $definition = Tracker_Definition::get($trackerId); 1006 $suppressFeedback = $input->suppressFeedback->bool(); 1007 1008 if (! $definition) { 1009 throw new Services_Exception_NotFound; 1010 } 1011 1012 if (! $itemId = $input->itemId->int()) { 1013 throw new Services_Exception_MissingValue('itemId'); 1014 } 1015 1016 $itemInfo = TikiLib::lib('trk')->get_tracker_item($itemId); 1017 if (! $itemInfo || $itemInfo['trackerId'] != $trackerId) { 1018 throw new Services_Exception_NotFound; 1019 } 1020 1021 $itemObject = Tracker_Item::fromInfo($itemInfo); 1022 if (! $itemObject->canModify()) { 1023 throw new Services_Exception_Denied; 1024 } 1025 1026 global $prefs; 1027 if ($prefs['feature_jquery_validation'] === 'y') { 1028 $validationjs = TikiLib::lib('validators')->generateTrackerValidateJS( 1029 $definition->getFields(), 1030 'ins_', 1031 '', 1032 '', 1033 // not custom submit handler that is only needed when called by this service 1034 'submitHandler: function(form, event){return process_submit(form, event);}' 1035 ); 1036 TikiLib::lib('header')->add_jq_onready('$("#updateItemForm' . $trackerId . '").validate({' . $validationjs . $this->get_validation_options()); 1037 } 1038 1039 if ($prefs['tracker_field_rules'] === 'y') { 1040 $js = TikiLib::lib('vuejs')->generateTrackerRulesJS($definition->getFields()); 1041 TikiLib::lib('header')->add_jq_onready($js); 1042 } 1043 1044 if ($_SERVER['REQUEST_METHOD'] == 'POST') { 1045 TikiLib::lib('access')->preventRedirect(true); 1046 //fetch the processed fields and the changes made in the form. Put them in the 'fields' variable 1047 $processedFields = $itemObject->prepareInput($input); 1048 $fields = []; 1049 foreach ($processedFields as $k => $f) { 1050 $permName = $f['permName']; 1051 $fields[$permName] = isset($f['value']) ? $f['value'] : ''; 1052 } 1053 // for each input from the form, ensure user has modify rights. If so, add to the fields var to be edited. 1054 $userInput = $input->fields->none(); 1055 if (! empty($userInput)) { 1056 foreach ($userInput as $key => $value) { 1057 if ($itemObject->canModifyField($key)) { 1058 $fields[$key] = $value; 1059 } 1060 } 1061 } 1062 // for each input from the form, ensure user has modify rights. If so, add to the fields var to be edited. 1063 $forcedInput = $input->forced->none(); 1064 if (! empty($forcedInput)) { 1065 foreach ($forcedInput as $key => $value) { 1066 if ($itemObject->canModifyField($key)) { 1067 $fields[$key] = $value; 1068 } 1069 } 1070 } 1071 1072 $result = $this->utilities->updateItem( 1073 $definition, 1074 [ 1075 'itemId' => $itemId, 1076 'status' => $input->status->word(), 1077 'fields' => $fields, 1078 ] 1079 ); 1080 1081 TikiLib::lib('access')->preventRedirect(false); 1082 1083 if ($result !== false) { 1084 TikiLib::lib('unifiedsearch')->processUpdateQueue(); 1085 TikiLib::events()->trigger('tiki.process.redirect'); // wait for indexing to complete before loading of next request to ensure updated info shown 1086 //only need feedback if success - feedback already set if there was an update error 1087 } 1088 if (isset($input['edit']) && $input['edit'] === 'inline') { 1089 if ($result && $suppressFeedback !== true) { 1090 Feedback::success(tr('Tracker item %0 has been updated', $itemId), true); 1091 } else { 1092 Feedback::send_headers(); 1093 } 1094 } else { 1095 if ($result && $suppressFeedback !== true) { 1096 if ($input->ajax->bool()) { 1097 $trackerinfo = $definition->getInformation(); 1098 $trackername = tr($trackerinfo['name']); 1099 $item = $this->utilities->getItem($trackerId, $itemId); 1100 $itemtitle = $this->utilities->getTitle($definition, $item); 1101 $msg = tr('%0: Updated "%1"', $trackername, $itemtitle) . " [" . TikiLib::lib('tiki')->get_long_time( TikiLib::lib('tiki')->now ) . "]"; 1102 Feedback::success($msg); 1103 Feedback::send_headers(); 1104 } else { 1105 Feedback::success(tr('Tracker item %0 has been updated', $itemId)); 1106 } 1107 } else { 1108 Feedback::send_headers(); 1109 } 1110 $redirect = $input->redirect->url(); 1111 1112 if ($input->saveAndComment->int()) { 1113 $version = TikiLib::lib('trk')->last_log_version($itemId); 1114 1115 return [ 1116 'FORWARD' => [ 1117 'controller' => 'comment', 1118 'action' => 'post', 1119 'type' => 'trackeritem', 1120 'objectId' => $itemId, 1121 'parentId' => 0, 1122 'version' => $version, 1123 'return_url' => $redirect, 1124 'title' => tr('Comment for edit #%0', $version), 1125 ], 1126 ]; 1127 } 1128 //return to page 1129 if (! $redirect) { 1130 $referer = Services_Utilities::noJsPath(); 1131 return Services_Utilities::refresh($referer); 1132 } else { 1133 return Services_Utilities::redirect($redirect); 1134 } 1135 } 1136 } 1137 1138 // sets all fields for the tracker item with their value 1139 $processedFields = $itemObject->prepareInput($input); 1140 // fields that we want to change in the form. If 1141 $editableFields = $input->editable->none(); 1142 // fields where the value is forced. 1143 $forcedFields = $input->forced->none(); 1144 1145 // if forced fields are set, remove them from the processedFields since they will not show up visually 1146 // in the form; they will be set up separately and hidden. 1147 if (! empty($forcedFields)) { 1148 foreach ($processedFields as $k => $f) { 1149 $permName = $f['permName']; 1150 if (isset($forcedFields[$permName])) { 1151 unset($processedFields[$k]); 1152 } 1153 } 1154 } 1155 1156 if (empty($editableFields)) { 1157 //if editable fields, show all fields in the form (except the ones from forced which have been removed). 1158 $displayedFields = $processedFields; 1159 } else { 1160 // if editableFields is set, only add the field if found in the editableFields array 1161 $displayedFields = []; 1162 foreach ($processedFields as $k => $f) { 1163 $permName = $f['permName']; 1164 if (in_array($permName, $editableFields)) { 1165 $displayedFields[] = $f; 1166 } 1167 } 1168 } 1169 1170 /* Allow overriding of default wording in the template */ 1171 if (empty($input->title->text())) { 1172 $title = tr('Update Item'); 1173 } else { 1174 $title = $input->title->text(); 1175 } 1176 1177 if ($input->format->word()) { 1178 $format = $input->format->word(); 1179 } else { 1180 $format = $definition->getConfiguration('sectionFormat'); 1181 } 1182 1183 $editItemPretty = ''; 1184 if ($format === 'config') { 1185 $editItemPretty = $definition->getConfiguration('editItemPretty'); 1186 } 1187 1188 //Used if skip form is set 1189 if (empty($input->skip_form_message->text())) { 1190 $skip_form_message = tr('Are you sure you would like to update this item?'); 1191 } else { 1192 $skip_form_message = $input->skip_form_message->text(); 1193 } 1194 1195 if (empty($input->button_label->text())) { 1196 $button_label = tr('Save'); 1197 } else { 1198 $button_label = $input->button_label->text(); 1199 } 1200 1201 if ($input->status->word() === null) { 1202 $status = $itemObject->getDisplayedStatus(); 1203 } else { 1204 $status = $input->status->word(); 1205 } 1206 1207 $saveAndComment = $definition->getConfiguration('saveAndComment'); 1208 if ($saveAndComment !== 'n') { 1209 if (! Tracker_Item::fromId($itemId)->canPostComments()) { 1210 $saveAndComment = 'n'; 1211 } 1212 } 1213 1214 return [ 1215 'title' => $title, 1216 'trackerId' => $trackerId, 1217 'itemId' => $itemId, 1218 'fields' => $displayedFields, 1219 'forced' => $forcedFields, 1220 'status' => $status, 1221 'skip_form' => $input->skip_form->word(), 1222 'skip_form_message' => $skip_form_message, 1223 'format' => $format, 1224 'editItemPretty' => $editItemPretty, 1225 'button_label' => $button_label, 1226 'redirect' => $input->redirect->none(), 1227 'saveAndComment' => $saveAndComment, 1228 'suppressFeedback' => $suppressFeedback, 1229 ]; 1230 } 1231 1232 /** 1233 * Preview tracker items 1234 * 1235 * @param JitFilter $input 1236 * @return null 1237 */ 1238 public function action_preview_item($input) 1239 { 1240 global $prefs; 1241 1242 $input = $input->fields; 1243 $trackerId = $input->trackerId->int(); 1244 $definition = Tracker_Definition::get($trackerId); 1245 1246 if (! $definition) { 1247 throw new Services_Exception_NotFound; 1248 } 1249 1250 $itemId = $input->itemId->int(); 1251 1252 if ($itemId) { 1253 $itemInfo = TikiLib::lib('trk')->get_tracker_item($itemId); 1254 if (! $itemInfo || $itemInfo['trackerId'] != $trackerId) { 1255 throw new Services_Exception_NotFound; 1256 } 1257 } else { 1258 $itemInfo = ['trackerId' => $trackerId]; 1259 } 1260 1261 $trklib = TikiLib::lib('trk'); 1262 $smarty = TikiLib::lib('smarty'); 1263 1264 $itemObject = Tracker_Item::fromInfo($itemInfo); 1265 $processedFields = $itemObject->prepareInput($input); 1266 $fieldsProcessed = []; 1267 foreach ($processedFields as $k => $f) { 1268 $permName = $f['permName']; 1269 $fieldsProcessed[$permName] = isset($f['value']) ? $f['value'] : ''; 1270 if (isset($f['relations'])) { 1271 $fieldsProcessed[$permName] = ['relations' => $f['relations']]; 1272 } 1273 if (isset($f['selected'])) { 1274 $fieldsProcessed[$permName] = ['selected' => $f['selected']]; 1275 } 1276 if (isset($f['selected_categories'])) { 1277 $fieldsProcessed[$permName] = ['selected_categories' => $f['selected_categories']]; 1278 } 1279 if (isset($f['files'])) { 1280 $fieldsProcessed[$permName] = ['files' => $f['files']]; 1281 } 1282 } 1283 1284 $fieldDefinitions = $definition->getFields(); 1285 $smarty->assign('tracker_is_multilingual', $prefs['feature_multilingual'] == 'y' && $definition->getLanguageField()); 1286 1287 if ($prefs['feature_groupalert'] == 'y') { 1288 $groupalertlib = TikiLib::lib('groupalert'); 1289 $groupforalert = $groupalertlib->GetGroup('tracker', $trackerId); 1290 if ($groupforalert != "") { 1291 $showeachuser = $groupalertlib->GetShowEachUser('tracker', $trackerId, $groupforalert); 1292 $userlib = TikiLib::lib('user'); 1293 $listusertoalert = $userlib->get_users(0, -1, 'login_asc', '', '', false, $groupforalert, ''); 1294 $smarty->assign_by_ref('listusertoalert', $listusertoalert['data']); 1295 } 1296 $smarty->assign_by_ref('groupforalert', $groupforalert); 1297 $smarty->assign_by_ref('showeachuser', $showeachuser); 1298 } 1299 1300 $smarty->assign('itemId', $itemId); 1301 $smarty->assign_by_ref('item_info', $itemInfo); 1302 $smarty->assign('item', ['itemId' => $itemId, 'trackerId' => $trackerId]); 1303 1304 $trackerInfo = $definition->getInformation(); 1305 1306 include_once('tiki-sefurl.php'); 1307 1308 $statusTypes = $trklib->status_types(); 1309 $smarty->assign('status_types', $statusTypes); 1310 $fields = []; 1311 $ins_fields = []; 1312 $itemUsers = $trklib->get_item_creators($trackerId, $itemId); 1313 $smarty->assign_by_ref('itemUsers', $itemUsers); 1314 1315 if (empty($trackerInfo)) { 1316 $itemInfo = []; 1317 } 1318 1319 $fieldFactory = $definition->getFieldFactory(); 1320 1321 foreach ($fieldDefinitions as &$fieldDefinition) { 1322 $fid = $fieldDefinition["fieldId"]; 1323 $fieldDefinition["ins_id"] = 'ins_' . $fid; 1324 $fieldDefinition["filter_id"] = 'filter_' . $fid; 1325 } 1326 unset($fieldDefinition); 1327 1328 $itemObject = Tracker_Item::fromInfo($itemInfo); 1329 1330 foreach ($fieldDefinitions as $i => $currentField) { 1331 $currentFieldIns = null; 1332 $fid = $currentField['fieldId']; 1333 1334 $handler = $fieldFactory->getHandler($currentField, $itemInfo); 1335 1336 $fieldIsVisible = $itemObject->canViewField($fid); 1337 $fieldIsEditable = $itemObject->canModifyField($fid); 1338 1339 if ($fieldIsVisible || $fieldIsEditable) { 1340 $currentFieldIns = $currentField; 1341 1342 if ($handler) { 1343 $insertValues = $handler->getFieldData(); 1344 1345 if ($insertValues) { 1346 $currentFieldIns = array_merge($currentFieldIns, $insertValues); 1347 } 1348 } 1349 } 1350 1351 if (! empty($currentFieldIns)) { 1352 if ($fieldIsVisible) { 1353 $fields['data'][$i] = $currentFieldIns; 1354 } 1355 if ($fieldIsEditable) { 1356 $ins_fields['data'][$i] = $currentFieldIns; 1357 } 1358 } 1359 } 1360 1361 if ($trackerInfo['doNotShowEmptyField'] == 'y') { 1362 $trackerlib = TikiLib::lib('trk'); 1363 $fields['data'] = $trackerlib->mark_fields_as_empty($fields['data']); 1364 } 1365 1366 foreach ($fields["data"] as &$field) { 1367 $permName = isset($field['permName']) ? $field['permName'] : null; 1368 if (isset($fieldsProcessed[$permName])) { 1369 $field['value'] = $fieldsProcessed[$permName]; 1370 $field['pvalue'] = $fieldsProcessed[$permName]; 1371 if (isset($fieldsProcessed[$permName]['relations'])) { 1372 $field['relations'] = $fieldsProcessed[$permName]['relations']; 1373 } 1374 if (isset($fieldsProcessed[$permName]['selected'])) { 1375 $field['selected'] = $fieldsProcessed[$permName]['selected']; 1376 } 1377 if (isset($fieldsProcessed[$permName]['selected_categories'])) { 1378 $field['selected_categories'] = $fieldsProcessed[$permName]['selected_categories']; 1379 } 1380 if (isset($field['freetags'])) { 1381 $freetags = trim($fieldsProcessed[$permName]); 1382 $freetags = explode(' ', $freetags); 1383 $field['freetags'] = $freetags; 1384 } 1385 if (isset($fieldsProcessed[$permName]['files'])) { 1386 $field['files'] = $fieldsProcessed[$permName]['files']; 1387 } 1388 } 1389 } 1390 1391 $smarty->assign('trackerId', $trackerId); 1392 $smarty->assign('tracker_info', $trackerInfo); 1393 $smarty->assign_by_ref('info', $itemInfo); 1394 $smarty->assign_by_ref('fields', $fields["data"]); 1395 $smarty->assign_by_ref('ins_fields', $ins_fields["data"]); 1396 1397 1398 if ($trackerInfo['useComments'] == 'y') { 1399 $comCount = $trklib->get_item_nb_comments($itemId); 1400 $smarty->assign("comCount", $comCount); 1401 $smarty->assign("canViewCommentsAsItemOwner", $itemObject->canViewComments()); 1402 } 1403 1404 if ($trackerInfo["useAttachments"] == 'y') { 1405 if ($input->removeattach->int()) { 1406 $_REQUEST["show"] = "att"; 1407 } 1408 if ($input->editattach->int()) { 1409 $att = $trklib->get_item_attachment($input->editattach->int()); 1410 $smarty->assign("attach_comment", $att['comment']); 1411 $smarty->assign("attach_version", $att['version']); 1412 $smarty->assign("attach_longdesc", $att['longdesc']); 1413 $smarty->assign("attach_file", $att["filename"]); 1414 $smarty->assign("attId", $att["attId"]); 1415 $_REQUEST["show"] = "att"; 1416 } 1417 // If anything below here is changed, please change lib/wiki-plugins/wikiplugin_attach.php as well. 1418 $attextra = 'n'; 1419 if (strstr($trackerInfo["orderAttachments"], '|')) { 1420 $attextra = 'y'; 1421 } 1422 $attfields = explode(',', strtok($trackerInfo["orderAttachments"], '|')); 1423 $atts = $trklib->list_item_attachments($itemId, 0, -1, 'comment_asc', ''); 1424 $smarty->assign('atts', $atts["data"]); 1425 $smarty->assign('attCount', $atts["cant"]); 1426 $smarty->assign('attfields', $attfields); 1427 $smarty->assign('attextra', $attextra); 1428 } 1429 1430 include_once('tiki-section_options.php'); 1431 1432 ask_ticket('view-trackers-items'); 1433 1434 $smarty->assign('canView', $itemObject->canView()); 1435 1436 // View 1437 $viewItemPretty = [ 1438 'override' => false, 1439 'value' => $trackerInfo['viewItemPretty'], 1440 'type' => 'wiki' 1441 ]; 1442 if (! empty($trackerInfo['viewItemPretty'])) { 1443 // Need to check wether this is a wiki: or tpl: template, bc the smarty template needs to take care of this 1444 if (strpos(strtolower($viewItemPretty['value']), 'wiki:') === false) { 1445 $viewItemPretty['type'] = 'tpl'; 1446 } 1447 } 1448 $smarty->assign('viewItemPretty', $viewItemPretty); 1449 1450 try { 1451 $smarty->assign('print_page', 'y'); 1452 $smarty->display('templates/tracker/preview_item.tpl'); 1453 } catch (SmartyException $e) { 1454 $message = tr('The requested element cannot be displayed. One of the view/edit templates is missing or has errors: %0', $e->getMessage()); 1455 trigger_error($e->getMessage(), E_USER_ERROR); 1456 $smarty->loadPlugin('smarty_modifier_sefurl'); 1457 $access = TikiLib::lib('access'); 1458 $access->redirect(smarty_modifier_sefurl($trackerId, 'tracker'), $message, 302, 'error'); 1459 } 1460 } 1461 1462 /** 1463 * Links wildcard ItemLink entries to the base tracker by cloning wildcard items 1464 * and removes unselected ItemLink entries that were already linked before. 1465 * Used by ItemLink update table button to refresh list of associated entries. 1466 * 1467 * @param JitFilter $input 1468 * @return array|string 1469 * @throws Services_Exception_Denied 1470 * @throws Services_Exception_NotFound 1471 */ 1472 function action_link_items($input) 1473 { 1474 $trackerId = $input->trackerId->int(); 1475 $definition = Tracker_Definition::get($trackerId); 1476 1477 if (! $definition) { 1478 throw new Services_Exception_NotFound; 1479 } 1480 1481 if (! $field = $definition->getField($input->linkField->int())) { 1482 throw new Services_Exception_NotFound; 1483 } 1484 1485 $linkedItemIds = []; 1486 $linkValue = trim($input->linkValue->text()); 1487 1488 foreach ($input->items as $itemId) { 1489 $itemObject = Tracker_Item::fromId($itemId); 1490 1491 if (! $itemObject) { 1492 throw new Services_Exception_NotFound; 1493 } 1494 1495 if (! $itemObject->canView()) { 1496 throw new Services_Exception_Denied(tr("The item to clone isn't visible")); 1497 } 1498 1499 $output = $itemObject->prepareFieldOutput($field); 1500 $currentValue = $output['value']; 1501 1502 if ($currentValue === '*') { 1503 $itemData = $itemObject->getData(); 1504 $itemData['fields'][$field['permName']] = $linkValue; 1505 $itemObject = $this->utilities->cloneItem($definition, $itemData); 1506 $linkedItemIds[] = $itemObject->getId(); 1507 } else { 1508 $this->utilities->updateItem( 1509 $definition, 1510 [ 1511 'itemId' => $itemId, 1512 'fields' => [ 1513 $field['permName'] => $linkValue 1514 ] 1515 ] 1516 ); 1517 $linkedItemIds[] = $itemId; 1518 } 1519 } 1520 1521 $allItemIds = TikiLib::lib('trk')->get_items_list($trackerId, $field['fieldId'], $linkValue); 1522 $toDelete = array_diff($allItemIds, $linkedItemIds); 1523 foreach ($toDelete as $itemId) { 1524 $itemObject = Tracker_Item::fromId($itemId); 1525 1526 if (! $itemObject) { 1527 throw new Services_Exception_NotFound; 1528 } 1529 1530 if (! $itemObject->canRemove()) { 1531 throw new Services_Exception_Denied(tr("Cannot remove item %0 from this tracker", $itemId)); 1532 } 1533 1534 $uncascaded = TikiLib::lib('trk')->findUncascadedDeletes($itemId, $trackerId); 1535 $this->utilities->removeItemAndReferences($definition, $itemObject, $uncascaded, ''); 1536 } 1537 1538 if ($trackerlistParams = $input->asArray('trackerlistParams')) { 1539 include_once 'lib/smarty_tiki/block.wikiplugin.php'; 1540 $trackerlistParams['_name'] = 'trackerlist'; 1541 $trackerlistParams['checkbox'] = preg_replace('#/[\d,]*$#', '/' . implode(',', $linkedItemIds), $trackerlistParams['checkbox']); 1542 return smarty_block_wikiplugin($trackerlistParams, '', TikiLib::lib('smarty')) . TikiLib::lib('header')->output_js(); 1543 } else { 1544 return [ 1545 'status' => 'ok' 1546 ]; 1547 } 1548 } 1549 1550 function action_fetch_item_field($input) 1551 { 1552 $trackerId = $input->trackerId->int(); 1553 $mode = $input->mode->word(); // output|input (default input) 1554 $listMode = $input->listMode->word(); 1555 $definition = Tracker_Definition::get($trackerId); 1556 1557 if (! $definition) { 1558 throw new Services_Exception_NotFound; 1559 } 1560 1561 if (! $field = $definition->getField($input->fieldId->int())) { 1562 throw new Services_Exception_NotFound; 1563 } 1564 1565 if ($itemId = $input->itemId->int()) { 1566 $itemInfo = TikiLib::lib('trk')->get_tracker_item($itemId); 1567 if (! $itemInfo || $itemInfo['trackerId'] != $trackerId) { 1568 throw new Services_Exception_NotFound; 1569 } 1570 1571 $itemObject = Tracker_Item::fromInfo($itemInfo); 1572 if (! $processed = $itemObject->prepareFieldInput($field, $input->none())) { 1573 throw new Services_Exception_Denied; 1574 } 1575 } else { 1576 $itemObject = Tracker_Item::newItem($trackerId); 1577 $processed = $itemObject->prepareFieldInput($field, $input->none()); 1578 } 1579 1580 return [ 1581 'field' => $processed, 1582 'mode' => $mode, 1583 'listMode' => $listMode, 1584 'itemId' => $itemId 1585 ]; 1586 } 1587 1588 function action_set_location($input) 1589 { 1590 $location = $input->location->text(); 1591 1592 if (! $itemId = $input->itemId->int()) { 1593 throw new Services_Exception_MissingValue('itemId'); 1594 } 1595 1596 $itemInfo = TikiLib::lib('trk')->get_tracker_item($itemId); 1597 if (! $itemInfo) { 1598 throw new Services_Exception_NotFound; 1599 } 1600 1601 $trackerId = $itemInfo['trackerId']; 1602 $definition = Tracker_Definition::get($trackerId); 1603 if (! $definition) { 1604 throw new Services_Exception_NotFound; 1605 } 1606 1607 $itemObject = Tracker_Item::fromInfo($itemInfo); 1608 if (! $itemObject->canModify()) { 1609 throw new Services_Exception_Denied; 1610 } 1611 1612 $field = $definition->getGeolocationField(); 1613 if (! $field) { 1614 throw new Services_Exception_NotFound; 1615 } 1616 1617 if ($_SERVER['REQUEST_METHOD'] == 'POST') { 1618 $field = $definition->getField($field); 1619 1620 $this->utilities->updateItem( 1621 $definition, 1622 [ 1623 'itemId' => $itemId, 1624 'status' => $itemInfo['status'], 1625 'fields' => [ 1626 $field['permName'] => $location, 1627 ], 1628 ] 1629 ); 1630 TikiLib::lib('unifiedsearch')->processUpdateQueue(); 1631 TikiLib::events()->trigger('tiki.process.redirect'); // wait for indexing to complete before loading of next request to ensure updated info shown 1632 } 1633 1634 return [ 1635 'trackerId' => $trackerId, 1636 'itemId' => $itemId, 1637 'location' => $location, 1638 ]; 1639 } 1640 1641 function action_remove_item($input) 1642 { 1643 $trackerId = $input->trackerId->int(); 1644 $definition = Tracker_Definition::get($trackerId); 1645 1646 if (! $definition) { 1647 throw new Services_Exception_NotFound; 1648 } 1649 1650 if (! $itemId = $input->itemId->int()) { 1651 throw new Services_Exception_MissingValue('itemId'); 1652 } 1653 1654 $trklib = TikiLib::lib('trk'); 1655 1656 $itemInfo = $trklib->get_tracker_item($itemId); 1657 if (! $itemInfo || $itemInfo['trackerId'] != $trackerId) { 1658 throw new Services_Exception_NotFound; 1659 } 1660 1661 $itemObject = Tracker_Item::fromInfo($itemInfo); 1662 if (! $itemObject->canRemove()) { 1663 throw new Services_Exception_Denied; 1664 } 1665 1666 $uncascaded = $trklib->findUncascadedDeletes($itemId, $trackerId); 1667 1668 if ($_SERVER['REQUEST_METHOD'] == 'POST') { 1669 $this->utilities->removeItemAndReferences($definition, $itemObject, $uncascaded, $input->replacement->int() ?: ''); 1670 1671 Feedback::success(tr('Tracker item %0 has been successfully deleted.', $itemId)); 1672 1673 TikiLib::events()->trigger('tiki.process.redirect'); // wait for indexing to complete before loading of next request to ensure updated info shown 1674 } 1675 1676 return [ 1677 'title' => tr('Remove'), 1678 'trackerId' => $trackerId, 1679 'itemId' => $itemId, 1680 'affectedCount' => count($uncascaded['itemIds']), 1681 ]; 1682 } 1683 1684 function action_remove($input) 1685 { 1686 $trackerId = $input->trackerId->int(); 1687 $confirm = $input->confirm->int(); 1688 1689 $perms = Perms::get('tracker', $trackerId); 1690 if (! $perms->admin_trackers) { 1691 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 1692 } 1693 1694 $definition = Tracker_Definition::get($trackerId); 1695 1696 if (! $definition) { 1697 throw new Services_Exception_NotFound; 1698 } 1699 1700 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $confirm) { 1701 $this->utilities->removeTracker($trackerId); 1702 1703 return [ 1704 'trackerId' => 0, 1705 ]; 1706 } 1707 1708 return [ 1709 'trackerId' => $trackerId, 1710 'name' => $definition->getConfiguration('name'), 1711 'info' => $definition->getInformation(), 1712 ]; 1713 } 1714 1715 //Function to just change the status of the tracker item 1716 function action_update_item_status($input) 1717 { 1718 if ($input->status->word() == 'DONE') { 1719 return [ 1720 'status' => 'DONE', 1721 'redirect' => $input->redirect->word(), 1722 ]; 1723 } 1724 1725 $trackerId = $input->trackerId->int(); 1726 $definition = Tracker_Definition::get($trackerId); 1727 1728 if (! $definition) { 1729 throw new Services_Exception_NotFound; 1730 } 1731 1732 if (! $itemId = $input->itemId->int()) { 1733 throw new Services_Exception_MissingValue('itemId'); 1734 } 1735 1736 $itemInfo = TikiLib::lib('trk')->get_tracker_item($itemId); 1737 if (! $itemInfo || $itemInfo['trackerId'] != $trackerId) { 1738 throw new Services_Exception_NotFound; 1739 } 1740 1741 if (empty($input->item_label->text())) { 1742 $item_label = "item"; 1743 } else { 1744 $item_label = $input->item_label->text(); 1745 } 1746 1747 if (empty($input->title->text())) { 1748 $title = "Change item status"; 1749 } else { 1750 $title = $input->title->text(); 1751 } 1752 1753 if (empty($input->button_label->text())) { 1754 $button_label = "Update " . $item_label; 1755 } else { 1756 $button_label = $input->button_label->text(); 1757 } 1758 1759 $itemObject = Tracker_Item::fromInfo($itemInfo); 1760 if (! $itemObject->canModify()) { 1761 throw new Services_Exception_Denied; 1762 } 1763 1764 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $input->confirm->int()) { 1765 $result = $this->utilities->updateItem( 1766 $definition, 1767 [ 1768 'itemId' => $itemId, 1769 'trackerId' => $trackerId, 1770 'status' => $input->status->text(), 1771 ] 1772 ); 1773 1774 return [ 1775 'FORWARD' => [ 1776 'controller' => 'tracker', 1777 'action' => 'update_item_status', 1778 'status' => 'DONE', 1779 'redirect' => $input->redirect->text(), 1780 ] 1781 ]; 1782 } else { 1783 return [ 1784 'trackerId' => $trackerId, 1785 'itemId' => $itemId, 1786 'item_label' => $item_label, 1787 'status' => $input->status->text(), 1788 'redirect' => $input->redirect->text(), 1789 'confirmation_message' => $input->confirmation_message->text(), 1790 'title' => $title, 1791 'button_label' => $button_label, 1792 ]; 1793 } 1794 if (false === $result) { 1795 throw new Services_Exception(tr('Validation error'), 406); 1796 } 1797 } 1798 1799 function action_clear($input) 1800 { 1801 1802 return TikiLib::lib('tiki')->allocate_extra( 1803 'tracker_clear_items', 1804 function () use ($input) { 1805 $trackerId = $input->trackerId->int(); 1806 $confirm = $input->confirm->int(); 1807 1808 $perms = Perms::get('tracker', $trackerId); 1809 if (! $perms->admin_trackers) { 1810 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 1811 } 1812 1813 $definition = Tracker_Definition::get($trackerId); 1814 1815 if (! $definition) { 1816 throw new Services_Exception_NotFound; 1817 } 1818 1819 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $confirm) { 1820 $this->utilities->clearTracker($trackerId); 1821 1822 return [ 1823 'trackerId' => 0, 1824 ]; 1825 } 1826 1827 return [ 1828 'trackerId' => $trackerId, 1829 'name' => $definition->getConfiguration('name'), 1830 ]; 1831 } 1832 ); 1833 } 1834 1835 function action_replace($input) 1836 { 1837 $trackerId = $input->trackerId->int(); 1838 $confirm = $input->confirm->int(); 1839 1840 $perms = Perms::get('tracker', $trackerId); 1841 if (! $perms->admin_trackers) { 1842 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 1843 } 1844 1845 if ($trackerId) { 1846 $definition = Tracker_Definition::get($trackerId); 1847 1848 if (! $definition) { 1849 throw new Services_Exception_NotFound; 1850 } 1851 } else { 1852 $definition = Tracker_Definition::getDefault(); 1853 } 1854 1855 $cat_type = 'tracker'; 1856 $cat_objid = $trackerId; 1857 1858 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $confirm) { 1859 $name = $input->name->text(); 1860 1861 if (! $name) { 1862 throw new Services_Exception_MissingValue('name'); 1863 } 1864 1865 $data = [ 1866 'name' => $name, 1867 'description' => $input->description->text(), 1868 'descriptionIsParsed' => $input->descriptionIsParsed->int() ? 'y' : 'n', 1869 'fieldPrefix' => $input->fieldPrefix->text(), 1870 'showStatus' => $input->showStatus->int() ? 'y' : 'n', 1871 'showStatusAdminOnly' => $input->showStatusAdminOnly->int() ? 'y' : 'n', 1872 'showCreated' => $input->showCreated->int() ? 'y' : 'n', 1873 'showCreatedView' => $input->showCreatedView->int() ? 'y' : 'n', 1874 'showCreatedBy' => $input->showCreatedBy->int() ? 'y' : 'n', 1875 'showCreatedFormat' => $input->showCreatedFormat->text(), 1876 'showLastModif' => $input->showLastModif->int() ? 'y' : 'n', 1877 'showLastModifView' => $input->showLastModifView->int() ? 'y' : 'n', 1878 'showLastModifBy' => $input->showLastModifBy->int() ? 'y' : 'n', 1879 'showLastModifFormat' => $input->showLastModifFormat->text(), 1880 'defaultOrderKey' => $input->defaultOrderKey->int(), 1881 'defaultOrderDir' => $input->defaultOrderDir->word(), 1882 'doNotShowEmptyField' => $input->doNotShowEmptyField->int() ? 'y' : 'n', 1883 'showPopup' => $input->showPopup->text(), 1884 'defaultStatus' => implode('', (array) $input->defaultStatus->word()), 1885 'newItemStatus' => $input->newItemStatus->word(), 1886 'modItemStatus' => $input->modItemStatus->word(), 1887 'outboundEmail' => $input->outboundEmail->email(), 1888 'simpleEmail' => $input->simpleEmail->int() ? 'y' : 'n', 1889 'userCanSeeOwn' => $input->userCanSeeOwn->int() ? 'y' : 'n', 1890 'groupCanSeeOwn' => $input->groupCanSeeOwn->int() ? 'y' : 'n', 1891 'writerCanModify' => $input->writerCanModify->int() ? 'y' : 'n', 1892 'writerCanRemove' => $input->writerCanRemove->int() ? 'y' : 'n', 1893 'userCanTakeOwnership' => $input->userCanTakeOwnership->int() ? 'y' : 'n', 1894 'oneUserItem' => $input->oneUserItem->int() ? 'y' : 'n', 1895 'writerGroupCanModify' => $input->writerGroupCanModify->int() ? 'y' : 'n', 1896 'writerGroupCanRemove' => $input->writerGroupCanRemove->int() ? 'y' : 'n', 1897 'useRatings' => $input->useRatings->int() ? 'y' : 'n', 1898 'showRatings' => $input->showRatings->int() ? 'y' : 'n', 1899 'ratingOptions' => $input->ratingOptions->text(), 1900 'useComments' => $input->useComments->int() ? 'y' : 'n', 1901 'showComments' => $input->showComments->int() ? 'y' : 'n', 1902 'showLastComment' => $input->showLastComment->int() ? 'y' : 'n', 1903 'saveAndComment' => $input->saveAndComment->int() ? 'y' : 'n', 1904 'useAttachments' => $input->useAttachments->int() ? 'y' : 'n', 1905 'showAttachments' => $input->showAttachments->int() ? 'y' : 'n', 1906 'orderAttachments' => implode(',', $input->orderAttachments->word()), 1907 'start' => $input->start->int() ? $this->readDate($input, 'start') : 0, 1908 'end' => $input->end->int() ? $this->readDate($input, 'end') : 0, 1909 'autoCreateGroup' => $input->autoCreateGroup->int() ? 'y' : 'n', 1910 'autoCreateGroupInc' => $input->autoCreateGroupInc->groupname(), 1911 'autoAssignCreatorGroup' => $input->autoAssignCreatorGroup->int() ? 'y' : 'n', 1912 'autoAssignCreatorGroupDefault' => $input->autoAssignCreatorGroupDefault->int() ? 'y' : 'n', 1913 'autoAssignGroupItem' => $input->autoAssignGroupItem->int() ? 'y' : 'n', 1914 'autoCopyGroup' => $input->autoCopyGroup->int() ? 'y' : 'n', 1915 'viewItemPretty' => $input->viewItemPretty->text(), 1916 'editItemPretty' => $input->editItemPretty->text(), 1917 'autoCreateCategories' => $input->autoCreateCategories->int() ? 'y' : 'n', 1918 'publishRSS' => $input->publishRSS->int() ? 'y' : 'n', 1919 'sectionFormat' => $input->sectionFormat->word(), 1920 'adminOnlyViewEditItem' => $input->adminOnlyViewEditItem->int() ? 'y' : 'n', 1921 'logo' => $input->logo->text(), 1922 'useFormClasses' => $input->useFormClasses->int() ? 'y' : 'n', 1923 'formClasses' => $input->formClasses->text(), 1924 ]; 1925 1926 $trackerId = $this->utilities->updateTracker($trackerId, $data); 1927 1928 $cat_desc = $data['description']; 1929 $cat_name = $data['name']; 1930 $cat_href = "tiki-view_tracker.php?trackerId=" . $trackerId; 1931 $cat_objid = $trackerId; 1932 include "categorize.php"; 1933 1934 $groupforAlert = $input->groupforAlert->groupname(); 1935 1936 if ($groupforAlert) { 1937 $groupalertlib = TikiLib::lib('groupalert'); 1938 $showeachuser = $input->showeachuser->int() ? 'y' : 'n'; 1939 $groupalertlib->AddGroup('tracker', $trackerId, $groupforAlert, $showeachuser); 1940 } 1941 1942 $definition = Tracker_Definition::get($trackerId); 1943 } 1944 1945 include_once("categorize_list.php"); 1946 $trklib = TikiLib::lib('trk'); 1947 $groupalertlib = TikiLib::lib('groupalert'); 1948 $groupforAlert = $groupalertlib->GetGroup('tracker', 'trackerId'); 1949 return [ 1950 'title' => $trackerId ? tr('Edit') . " " . tr('%0', $definition->getConfiguration('name')) : tr('Create Tracker'), 1951 'trackerId' => $trackerId, 1952 'info' => $definition->getInformation(), 1953 'statusTypes' => TikiLib::lib('trk')->status_types(), 1954 'statusList' => preg_split('//', $definition->getConfiguration('defaultStatus', 'o'), -1, PREG_SPLIT_NO_EMPTY), 1955 'sortFields' => $this->getSortFields($definition), 1956 'attachmentAttributes' => $this->getAttachmentAttributes($definition->getConfiguration('orderAttachments', 'created,filesize,hits')), 1957 'startDate' => $this->format($definition->getConfiguration('start'), '%Y-%m-%d'), 1958 'startTime' => $this->format($definition->getConfiguration('start'), '%H:%M'), 1959 'endDate' => $this->format($definition->getConfiguration('end'), '%Y-%m-%d'), 1960 'endTime' => $this->format($definition->getConfiguration('end'), '%H:%M'), 1961 'groupList' => $this->getGroupList(), 1962 'groupforAlert' => $groupforAlert, 1963 'showeachuser' => $groupalertlib->GetShowEachUser('tracker', 'trackerId', $groupforAlert), 1964 'sectionFormats' => $trklib->getGlobalSectionFormats(), 1965 ]; 1966 } 1967 1968 function action_duplicate($input) 1969 { 1970 $confirm = $input->confirm->int(); 1971 1972 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $confirm) { 1973 $trackerId = $input->trackerId->int(); 1974 $perms = Perms::get('tracker', $trackerId); 1975 if (! $perms->admin_trackers || ! Perms::get()->admin_trackers) { 1976 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 1977 } 1978 $definition = Tracker_Definition::get($trackerId); 1979 if (! $definition) { 1980 throw new Services_Exception_NotFound; 1981 } 1982 $name = $input->name->text(); 1983 if (! $name) { 1984 throw new Services_Exception_MissingValue('name'); 1985 } 1986 $newId = $this->utilities->duplicateTracker($trackerId, $name, $input->dupCateg->int(), $input->dupPerms->int()); 1987 return [ 1988 'trackerId' => $newId, 1989 'name' => $name, 1990 ]; 1991 } else { 1992 $trackers = $this->action_list_trackers($input); 1993 return [ 1994 'title' => tr('Duplicate Tracker'), 1995 'trackers' => $trackers["data"], 1996 ]; 1997 } 1998 } 1999 2000 function action_export($input) 2001 { 2002 $trackerId = $input->trackerId->int(); 2003 $filterField = $input->filterfield->string(); 2004 $filterValue = $input->filtervalue->string(); 2005 2006 $perms = Perms::get('tracker', $trackerId); 2007 if (! $perms->export_tracker) { 2008 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 2009 } 2010 2011 $definition = Tracker_Definition::get($trackerId); 2012 2013 if (! $definition) { 2014 throw new Services_Exception_NotFound; 2015 } 2016 2017 if ($perms->admin_trackers) { 2018 $info = $definition->getInformation(); 2019 2020 $out = "[TRACKER]\n"; 2021 2022 foreach ($info as $key => $value) { 2023 if ($key && $value) { 2024 $out .= "$key = $value\n"; 2025 } 2026 } 2027 } else { 2028 $out = null; 2029 } 2030 2031 // Check if can view field otherwise exclude it 2032 $fields = $definition->getFields(); 2033 $item = Tracker_Item::newItem($trackerId); 2034 foreach ($fields as $k => $field) { 2035 if (!$item->canViewField($field['fieldId'])) { 2036 unset($fields[$k]); 2037 } 2038 } 2039 2040 return [ 2041 'title' => tr('Export Items'), 2042 'trackerId' => $trackerId, 2043 'export' => $out, 2044 'fields' => $fields, 2045 'filterfield' => $filterField, 2046 'filtervalue' => $filterValue, 2047 'recordsMax' => $definition->getConfiguration('items'), 2048 ]; 2049 } 2050 2051 function action_export_items($input) 2052 { 2053 @ini_set('max_execution_time', 0); 2054 TikiLib::lib('tiki')->allocate_extra( 2055 'tracker_export_items', 2056 function () use ($input) { 2057 $trackerId = $input->trackerId->int(); 2058 2059 $definition = Tracker_Definition::get($trackerId); 2060 2061 if (! $definition) { 2062 throw new Services_Exception_NotFound; 2063 } 2064 2065 $perms = Perms::get('tracker', $trackerId); 2066 if (! $perms->export_tracker) { 2067 throw new Services_Exception_Denied(tr("You don't have permission to export")); 2068 } 2069 2070 $fields = []; 2071 foreach ((array) $input->listfields->int() as $fieldId) { 2072 if ($f = $definition->getField($fieldId)) { 2073 $fields[$fieldId] = $f; 2074 } 2075 } 2076 2077 if (0 === count($fields)) { 2078 $fields = $definition->getFields(); 2079 } 2080 2081 $filterField = $input->filterfield->string(); 2082 $filterValue = $input->filtervalue->string(); 2083 2084 $showItemId = $input->showItemId->int(); 2085 $showStatus = $input->showStatus->int(); 2086 $showCreated = $input->showCreated->int(); 2087 $showLastModif = $input->showLastModif->int(); 2088 $keepItemlinkId = $input->keepItemlinkId->int(); 2089 $keepCountryId = $input->keepCountryId->int(); 2090 $dateFormatUnixTimestamp = $input->dateFormatUnixTimestamp->int(); 2091 2092 $encoding = $input->encoding->text(); 2093 if (! in_array($encoding, ['UTF-8', 'ISO-8859-1'])) { 2094 $encoding = 'UTF-8'; 2095 } 2096 $separator = $input->separator->none(); 2097 $delimitorR = $input->delimitorR->none(); 2098 $delimitorL = $input->delimitorL->none(); 2099 2100 $cr = $input->CR->none(); 2101 2102 $recordsMax = $input->recordsMax->int(); 2103 $recordsOffset = $input->recordsOffset->int() - 1; 2104 2105 $writeCsv = function ($fields) use ($separator, $delimitorL, $delimitorR, $encoding, $cr) { 2106 $values = []; 2107 foreach ($fields as $v) { 2108 $values[] = "$delimitorL$v$delimitorR"; 2109 } 2110 2111 $line = implode($separator, $values); 2112 $line = str_replace(["\r\n", "\n", "<br/>", "<br />"], $cr, $line); 2113 2114 if ($encoding === 'ISO-8859-1') { 2115 echo utf8_decode($line) . "\n"; 2116 } else { 2117 echo $line . "\n"; 2118 } 2119 }; 2120 2121 session_write_close(); 2122 2123 $trklib = TikiLib::lib('trk'); 2124 $trklib->write_export_header($encoding, $trackerId); 2125 2126 $header = []; 2127 if ($showItemId) { 2128 $header[] = 'itemId'; 2129 } 2130 if ($showStatus) { 2131 $header[] = 'status'; 2132 } 2133 if ($showCreated) { 2134 $header[] = 'created'; 2135 } 2136 if ($showLastModif) { 2137 $header[] = 'lastModif'; 2138 } 2139 foreach ($fields as $f) { 2140 $header[] = $f['name'] . ' -- ' . $f['fieldId']; 2141 } 2142 2143 $writeCsv($header); 2144 2145 /** @noinspection PhpParamsInspection */ 2146 $items = $trklib->list_items($trackerId, $recordsOffset, $recordsMax, 'itemId_asc', $fields, $filterField, $filterValue); 2147 2148 $smarty = TikiLib::lib('smarty'); 2149 $smarty->loadPlugin('smarty_modifier_tiki_short_datetime'); 2150 foreach ($items['data'] as $row) { 2151 $toDisplay = []; 2152 if ($showItemId) { 2153 $toDisplay[] = $row['itemId']; 2154 } 2155 if ($showStatus) { 2156 $toDisplay[] = $row['status']; 2157 } 2158 if ($showCreated) { 2159 if ($dateFormatUnixTimestamp) { 2160 $toDisplay[] = $row['created']; 2161 } else { 2162 $toDisplay[] = smarty_modifier_tiki_short_datetime($row['created'], '', 'n'); 2163 } 2164 } 2165 if ($showLastModif) { 2166 if ($dateFormatUnixTimestamp) { 2167 $toDisplay[] = $row['lastModif']; 2168 } else { 2169 $toDisplay[] = smarty_modifier_tiki_short_datetime($row['lastModif'], '', 'n'); 2170 } 2171 } 2172 foreach ($row['field_values'] as $val) { 2173 if (($keepItemlinkId) && ($val['type'] == 'r')) { 2174 $toDisplay[] = $val['value']; 2175 } elseif (($keepCountryId) && ($val['type'] == 'y')) { 2176 $toDisplay[] = $val['value']; 2177 } elseif (($dateFormatUnixTimestamp) && ($val['type'] == 'f')) { 2178 $toDisplay[] = $val['value']; 2179 } elseif (($dateFormatUnixTimestamp) && ($val['type'] == 'j')) { 2180 $toDisplay[] = $val['value']; 2181 } else { 2182 $toDisplay[] = $trklib->get_field_handler($val, $row)->renderOutput([ 2183 'list_mode' => 'csv', 2184 'CR' => $cr, 2185 'delimitorL' => $delimitorL, 2186 'delimitorR' => $delimitorR, 2187 ]); 2188 } 2189 } 2190 2191 $writeCsv($toDisplay); 2192 } 2193 } 2194 ); 2195 2196 exit; 2197 } 2198 2199 function action_dump_items($input) 2200 { 2201 $trackerId = $input->trackerId->int(); 2202 2203 $definition = Tracker_Definition::get($trackerId); 2204 2205 if (! $definition) { 2206 throw new Services_Exception_NotFound; 2207 } 2208 2209 $perms = Perms::get('tracker', $trackerId); 2210 if (! $perms->export_tracker) { 2211 throw new Services_Exception_Denied(tr("You don't have permission to export")); 2212 } 2213 2214 $trklib = TikiLib::lib('trk'); 2215 $trklib->dump_tracker_csv($trackerId); 2216 exit; 2217 } 2218 2219 function action_export_profile($input) 2220 { 2221 if (! Perms::get()->admin_trackers) { 2222 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 2223 } 2224 2225 $trackerId = $input->trackerId->int(); 2226 2227 $profile = Tiki_Profile::fromString('dummy', ''); 2228 $data = []; 2229 $profileObject = new Tiki_Profile_Object($data, $profile); 2230 $profileTrackerInstallHandler = new Tiki_Profile_InstallHandler_Tracker($profileObject, []); 2231 2232 $export_yaml = $profileTrackerInstallHandler->_export($trackerId, $profileObject); 2233 2234 include_once 'lib/wiki-plugins/wikiplugin_code.php'; 2235 $export_yaml = wikiplugin_code($export_yaml, ['caption' => 'YAML', 'colors' => 'yaml']); 2236 $export_yaml = preg_replace('/~[\/]?np~/', '', $export_yaml); 2237 2238 return [ 2239 'trackerId' => $trackerId, 2240 'yaml' => $export_yaml, 2241 ]; 2242 } 2243 2244 private function trackerName($trackerId) 2245 { 2246 return TikiLib::lib('tiki')->table('tiki_trackers')->fetchOne('name', ['trackerId' => $trackerId]); 2247 } 2248 2249 private function trackerId($trackerName) 2250 { 2251 return TikiLib::lib('tiki')->table('tiki_trackers')->fetchOne('trackerId', ['name' => $trackerName]); 2252 } 2253 2254 private function trackerNameAndId(&$trackerId, &$trackerName) 2255 { 2256 if ($trackerId > 0 && empty($trackerName)) { 2257 $trackerName = $this->trackerName($trackerId); 2258 } elseif ($trackerId < 1 && ! empty($trackerName)) { 2259 $trackerId = $this->trackerId($trackerName); 2260 } 2261 } 2262 2263 function action_import($input) 2264 { 2265 if (! Perms::get()->admin_trackers) { 2266 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 2267 } 2268 2269 unset($success); 2270 $confirm = $input->confirm->int(); 2271 2272 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $confirm) { 2273 $raw = $input->raw->none(); 2274 $preserve = $input->preserve->int(); 2275 2276 $data = TikiLib::lib('tiki')->read_raw($raw); 2277 2278 if (! $data || ! isset($data['tracker'])) { 2279 throw new Services_Exception(tr('Invalid data provided'), 400); 2280 } 2281 2282 $data = $data['tracker']; 2283 2284 $trackerId = 0; 2285 if ($preserve) { 2286 $trackerId = (int) $data['trackerId']; 2287 } 2288 2289 unset($data['trackerId']); 2290 $trackerId = $this->utilities->updateTracker($trackerId, $data); 2291 $success = 1; 2292 2293 return [ 2294 'trackerId' => $trackerId, 2295 'name' => $data['name'], 2296 'success' => $success, 2297 ]; 2298 } 2299 2300 return [ 2301 'title' => tr('Import Tracker Structure'), 2302 'modal' => $input->modal->int(), 2303 ]; 2304 } 2305 2306 function action_import_items($input) 2307 { 2308 $trackerId = $input->trackerId->int(); 2309 2310 $perms = Perms::get('tracker', $trackerId); 2311 if (! $perms->admin_trackers) { 2312 throw new Services_Exception_Denied(tr('Reserved for tracker administrators')); 2313 } 2314 2315 $definition = Tracker_Definition::get($trackerId); 2316 2317 if (! $definition) { 2318 throw new Services_Exception_NotFound; 2319 } 2320 2321 if (isset($_FILES['importfile'])) { 2322 if (! is_uploaded_file($_FILES['importfile']['tmp_name'])) { 2323 throw new Services_Exception(tr('File upload failed.'), 400); 2324 } 2325 2326 if (! $fp = @ fopen($_FILES['importfile']['tmp_name'], "rb")) { 2327 throw new Services_Exception(tr('Uploaded file could not be read.'), 500); 2328 } 2329 2330 $trklib = TikiLib::lib('trk'); 2331 $count = $trklib->import_csv( 2332 $trackerId, 2333 $fp, 2334 ($input->add_items->int() !== 1), // checkbox is "Create as new items" - param is replace_rows 2335 $input->dateFormat->text(), 2336 $input->encoding->text(), 2337 $input->separator->text(), 2338 $input->updateLastModif->int(), 2339 $input->convertItemLinkValues->int() 2340 ); 2341 2342 fclose($fp); 2343 2344 return [ 2345 'trackerId' => $trackerId, 2346 'return' => $count, 2347 'importfile' => $_FILES['importfile']['name'], 2348 ]; 2349 } 2350 2351 return [ 2352 'title' => tr('Import Items'), 2353 'trackerId' => $trackerId, 2354 'return' => '', 2355 ]; 2356 } 2357 2358 function action_vote($input) 2359 { 2360 $requestData = []; 2361 $requestData['itemId'] = $input->i->int(); 2362 $requestData['fieldId'] = $input->f->int(); 2363 $requestData['vote'] = 'y'; 2364 2365 $v = $input->v->text(); 2366 if ($v !== 'NULL') { 2367 $v = $input->v->int(); 2368 } 2369 $requestData['ins_' . $requestData['fieldId']] = $v; 2370 2371 $trklib = TikiLib::lib('trk'); 2372 $field = $trklib->get_tracker_field($requestData['fieldId']); 2373 2374 $handler = $trklib->get_field_handler($field); 2375 2376 $result = $handler->getFieldData($requestData); 2377 2378 return [$result]; 2379 } 2380 2381 public function action_import_profile($input) 2382 { 2383 $tikilib = TikiLib::lib('tiki'); 2384 2385 $perms = Perms::get(); 2386 if (! $perms->admin) { 2387 throw new Services_Exception_Denied(tr('Reserved for administrators')); 2388 } 2389 2390 unset($success); 2391 $confirm = $input->confirm->int(); 2392 2393 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $confirm) { 2394 $transaction = $tikilib->begin(); 2395 $installer = new Tiki_Profile_Installer; 2396 2397 $yaml = $input->yaml->text(); 2398 $name = "tracker_import:" . md5($yaml); 2399 $profile = Tiki_Profile::fromString('{CODE(caption="yaml")}' . "\n" . $yaml . "\n" . '{CODE}', $name); 2400 2401 if ($installer->isInstallable($profile) == true) { 2402 if ($installer->isInstalled($profile) == true) { 2403 $installer->forget($profile); 2404 } 2405 2406 $installer->install($profile); 2407 $feedback = $installer->getFeedback(); 2408 $transaction->commit(); 2409 return $feedback; 2410 $success = 1; 2411 } else { 2412 return false; 2413 } 2414 } 2415 return [ 2416 'title' => tr('Import Tracker From Profile/YAML'), 2417 'modal' => $input->modal->int(), 2418 ]; 2419 } 2420 2421 private function getSortFields($definition) 2422 { 2423 $sorts = []; 2424 2425 foreach ($definition->getFields() as $field) { 2426 $sorts[$field['fieldId']] = $field['name']; 2427 } 2428 2429 $sorts[-1] = tr('Last Modification'); 2430 $sorts[-2] = tr('Creation Date'); 2431 $sorts[-3] = tr('Item ID'); 2432 2433 return $sorts; 2434 } 2435 2436 private function getAttachmentAttributes($active) 2437 { 2438 $active = explode(',', $active); 2439 2440 $available = [ 2441 'filename' => tr('Filename'), 2442 'created' => tr('Creation date'), 2443 'hits' => tr('Views'), 2444 'comment' => tr('Comment'), 2445 'filesize' => tr('File size'), 2446 'version' => tr('Version'), 2447 'filetype' => tr('File type'), 2448 'longdesc' => tr('Long description'), 2449 'user' => tr('User'), 2450 ]; 2451 2452 $active = array_intersect(array_keys($available), $active); 2453 2454 $attributes = array_fill_keys($active, null); 2455 foreach ($available as $key => $label) { 2456 $attributes[$key] = ['label' => $label, 'selected' => in_array($key, $active)]; 2457 } 2458 2459 return $attributes; 2460 } 2461 2462 private function readDate($input, $prefix) 2463 { 2464 $date = $input->{$prefix . 'Date'}->text(); 2465 $time = $input->{$prefix . 'Time'}->text(); 2466 2467 if (! $time) { 2468 $time = '00:00'; 2469 } 2470 2471 list($year, $month, $day) = explode('-', $date); 2472 list($hour, $minute) = explode(':', $time); 2473 $second = 0; 2474 2475 $tikilib = TikiLib::lib('tiki'); 2476 $tikidate = TikiLib::lib('tikidate'); 2477 $display_tz = $tikilib->get_display_timezone(); 2478 if ($display_tz == '') { 2479 $display_tz = 'UTC'; 2480 } 2481 $tikidate->setTZbyID($display_tz); 2482 $tikidate->setLocalTime($day, $month, $year, $hour, $minute, $second, 0); 2483 return $tikidate->getTime(); 2484 } 2485 2486 private function format($date, $format) 2487 { 2488 if ($date) { 2489 return TikiLib::date_format($format, $date); 2490 } 2491 } 2492 2493 private function getGroupList() 2494 { 2495 $userlib = TikiLib::lib('user'); 2496 $groups = $userlib->list_all_groupIds(); 2497 $out = []; 2498 2499 foreach ($groups as $g) { 2500 $out[] = $g['groupName']; 2501 } 2502 2503 return $out; 2504 } 2505 2506 function action_select_tracker($input) 2507 { 2508 $confirm = $input->confirm->int(); 2509 2510 if ($confirm) { 2511 $trackerId = $input->trackerId->int(); 2512 return [ 2513 'FORWARD' => [ 2514 'action' => 'insert_item', 2515 'trackerId' => $trackerId, 2516 ], 2517 ]; 2518 } else { 2519 $trklib = TikiLib::lib('trk'); 2520 $trackers = $trklib->list_trackers(); 2521 return [ 2522 'title' => tr('Select Tracker'), 2523 'trackers' => $trackers["data"], 2524 ]; 2525 } 2526 } 2527 2528 function action_search_help($input) 2529 { 2530 return [ 2531 'title' => tr('Help'), 2532 ]; 2533 } 2534 2535 function get_validation_options($formId = '') 2536 { 2537 $jsString = ', 2538 onkeyup: false, 2539 errorClass: "invalid-feedback", 2540 errorPlacement: function(error,element) { 2541 if ($(element).parents(".input-group").length > 0) { 2542 error.insertAfter($(element).parents(".input-group").first()); 2543 } else { 2544 error.appendTo($(element).parents().first()); 2545 } 2546 }, 2547 highlight: function(element) { 2548 $(element).addClass("is-invalid"); 2549 2550 // Highlight chosen element if exists 2551 $("#" + element.getAttribute("id") + "_chosen").addClass("is-invalid"); 2552 }, 2553 unhighlight: function(element) { 2554 $(element).removeClass("is-invalid"); 2555 2556 // Unhighlight chosen element if exists 2557 $("#" + element.getAttribute("id") + "_chosen").removeClass("is-invalid"); 2558 }, 2559 ignore: ".ignore" 2560 });'; 2561 2562 if ($formId) { 2563 $jsString .= "\n" . ' 2564 $("' . $formId . '").on("click.validate", ":submit", function(){$("' . $formId . '").find("[name^=other_ins_]").each(function(key, item){$(item).data("tiki_never_visited","")})}); 2565 '; 2566 } 2567 2568 return $jsString; 2569 } 2570 2571 function action_itemslist_output($input) 2572 { 2573 $trklib = TikiLib::lib('trk'); 2574 $field = $trklib->get_tracker_field($input->field->int()); 2575 if (! $field) { 2576 return ''; 2577 } 2578 $fieldHandler = $trklib->get_field_handler($field, [ 2579 $input->fieldIdHere->int() => $input->value->text() 2580 ]); 2581 if (! $fieldHandler) { 2582 return ''; 2583 } 2584 return $fieldHandler->renderOutput(); 2585 } 2586} 2587