[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/administrator/components/com_fields/src/Model/ -> FieldModel.php (source)

   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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer