[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Administrator 5 * @subpackage com_fields 6 * 7 * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\Component\Fields\Administrator\Model; 12 13 use Joomla\CMS\Categories\CategoryServiceInterface; 14 use Joomla\CMS\Categories\SectionNotFoundException; 15 use Joomla\CMS\Component\ComponentHelper; 16 use Joomla\CMS\Factory; 17 use Joomla\CMS\Filesystem\Path; 18 use Joomla\CMS\Form\Form; 19 use Joomla\CMS\Form\FormHelper; 20 use Joomla\CMS\Language\Text; 21 use Joomla\CMS\MVC\Factory\MVCFactoryInterface; 22 use Joomla\CMS\MVC\Model\AdminModel; 23 use Joomla\CMS\Plugin\PluginHelper; 24 use Joomla\CMS\Table\Table; 25 use Joomla\Component\Fields\Administrator\Helper\FieldsHelper; 26 use Joomla\Database\DatabaseAwareInterface; 27 use Joomla\Database\DatabaseInterface; 28 use Joomla\Database\Exception\DatabaseNotFoundException; 29 use Joomla\Database\ParameterType; 30 use Joomla\Registry\Registry; 31 use Joomla\String\StringHelper; 32 use Joomla\Utilities\ArrayHelper; 33 34 // phpcs:disable PSR1.Files.SideEffects 35 \defined('_JEXEC') or die; 36 // phpcs:enable PSR1.Files.SideEffects 37 38 /** 39 * Field Model 40 * 41 * @since 3.7.0 42 */ 43 class FieldModel extends AdminModel 44 { 45 /** 46 * @var null|string 47 * 48 * @since 3.7.0 49 */ 50 public $typeAlias = null; 51 52 /** 53 * @var string 54 * 55 * @since 3.7.0 56 */ 57 protected $text_prefix = 'COM_FIELDS'; 58 59 /** 60 * Batch copy/move command. If set to false, 61 * the batch copy/move command is not supported 62 * 63 * @var string 64 * @since 3.4 65 */ 66 protected $batch_copymove = 'group_id'; 67 68 /** 69 * Allowed batch commands 70 * 71 * @var array 72 */ 73 protected $batch_commands = array( 74 'assetgroup_id' => 'batchAccess', 75 'language_id' => 'batchLanguage' 76 ); 77 78 /** 79 * @var array 80 * 81 * @since 3.7.0 82 */ 83 private $valueCache = array(); 84 85 /** 86 * Constructor 87 * 88 * @param array $config An array of configuration options (name, state, dbo, table_path, ignore_request). 89 * @param MVCFactoryInterface $factory The factory. 90 * 91 * @since 3.7.0 92 * @throws \Exception 93 */ 94 public function __construct($config = array(), MVCFactoryInterface $factory = null) 95 { 96 parent::__construct($config, $factory); 97 98 $this->typeAlias = Factory::getApplication()->input->getCmd('context', 'com_content.article') . '.field'; 99 } 100 101 /** 102 * Method to save the form data. 103 * 104 * @param array $data The form data. 105 * 106 * @return boolean True on success, False on error. 107 * 108 * @since 3.7.0 109 */ 110 public function save($data) 111 { 112 $field = null; 113 114 if (isset($data['id']) && $data['id']) { 115 $field = $this->getItem($data['id']); 116 } 117 118 if (!isset($data['label']) && isset($data['params']['label'])) { 119 $data['label'] = $data['params']['label']; 120 121 unset($data['params']['label']); 122 } 123 124 // Alter the title for save as copy 125 $input = Factory::getApplication()->input; 126 127 if ($input->get('task') == 'save2copy') { 128 $origTable = clone $this->getTable(); 129 $origTable->load($input->getInt('id')); 130 131 if ($data['title'] == $origTable->title) { 132 list($title, $name) = $this->generateNewTitle($data['group_id'], $data['name'], $data['title']); 133 $data['title'] = $title; 134 $data['label'] = $title; 135 $data['name'] = $name; 136 } else { 137 if ($data['name'] == $origTable->name) { 138 $data['name'] = ''; 139 } 140 } 141 142 $data['state'] = 0; 143 } 144 145 // Load the fields plugins, perhaps they want to do something 146 PluginHelper::importPlugin('fields'); 147 148 $message = $this->checkDefaultValue($data); 149 150 if ($message !== true) { 151 $this->setError($message); 152 153 return false; 154 } 155 156 if (!parent::save($data)) { 157 return false; 158 } 159 160 // Save the assigned categories into #__fields_categories 161 $db = $this->getDatabase(); 162 $id = (int) $this->getState('field.id'); 163 164 /** 165 * If the field is only used in subform, set Category to None automatically so that it will only be displayed 166 * as part of SubForm on add/edit item screen 167 */ 168 if (!empty($data['only_use_in_subform'])) { 169 $cats = [-1]; 170 } else { 171 $cats = isset($data['assigned_cat_ids']) ? (array) $data['assigned_cat_ids'] : array(); 172 $cats = ArrayHelper::toInteger($cats); 173 } 174 175 $assignedCatIds = array(); 176 177 foreach ($cats as $cat) { 178 // If we have found the 'JNONE' category, remove all other from the result and break. 179 if ($cat == '-1') { 180 $assignedCatIds = array('-1'); 181 break; 182 } 183 184 if ($cat) { 185 $assignedCatIds[] = $cat; 186 } 187 } 188 189 // First delete all assigned categories 190 $query = $db->getQuery(true); 191 $query->delete('#__fields_categories') 192 ->where($db->quoteName('field_id') . ' = :fieldid') 193 ->bind(':fieldid', $id, ParameterType::INTEGER); 194 195 $db->setQuery($query); 196 $db->execute(); 197 198 // Inset new assigned categories 199 $tupel = new \stdClass(); 200 $tupel->field_id = $id; 201 202 foreach ($assignedCatIds as $catId) { 203 $tupel->category_id = $catId; 204 $db->insertObject('#__fields_categories', $tupel); 205 } 206 207 /** 208 * If the options have changed, delete the values. This should only apply for list, checkboxes and radio 209 * custom field types, because when their options are being changed, their values might get invalid, because 210 * e.g. there is a value selected from a list, which is not part of the list anymore. Hence we need to delete 211 * all values that are not part of the options anymore. Note: The only field types with fieldparams+options 212 * are those above listed plus the subfields type. And we do explicitly not want the values to be deleted 213 * when the options of a subfields field are getting changed. 214 */ 215 if ( 216 $field && in_array($field->type, array('list', 'checkboxes', 'radio'), true) 217 && isset($data['fieldparams']['options']) && isset($field->fieldparams['options']) 218 ) { 219 $oldParams = $this->getParams($field->fieldparams['options']); 220 $newParams = $this->getParams($data['fieldparams']['options']); 221 222 if (is_object($oldParams) && is_object($newParams) && $oldParams != $newParams) { 223 // Get new values. 224 $names = array_column((array) $newParams, 'value'); 225 226 $fieldId = (int) $field->id; 227 $query = $db->getQuery(true); 228 $query->delete($db->quoteName('#__fields_values')) 229 ->where($db->quoteName('field_id') . ' = :fieldid') 230 ->bind(':fieldid', $fieldId, ParameterType::INTEGER); 231 232 // If new values are set, delete only old values. Otherwise delete all values. 233 if ($names) { 234 $query->whereNotIn($db->quoteName('value'), $names, ParameterType::STRING); 235 } 236 237 $db->setQuery($query); 238 $db->execute(); 239 } 240 } 241 242 FieldsHelper::clearFieldsCache(); 243 244 return true; 245 } 246 247 248 /** 249 * Checks if the default value is valid for the given data. If a string is returned then 250 * it can be assumed that the default value is invalid. 251 * 252 * @param array $data The data. 253 * 254 * @return true|string true if valid, a string containing the exception message when not. 255 * 256 * @since 3.7.0 257 */ 258 private function checkDefaultValue($data) 259 { 260 // Empty default values are correct 261 if (empty($data['default_value']) && $data['default_value'] !== '0') { 262 return true; 263 } 264 265 $types = FieldsHelper::getFieldTypes(); 266 267 // Check if type exists 268 if (!array_key_exists($data['type'], $types)) { 269 return true; 270 } 271 272 $path = $types[$data['type']]['rules']; 273 274 // Add the path for the rules of the plugin when available 275 if ($path) { 276 // Add the lookup path for the rule 277 FormHelper::addRulePath($path); 278 } 279 280 // Create the fields object 281 $obj = (object) $data; 282 $obj->params = new Registry($obj->params); 283 $obj->fieldparams = new Registry(!empty($obj->fieldparams) ? $obj->fieldparams : array()); 284 285 // Prepare the dom 286 $dom = new \DOMDocument(); 287 $node = $dom->appendChild(new \DOMElement('form')); 288 289 // Trigger the event to create the field dom node 290 $form = new Form($data['context']); 291 $form->setDatabase($this->getDatabase()); 292 Factory::getApplication()->triggerEvent('onCustomFieldsPrepareDom', array($obj, $node, $form)); 293 294 // Check if a node is created 295 if (!$node->firstChild) { 296 return true; 297 } 298 299 // Define the type either from the field or from the data 300 $type = $node->firstChild->getAttribute('validate') ? : $data['type']; 301 302 // Load the rule 303 $rule = FormHelper::loadRuleType($type); 304 305 // When no rule exists, we allow the default value 306 if (!$rule) { 307 return true; 308 } 309 310 if ($rule instanceof DatabaseAwareInterface) { 311 try { 312 $rule->setDatabase($this->getDatabase()); 313 } catch (DatabaseNotFoundException $e) { 314 @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED); 315 $rule->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); 316 } 317 } 318 319 try { 320 // Perform the check 321 $result = $rule->test(simplexml_import_dom($node->firstChild), $data['default_value']); 322 323 // Check if the test succeeded 324 return $result === true ? : Text::_('COM_FIELDS_FIELD_INVALID_DEFAULT_VALUE'); 325 } catch (\UnexpectedValueException $e) { 326 return $e->getMessage(); 327 } 328 } 329 330 /** 331 * Converts the unknown params into an object. 332 * 333 * @param mixed $params The params. 334 * 335 * @return \stdClass Object on success, false on failure. 336 * 337 * @since 3.7.0 338 */ 339 private function getParams($params) 340 { 341 if (is_string($params)) { 342 $params = json_decode($params); 343 } 344 345 if (is_array($params)) { 346 $params = (object) $params; 347 } 348 349 return $params; 350 } 351 352 /** 353 * Method to get a single record. 354 * 355 * @param integer $pk The id of the primary key. 356 * 357 * @return mixed Object on success, false on failure. 358 * 359 * @since 3.7.0 360 */ 361 public function getItem($pk = null) 362 { 363 $result = parent::getItem($pk); 364 365 if ($result) { 366 // Prime required properties. 367 if (empty($result->id)) { 368 $result->context = Factory::getApplication()->input->getCmd('context', $this->getState('field.context')); 369 } 370 371 if (property_exists($result, 'fieldparams') && $result->fieldparams !== null) { 372 $registry = new Registry(); 373 374 if ($result->fieldparams) { 375 $registry->loadString($result->fieldparams); 376 } 377 378 $result->fieldparams = $registry->toArray(); 379 } 380 381 $db = $this->getDatabase(); 382 $query = $db->getQuery(true); 383 $fieldId = (int) $result->id; 384 $query->select($db->quoteName('category_id')) 385 ->from($db->quoteName('#__fields_categories')) 386 ->where($db->quoteName('field_id') . ' = :fieldid') 387 ->bind(':fieldid', $fieldId, ParameterType::INTEGER); 388 389 $db->setQuery($query); 390 $result->assigned_cat_ids = $db->loadColumn() ?: array(0); 391 } 392 393 return $result; 394 } 395 396 /** 397 * Method to get a table object, load it if necessary. 398 * 399 * @param string $name The table name. Optional. 400 * @param string $prefix The class prefix. Optional. 401 * @param array $options Configuration array for model. Optional. 402 * 403 * @return Table A Table object 404 * 405 * @since 3.7.0 406 * @throws \Exception 407 */ 408 public function getTable($name = 'Field', $prefix = 'Administrator', $options = array()) 409 { 410 // Default to text type 411 $table = parent::getTable($name, $prefix, $options); 412 $table->type = 'text'; 413 414 return $table; 415 } 416 417 /** 418 * Method to change the title & name. 419 * 420 * @param integer $categoryId The id of the category. 421 * @param string $name The name. 422 * @param string $title The title. 423 * 424 * @return array Contains the modified title and name. 425 * 426 * @since 3.7.0 427 */ 428 protected function generateNewTitle($categoryId, $name, $title) 429 { 430 // Alter the title & name 431 $table = $this->getTable(); 432 433 while ($table->load(array('name' => $name))) { 434 $title = StringHelper::increment($title); 435 $name = StringHelper::increment($name, 'dash'); 436 } 437 438 return array( 439 $title, 440 $name, 441 ); 442 } 443 444 /** 445 * Method to delete one or more records. 446 * 447 * @param array $pks An array of record primary keys. 448 * 449 * @return boolean True if successful, false if an error occurs. 450 * 451 * @since 3.7.0 452 */ 453 public function delete(&$pks) 454 { 455 $success = parent::delete($pks); 456 457 if ($success) { 458 $pks = (array) $pks; 459 $pks = ArrayHelper::toInteger($pks); 460 $pks = array_filter($pks); 461 462 if (!empty($pks)) { 463 // Delete Values 464 $query = $this->getDatabase()->getQuery(true); 465 466 $query->delete($query->quoteName('#__fields_values')) 467 ->whereIn($query->quoteName('field_id'), $pks); 468 469 $this->getDatabase()->setQuery($query)->execute(); 470 471 // Delete Assigned Categories 472 $query = $this->getDatabase()->getQuery(true); 473 474 $query->delete($query->quoteName('#__fields_categories')) 475 ->whereIn($query->quoteName('field_id'), $pks); 476 477 $this->getDatabase()->setQuery($query)->execute(); 478 } 479 } 480 481 return $success; 482 } 483 484 /** 485 * Abstract method for getting the form from the model. 486 * 487 * @param array $data Data for the form. 488 * @param boolean $loadData True if the form is to load its own data (default case), false if not. 489 * 490 * @return Form|bool A Form object on success, false on failure 491 * 492 * @since 3.7.0 493 */ 494 public function getForm($data = array(), $loadData = true) 495 { 496 $context = $this->getState('field.context'); 497 $jinput = Factory::getApplication()->input; 498 499 // A workaround to get the context into the model for save requests. 500 if (empty($context) && isset($data['context'])) { 501 $context = $data['context']; 502 $parts = FieldsHelper::extract($context); 503 504 $this->setState('field.context', $context); 505 506 if ($parts) { 507 $this->setState('field.component', $parts[0]); 508 $this->setState('field.section', $parts[1]); 509 } 510 } 511 512 if (isset($data['type'])) { 513 // This is needed that the plugins can determine the type 514 $this->setState('field.type', $data['type']); 515 } 516 517 // Load the fields plugin that they can add additional parameters to the form 518 PluginHelper::importPlugin('fields'); 519 520 // Get the form. 521 $form = $this->loadForm( 522 'com_fields.field.' . $context, 523 'field', 524 array( 525 'control' => 'jform', 526 'load_data' => true, 527 ) 528 ); 529 530 if (empty($form)) { 531 return false; 532 } 533 534 // Modify the form based on Edit State access controls. 535 if (empty($data['context'])) { 536 $data['context'] = $context; 537 } 538 539 $fieldId = $jinput->get('id'); 540 $assetKey = $this->state->get('field.component') . '.field.' . $fieldId; 541 542 if (!Factory::getUser()->authorise('core.edit.state', $assetKey)) { 543 // Disable fields for display. 544 $form->setFieldAttribute('ordering', 'disabled', 'true'); 545 $form->setFieldAttribute('state', 'disabled', 'true'); 546 547 // Disable fields while saving. The controller has already verified this is a record you can edit. 548 $form->setFieldAttribute('ordering', 'filter', 'unset'); 549 $form->setFieldAttribute('state', 'filter', 'unset'); 550 } 551 552 // Don't allow to change the created_user_id user if not allowed to access com_users. 553 if (!Factory::getUser()->authorise('core.manage', 'com_users')) { 554 $form->setFieldAttribute('created_user_id', 'filter', 'unset'); 555 } 556 557 // In case we are editing a field, field type cannot be changed, so some extra handling below is needed 558 if ($fieldId) { 559 $fieldType = $form->getField('type'); 560 561 if ($fieldType->value == 'subform') { 562 // Only Use In subform should not be available for subform field type, so we remove it 563 $form->removeField('only_use_in_subform'); 564 } else { 565 // Field type could not be changed, so remove showon attribute to avoid js errors 566 $form->setFieldAttribute('only_use_in_subform', 'showon', ''); 567 } 568 } 569 570 return $form; 571 } 572 573 /** 574 * Setting the value for the given field id, context and item id. 575 * 576 * @param string $fieldId The field ID. 577 * @param string $itemId The ID of the item. 578 * @param string $value The value. 579 * 580 * @return boolean 581 * 582 * @since 3.7.0 583 */ 584 public function setFieldValue($fieldId, $itemId, $value) 585 { 586 $field = $this->getItem($fieldId); 587 $params = $field->params; 588 589 if (is_array($params)) { 590 $params = new Registry($params); 591 } 592 593 // Don't save the value when the user is not authorized to change it 594 if (!$field || !FieldsHelper::canEditFieldValue($field)) { 595 return false; 596 } 597 598 $needsDelete = false; 599 $needsInsert = false; 600 $needsUpdate = false; 601 602 $oldValue = $this->getFieldValue($fieldId, $itemId); 603 $value = (array) $value; 604 605 if ($oldValue === null) { 606 // No records available, doing normal insert 607 $needsInsert = true; 608 } elseif (count($value) == 1 && count((array) $oldValue) == 1) { 609 // Only a single row value update can be done when not empty 610 $needsUpdate = is_array($value[0]) ? count($value[0]) : strlen($value[0]); 611 $needsDelete = !$needsUpdate; 612 } else { 613 // Multiple values, we need to purge the data and do a new 614 // insert 615 $needsDelete = true; 616 $needsInsert = true; 617 } 618 619 if ($needsDelete) { 620 $fieldId = (int) $fieldId; 621 622 // Deleting the existing record as it is a reset 623 $query = $this->getDatabase()->getQuery(true); 624 625 $query->delete($query->quoteName('#__fields_values')) 626 ->where($query->quoteName('field_id') . ' = :fieldid') 627 ->where($query->quoteName('item_id') . ' = :itemid') 628 ->bind(':fieldid', $fieldId, ParameterType::INTEGER) 629 ->bind(':itemid', $itemId); 630 631 $this->getDatabase()->setQuery($query)->execute(); 632 } 633 634 if ($needsInsert) { 635 $newObj = new \stdClass(); 636 637 $newObj->field_id = (int) $fieldId; 638 $newObj->item_id = $itemId; 639 640 foreach ($value as $v) { 641 $newObj->value = $v; 642 643 $this->getDatabase()->insertObject('#__fields_values', $newObj); 644 } 645 } 646 647 if ($needsUpdate) { 648 $updateObj = new \stdClass(); 649 650 $updateObj->field_id = (int) $fieldId; 651 $updateObj->item_id = $itemId; 652 $updateObj->value = reset($value); 653 654 $this->getDatabase()->updateObject('#__fields_values', $updateObj, array('field_id', 'item_id')); 655 } 656 657 $this->valueCache = array(); 658 FieldsHelper::clearFieldsCache(); 659 660 return true; 661 } 662 663 /** 664 * Returning the value for the given field id, context and item id. 665 * 666 * @param string $fieldId The field ID. 667 * @param string $itemId The ID of the item. 668 * 669 * @return NULL|string 670 * 671 * @since 3.7.0 672 */ 673 public function getFieldValue($fieldId, $itemId) 674 { 675 $values = $this->getFieldValues(array($fieldId), $itemId); 676 677 if (array_key_exists($fieldId, $values)) { 678 return $values[$fieldId]; 679 } 680 681 return null; 682 } 683 684 /** 685 * Returning the values for the given field ids, context and item id. 686 * 687 * @param array $fieldIds The field Ids. 688 * @param string $itemId The ID of the item. 689 * 690 * @return NULL|array 691 * 692 * @since 3.7.0 693 */ 694 public function getFieldValues(array $fieldIds, $itemId) 695 { 696 if (!$fieldIds) { 697 return array(); 698 } 699 700 // Create a unique key for the cache 701 $key = md5(serialize($fieldIds) . $itemId); 702 703 // Fill the cache when it doesn't exist 704 if (!array_key_exists($key, $this->valueCache)) { 705 // Create the query 706 $query = $this->getDatabase()->getQuery(true); 707 708 $query->select($query->quoteName(['field_id', 'value'])) 709 ->from($query->quoteName('#__fields_values')) 710 ->whereIn($query->quoteName('field_id'), ArrayHelper::toInteger($fieldIds)) 711 ->where($query->quoteName('item_id') . ' = :itemid') 712 ->bind(':itemid', $itemId); 713 714 // Fetch the row from the database 715 $rows = $this->getDatabase()->setQuery($query)->loadObjectList(); 716 717 $data = array(); 718 719 // Fill the data container from the database rows 720 foreach ($rows as $row) { 721 // If there are multiple values for a field, create an array 722 if (array_key_exists($row->field_id, $data)) { 723 // Transform it to an array 724 if (!is_array($data[$row->field_id])) { 725 $data[$row->field_id] = array($data[$row->field_id]); 726 } 727 728 // Set the value in the array 729 $data[$row->field_id][] = $row->value; 730 731 // Go to the next row, otherwise the value gets overwritten in the data container 732 continue; 733 } 734 735 // Set the value 736 $data[$row->field_id] = $row->value; 737 } 738 739 // Assign it to the internal cache 740 $this->valueCache[$key] = $data; 741 } 742 743 // Return the value from the cache 744 return $this->valueCache[$key]; 745 } 746 747 /** 748 * Cleaning up the values for the given item on the context. 749 * 750 * @param string $context The context. 751 * @param string $itemId The Item ID. 752 * 753 * @return void 754 * 755 * @since 3.7.0 756 */ 757 public function cleanupValues($context, $itemId) 758 { 759 // Delete with inner join is not possible so we need to do a subquery 760 $fieldsQuery = $this->getDatabase()->getQuery(true); 761 $fieldsQuery->select($fieldsQuery->quoteName('id')) 762 ->from($fieldsQuery->quoteName('#__fields')) 763 ->where($fieldsQuery->quoteName('context') . ' = :context'); 764 765 $query = $this->getDatabase()->getQuery(true); 766 767 $query->delete($query->quoteName('#__fields_values')) 768 ->where($query->quoteName('field_id') . ' IN (' . $fieldsQuery . ')') 769 ->where($query->quoteName('item_id') . ' = :itemid') 770 ->bind(':itemid', $itemId) 771 ->bind(':context', $context); 772 773 $this->getDatabase()->setQuery($query)->execute(); 774 } 775 776 /** 777 * Method to test whether a record can be deleted. 778 * 779 * @param object $record A record object. 780 * 781 * @return boolean True if allowed to delete the record. Defaults to the permission for the component. 782 * 783 * @since 3.7.0 784 */ 785 protected function canDelete($record) 786 { 787 if (empty($record->id) || $record->state != -2) { 788 return false; 789 } 790 791 $parts = FieldsHelper::extract($record->context); 792 793 return Factory::getUser()->authorise('core.delete', $parts[0] . '.field.' . (int) $record->id); 794 } 795 796 /** 797 * Method to test whether a record can have its state changed. 798 * 799 * @param object $record A record object. 800 * 801 * @return boolean True if allowed to change the state of the record. Defaults to the permission for the 802 * component. 803 * 804 * @since 3.7.0 805 */ 806 protected function canEditState($record) 807 { 808 $user = Factory::getUser(); 809 $parts = FieldsHelper::extract($record->context); 810 811 // Check for existing field. 812 if (!empty($record->id)) { 813 return $user->authorise('core.edit.state', $parts[0] . '.field.' . (int) $record->id); 814 } 815 816 return $user->authorise('core.edit.state', $parts[0]); 817 } 818 819 /** 820 * Stock method to auto-populate the model state. 821 * 822 * @return void 823 * 824 * @since 3.7.0 825 */ 826 protected function populateState() 827 { 828 $app = Factory::getApplication(); 829 830 // Load the User state. 831 $pk = $app->input->getInt('id'); 832 $this->setState($this->getName() . '.id', $pk); 833 834 $context = $app->input->get('context', 'com_content.article'); 835 $this->setState('field.context', $context); 836 $parts = FieldsHelper::extract($context); 837 838 // Extract the component name 839 $this->setState('field.component', $parts[0]); 840 841 // Extract the optional section name 842 $this->setState('field.section', (count($parts) > 1) ? $parts[1] : null); 843 844 // Load the parameters. 845 $params = ComponentHelper::getParams('com_fields'); 846 $this->setState('params', $params); 847 } 848 849 /** 850 * A protected method to get a set of ordering conditions. 851 * 852 * @param Table $table A Table object. 853 * 854 * @return array An array of conditions to add to ordering queries. 855 * 856 * @since 3.7.0 857 */ 858 protected function getReorderConditions($table) 859 { 860 $db = $this->getDatabase(); 861 862 return [ 863 $db->quoteName('context') . ' = ' . $db->quote($table->context), 864 ]; 865 } 866 867 /** 868 * Method to get the data that should be injected in the form. 869 * 870 * @return array The default data is an empty array. 871 * 872 * @since 3.7.0 873 */ 874 protected function loadFormData() 875 { 876 // Check the session for previously entered form data. 877 $app = Factory::getApplication(); 878 $data = $app->getUserState('com_fields.edit.field.data', array()); 879 880 if (empty($data)) { 881 $data = $this->getItem(); 882 883 // Pre-select some filters (Status, Language, Access) in edit form 884 // if those have been selected in Category Manager 885 if (!$data->id) { 886 // Check for which context the Category Manager is used and 887 // get selected fields 888 $filters = (array) $app->getUserState('com_fields.fields.filter'); 889 890 $data->set('state', $app->input->getInt('state', ((isset($filters['state']) && $filters['state'] !== '') ? $filters['state'] : null))); 891 $data->set('language', $app->input->getString('language', (!empty($filters['language']) ? $filters['language'] : null))); 892 $data->set('group_id', $app->input->getString('group_id', (!empty($filters['group_id']) ? $filters['group_id'] : null))); 893 $data->set( 894 'access', 895 $app->input->getInt('access', (!empty($filters['access']) ? $filters['access'] : $app->get('access'))) 896 ); 897 898 // Set the type if available from the request 899 $data->set('type', $app->input->getWord('type', $this->state->get('field.type', $data->get('type')))); 900 } 901 902 if ($data->label && !isset($data->params['label'])) { 903 $data->params['label'] = $data->label; 904 } 905 } 906 907 $this->preprocessData('com_fields.field', $data); 908 909 return $data; 910 } 911 912 /** 913 * Method to validate the form data. 914 * 915 * @param Form $form The form to validate against. 916 * @param array $data The data to validate. 917 * @param string $group The name of the field group to validate. 918 * 919 * @return array|boolean Array of filtered data if valid, false otherwise. 920 * 921 * @see JFormRule 922 * @see JFilterInput 923 * @since 3.9.23 924 */ 925 public function validate($form, $data, $group = null) 926 { 927 if (!Factory::getUser()->authorise('core.admin', 'com_fields')) { 928 if (isset($data['rules'])) { 929 unset($data['rules']); 930 } 931 } 932 933 return parent::validate($form, $data, $group); 934 } 935 936 /** 937 * Method to allow derived classes to preprocess the form. 938 * 939 * @param Form $form A Form object. 940 * @param mixed $data The data expected for the form. 941 * @param string $group The name of the plugin group to import (defaults to "content"). 942 * 943 * @return void 944 * 945 * @since 3.7.0 946 * 947 * @throws \Exception if there is an error in the form event. 948 * 949 * @see \Joomla\CMS\Form\FormField 950 */ 951 protected function preprocessForm(Form $form, $data, $group = 'content') 952 { 953 $component = $this->state->get('field.component'); 954 $section = $this->state->get('field.section'); 955 $dataObject = $data; 956 957 if (is_array($dataObject)) { 958 $dataObject = (object) $dataObject; 959 } 960 961 if (isset($dataObject->type)) { 962 $form->setFieldAttribute('type', 'component', $component); 963 964 // Not allowed to change the type of an existing record 965 if ($dataObject->id) { 966 $form->setFieldAttribute('type', 'readonly', 'true'); 967 } 968 969 // Allow to override the default value label and description through the plugin 970 $key = 'PLG_FIELDS_' . strtoupper($dataObject->type) . '_DEFAULT_VALUE_LABEL'; 971 972 if (Factory::getLanguage()->hasKey($key)) { 973 $form->setFieldAttribute('default_value', 'label', $key); 974 } 975 976 $key = 'PLG_FIELDS_' . strtoupper($dataObject->type) . '_DEFAULT_VALUE_DESC'; 977 978 if (Factory::getLanguage()->hasKey($key)) { 979 $form->setFieldAttribute('default_value', 'description', $key); 980 } 981 982 // Remove placeholder field on list fields 983 if ($dataObject->type == 'list') { 984 $form->removeField('hint', 'params'); 985 } 986 } 987 988 // Get the categories for this component (and optionally this section, if available) 989 $cat = ( 990 function () use ($component, $section) { 991 // Get the CategoryService for this component 992 $componentObject = $this->bootComponent($component); 993 994 if (!$componentObject instanceof CategoryServiceInterface) { 995 // No CategoryService -> no categories 996 return null; 997 } 998 999 $cat = null; 1000 1001 // Try to get the categories for this component and section 1002 try { 1003 $cat = $componentObject->getCategory([], $section ?: ''); 1004 } catch (SectionNotFoundException $e) { 1005 // Not found for component and section -> Now try once more without the section, so only component 1006 try { 1007 $cat = $componentObject->getCategory(); 1008 } catch (SectionNotFoundException $e) { 1009 // If we haven't found it now, return (no categories available for this component) 1010 return null; 1011 } 1012 } 1013 1014 // So we found categories for at least the component, return them 1015 return $cat; 1016 } 1017 )(); 1018 1019 // If we found categories, and if the root category has children, set them in the form 1020 if ($cat && $cat->get('root')->hasChildren()) { 1021 $form->setFieldAttribute('assigned_cat_ids', 'extension', $cat->getExtension()); 1022 } else { 1023 // Else remove the field from the form 1024 $form->removeField('assigned_cat_ids'); 1025 } 1026 1027 $form->setFieldAttribute('type', 'component', $component); 1028 $form->setFieldAttribute('group_id', 'context', $this->state->get('field.context')); 1029 $form->setFieldAttribute('rules', 'component', $component); 1030 1031 // Looking in the component forms folder for a specific section forms file 1032 $path = Path::clean(JPATH_ADMINISTRATOR . '/components/' . $component . '/forms/fields/' . $section . '.xml'); 1033 1034 if (!file_exists($path)) { 1035 // Looking in the component models/forms folder for a specific section forms file 1036 $path = Path::clean(JPATH_ADMINISTRATOR . '/components/' . $component . '/models/forms/fields/' . $section . '.xml'); 1037 } 1038 1039 if (file_exists($path)) { 1040 $lang = Factory::getLanguage(); 1041 $lang->load($component, JPATH_BASE); 1042 $lang->load($component, JPATH_BASE . '/components/' . $component); 1043 1044 if (!$form->loadFile($path, false)) { 1045 throw new \Exception(Text::_('JERROR_LOADFILE_FAILED')); 1046 } 1047 } 1048 1049 // Trigger the default form events. 1050 parent::preprocessForm($form, $data, $group); 1051 } 1052 1053 /** 1054 * Clean the cache 1055 * 1056 * @param string $group The cache group 1057 * @param integer $clientId @deprecated 5.0 No longer used. 1058 * 1059 * @return void 1060 * 1061 * @since 3.7.0 1062 */ 1063 protected function cleanCache($group = null, $clientId = 0) 1064 { 1065 $context = Factory::getApplication()->input->get('context'); 1066 1067 switch ($context) { 1068 case 'com_content': 1069 parent::cleanCache('com_content'); 1070 parent::cleanCache('mod_articles_archive'); 1071 parent::cleanCache('mod_articles_categories'); 1072 parent::cleanCache('mod_articles_category'); 1073 parent::cleanCache('mod_articles_latest'); 1074 parent::cleanCache('mod_articles_news'); 1075 parent::cleanCache('mod_articles_popular'); 1076 break; 1077 default: 1078 parent::cleanCache($context); 1079 break; 1080 } 1081 } 1082 1083 /** 1084 * Batch copy fields to a new group. 1085 * 1086 * @param integer $value The new value matching a fields group. 1087 * @param array $pks An array of row IDs. 1088 * @param array $contexts An array of item contexts. 1089 * 1090 * @return array|boolean new IDs if successful, false otherwise and internal error is set. 1091 * 1092 * @since 3.7.0 1093 */ 1094 protected function batchCopy($value, $pks, $contexts) 1095 { 1096 // Set the variables 1097 $user = Factory::getUser(); 1098 $table = $this->getTable(); 1099 $newIds = array(); 1100 $component = $this->state->get('filter.component'); 1101 $value = (int) $value; 1102 1103 foreach ($pks as $pk) { 1104 if ($user->authorise('core.create', $component . '.fieldgroup.' . $value)) { 1105 $table->reset(); 1106 $table->load($pk); 1107 1108 $table->group_id = $value; 1109 1110 // Reset the ID because we are making a copy 1111 $table->id = 0; 1112 1113 // Unpublish the new field 1114 $table->state = 0; 1115 1116 if (!$table->store()) { 1117 $this->setError($table->getError()); 1118 1119 return false; 1120 } 1121 1122 // Get the new item ID 1123 $newId = $table->get('id'); 1124 1125 // Add the new ID to the array 1126 $newIds[$pk] = $newId; 1127 } else { 1128 $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_CREATE')); 1129 1130 return false; 1131 } 1132 } 1133 1134 // Clean the cache 1135 $this->cleanCache(); 1136 1137 return $newIds; 1138 } 1139 1140 /** 1141 * Batch move fields to a new group. 1142 * 1143 * @param integer $value The new value matching a fields group. 1144 * @param array $pks An array of row IDs. 1145 * @param array $contexts An array of item contexts. 1146 * 1147 * @return boolean True if successful, false otherwise and internal error is set. 1148 * 1149 * @since 3.7.0 1150 */ 1151 protected function batchMove($value, $pks, $contexts) 1152 { 1153 // Set the variables 1154 $user = Factory::getUser(); 1155 $table = $this->getTable(); 1156 $context = explode('.', Factory::getApplication()->getUserState('com_fields.fields.context')); 1157 $value = (int) $value; 1158 1159 foreach ($pks as $pk) { 1160 if ($user->authorise('core.edit', $context[0] . '.fieldgroup.' . $value)) { 1161 $table->reset(); 1162 $table->load($pk); 1163 1164 $table->group_id = $value; 1165 1166 if (!$table->store()) { 1167 $this->setError($table->getError()); 1168 1169 return false; 1170 } 1171 } else { 1172 $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); 1173 1174 return false; 1175 } 1176 } 1177 1178 // Clean the cache 1179 $this->cleanCache(); 1180 1181 return true; 1182 } 1183 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |