[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/administrator/components/com_categories/src/Model/ -> CategoryModel.php (source)

   1  <?php
   2  
   3  /**
   4   * @package     Joomla.Administrator
   5   * @subpackage  com_categories
   6   *
   7   * @copyright   (C) 2008 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\Categories\Administrator\Model;
  12  
  13  use Joomla\CMS\Access\Rules;
  14  use Joomla\CMS\Association\AssociationServiceInterface;
  15  use Joomla\CMS\Categories\CategoryServiceInterface;
  16  use Joomla\CMS\Component\ComponentHelper;
  17  use Joomla\CMS\Factory;
  18  use Joomla\CMS\Filesystem\Path;
  19  use Joomla\CMS\Form\Form;
  20  use Joomla\CMS\Helper\TagsHelper;
  21  use Joomla\CMS\Language\Associations;
  22  use Joomla\CMS\Language\LanguageHelper;
  23  use Joomla\CMS\Language\Text;
  24  use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
  25  use Joomla\CMS\MVC\Model\AdminModel;
  26  use Joomla\CMS\Plugin\PluginHelper;
  27  use Joomla\CMS\Table\Category;
  28  use Joomla\CMS\UCM\UCMType;
  29  use Joomla\CMS\Versioning\VersionableModelTrait;
  30  use Joomla\Component\Categories\Administrator\Helper\CategoriesHelper;
  31  use Joomla\Database\ParameterType;
  32  use Joomla\Registry\Registry;
  33  use Joomla\String\StringHelper;
  34  use Joomla\Utilities\ArrayHelper;
  35  
  36  // phpcs:disable PSR1.Files.SideEffects
  37  \defined('_JEXEC') or die;
  38  // phpcs:enable PSR1.Files.SideEffects
  39  
  40  /**
  41   * Categories Component Category Model
  42   *
  43   * @since  1.6
  44   */
  45  class CategoryModel extends AdminModel
  46  {
  47      use VersionableModelTrait;
  48  
  49      /**
  50       * The prefix to use with controller messages.
  51       *
  52       * @var    string
  53       * @since  1.6
  54       */
  55      protected $text_prefix = 'COM_CATEGORIES';
  56  
  57      /**
  58       * The type alias for this content type. Used for content version history.
  59       *
  60       * @var      string
  61       * @since    3.2
  62       */
  63      public $typeAlias = null;
  64  
  65      /**
  66       * The context used for the associations table
  67       *
  68       * @var      string
  69       * @since    3.4.4
  70       */
  71      protected $associationsContext = 'com_categories.item';
  72  
  73      /**
  74       * Does an association exist? Caches the result of getAssoc().
  75       *
  76       * @var   boolean|null
  77       * @since 3.10.4
  78       */
  79      private $hasAssociation;
  80  
  81      /**
  82       * Override parent constructor.
  83       *
  84       * @param   array                     $config   An optional associative array of configuration settings.
  85       * @param   MVCFactoryInterface|null  $factory  The factory.
  86       *
  87       * @see     \Joomla\CMS\MVC\Model\BaseDatabaseModel
  88       * @since   3.2
  89       */
  90      public function __construct($config = array(), MVCFactoryInterface $factory = null)
  91      {
  92          $extension = Factory::getApplication()->input->get('extension', 'com_content');
  93          $this->typeAlias = $extension . '.category';
  94  
  95          // Add a new batch command
  96          $this->batch_commands['flip_ordering'] = 'batchFlipordering';
  97  
  98          parent::__construct($config, $factory);
  99      }
 100  
 101      /**
 102       * Method to test whether a record can be deleted.
 103       *
 104       * @param   object  $record  A record object.
 105       *
 106       * @return  boolean  True if allowed to delete the record. Defaults to the permission set in the component.
 107       *
 108       * @since   1.6
 109       */
 110      protected function canDelete($record)
 111      {
 112          if (empty($record->id) || $record->published != -2) {
 113              return false;
 114          }
 115  
 116          return Factory::getUser()->authorise('core.delete', $record->extension . '.category.' . (int) $record->id);
 117      }
 118  
 119      /**
 120       * Method to test whether a record can have its state changed.
 121       *
 122       * @param   object  $record  A record object.
 123       *
 124       * @return  boolean  True if allowed to change the state of the record. Defaults to the permission set in the component.
 125       *
 126       * @since   1.6
 127       */
 128      protected function canEditState($record)
 129      {
 130          $user = Factory::getUser();
 131  
 132          // Check for existing category.
 133          if (!empty($record->id)) {
 134              return $user->authorise('core.edit.state', $record->extension . '.category.' . (int) $record->id);
 135          }
 136  
 137          // New category, so check against the parent.
 138          if (!empty($record->parent_id)) {
 139              return $user->authorise('core.edit.state', $record->extension . '.category.' . (int) $record->parent_id);
 140          }
 141  
 142          // Default to component settings if neither category nor parent known.
 143          return $user->authorise('core.edit.state', $record->extension);
 144      }
 145  
 146      /**
 147       * Method to get a table object, load it if necessary.
 148       *
 149       * @param   string  $type    The table name. Optional.
 150       * @param   string  $prefix  The class prefix. Optional.
 151       * @param   array   $config  Configuration array for model. Optional.
 152       *
 153       * @return  \Joomla\CMS\Table\Table  A Table object
 154       *
 155       * @since   1.6
 156       */
 157      public function getTable($type = 'Category', $prefix = 'Administrator', $config = array())
 158      {
 159          return parent::getTable($type, $prefix, $config);
 160      }
 161  
 162      /**
 163       * Auto-populate the model state.
 164       *
 165       * Note. Calling getState in this method will result in recursion.
 166       *
 167       * @return  void
 168       *
 169       * @since   1.6
 170       */
 171      protected function populateState()
 172      {
 173          $app = Factory::getApplication();
 174  
 175          $parentId = $app->input->getInt('parent_id');
 176          $this->setState('category.parent_id', $parentId);
 177  
 178          // Load the User state.
 179          $pk = $app->input->getInt('id');
 180          $this->setState($this->getName() . '.id', $pk);
 181  
 182          $extension = $app->input->get('extension', 'com_content');
 183          $this->setState('category.extension', $extension);
 184          $parts = explode('.', $extension);
 185  
 186          // Extract the component name
 187          $this->setState('category.component', $parts[0]);
 188  
 189          // Extract the optional section name
 190          $this->setState('category.section', (\count($parts) > 1) ? $parts[1] : null);
 191  
 192          // Load the parameters.
 193          $params = ComponentHelper::getParams('com_categories');
 194          $this->setState('params', $params);
 195      }
 196  
 197      /**
 198       * Method to get a category.
 199       *
 200       * @param   integer  $pk  An optional id of the object to get, otherwise the id from the model state is used.
 201       *
 202       * @return  mixed    Category data object on success, false on failure.
 203       *
 204       * @since   1.6
 205       */
 206      public function getItem($pk = null)
 207      {
 208          if ($result = parent::getItem($pk)) {
 209              // Prime required properties.
 210              if (empty($result->id)) {
 211                  $result->parent_id = $this->getState('category.parent_id');
 212                  $result->extension = $this->getState('category.extension');
 213              }
 214  
 215              // Convert the metadata field to an array.
 216              $registry = new Registry($result->metadata);
 217              $result->metadata = $registry->toArray();
 218  
 219              if (!empty($result->id)) {
 220                  $result->tags = new TagsHelper();
 221                  $result->tags->getTagIds($result->id, $result->extension . '.category');
 222              }
 223          }
 224  
 225          $assoc = $this->getAssoc();
 226  
 227          if ($assoc) {
 228              if ($result->id != null) {
 229                  $result->associations = ArrayHelper::toInteger(CategoriesHelper::getAssociations($result->id, $result->extension));
 230              } else {
 231                  $result->associations = array();
 232              }
 233          }
 234  
 235          return $result;
 236      }
 237  
 238      /**
 239       * Method to get the row form.
 240       *
 241       * @param   array    $data      Data for the form.
 242       * @param   boolean  $loadData  True if the form is to load its own data (default case), false if not.
 243       *
 244       * @return  Form|boolean  A JForm object on success, false on failure
 245       *
 246       * @since   1.6
 247       */
 248      public function getForm($data = array(), $loadData = true)
 249      {
 250          $extension = $this->getState('category.extension');
 251          $jinput = Factory::getApplication()->input;
 252  
 253          // A workaround to get the extension into the model for save requests.
 254          if (empty($extension) && isset($data['extension'])) {
 255              $extension = $data['extension'];
 256              $parts = explode('.', $extension);
 257  
 258              $this->setState('category.extension', $extension);
 259              $this->setState('category.component', $parts[0]);
 260              $this->setState('category.section', @$parts[1]);
 261          }
 262  
 263          // Get the form.
 264          $form = $this->loadForm('com_categories.category' . $extension, 'category', array('control' => 'jform', 'load_data' => $loadData));
 265  
 266          if (empty($form)) {
 267              return false;
 268          }
 269  
 270          // Modify the form based on Edit State access controls.
 271          if (empty($data['extension'])) {
 272              $data['extension'] = $extension;
 273          }
 274  
 275          $categoryId = $jinput->get('id');
 276          $parts      = explode('.', $extension);
 277          $assetKey   = $categoryId ? $extension . '.category.' . $categoryId : $parts[0];
 278  
 279          if (!Factory::getUser()->authorise('core.edit.state', $assetKey)) {
 280              // Disable fields for display.
 281              $form->setFieldAttribute('ordering', 'disabled', 'true');
 282              $form->setFieldAttribute('published', 'disabled', 'true');
 283  
 284              // Disable fields while saving.
 285              // The controller has already verified this is a record you can edit.
 286              $form->setFieldAttribute('ordering', 'filter', 'unset');
 287              $form->setFieldAttribute('published', 'filter', 'unset');
 288          }
 289  
 290          // Don't allow to change the created_user_id user if not allowed to access com_users.
 291          if (!Factory::getUser()->authorise('core.manage', 'com_users')) {
 292              $form->setFieldAttribute('created_user_id', 'filter', 'unset');
 293          }
 294  
 295          return $form;
 296      }
 297  
 298      /**
 299       * A protected method to get the where clause for the reorder
 300       * This ensures that the row will be moved relative to a row with the same extension
 301       *
 302       * @param   Category  $table  Current table instance
 303       *
 304       * @return  array  An array of conditions to add to ordering queries.
 305       *
 306       * @since   1.6
 307       */
 308      protected function getReorderConditions($table)
 309      {
 310          $db = $this->getDatabase();
 311  
 312          return [
 313              $db->quoteName('extension') . ' = ' . $db->quote($table->extension),
 314          ];
 315      }
 316  
 317      /**
 318       * Method to get the data that should be injected in the form.
 319       *
 320       * @return  mixed  The data for the form.
 321       *
 322       * @since   1.6
 323       */
 324      protected function loadFormData()
 325      {
 326          // Check the session for previously entered form data.
 327          $app = Factory::getApplication();
 328          $data = $app->getUserState('com_categories.edit.' . $this->getName() . '.data', array());
 329  
 330          if (empty($data)) {
 331              $data = $this->getItem();
 332  
 333              // Pre-select some filters (Status, Language, Access) in edit form if those have been selected in Category Manager
 334              if (!$data->id) {
 335                  // Check for which extension the Category Manager is used and get selected fields
 336                  $extension = substr($app->getUserState('com_categories.categories.filter.extension', ''), 4);
 337                  $filters = (array) $app->getUserState('com_categories.categories.' . $extension . '.filter');
 338  
 339                  $data->set(
 340                      'published',
 341                      $app->input->getInt(
 342                          'published',
 343                          ((isset($filters['published']) && $filters['published'] !== '') ? $filters['published'] : null)
 344                      )
 345                  );
 346                  $data->set('language', $app->input->getString('language', (!empty($filters['language']) ? $filters['language'] : null)));
 347                  $data->set(
 348                      'access',
 349                      $app->input->getInt('access', (!empty($filters['access']) ? $filters['access'] : $app->get('access')))
 350                  );
 351              }
 352          }
 353  
 354          $this->preprocessData('com_categories.category', $data);
 355  
 356          return $data;
 357      }
 358  
 359      /**
 360       * Method to validate the form data.
 361       *
 362       * @param   Form    $form   The form to validate against.
 363       * @param   array   $data   The data to validate.
 364       * @param   string  $group  The name of the field group to validate.
 365       *
 366       * @return  array|boolean  Array of filtered data if valid, false otherwise.
 367       *
 368       * @see     JFormRule
 369       * @see     JFilterInput
 370       * @since   3.9.23
 371       */
 372      public function validate($form, $data, $group = null)
 373      {
 374          if (!Factory::getUser()->authorise('core.admin', $data['extension'])) {
 375              if (isset($data['rules'])) {
 376                  unset($data['rules']);
 377              }
 378          }
 379  
 380          return parent::validate($form, $data, $group);
 381      }
 382  
 383      /**
 384       * Method to preprocess the form.
 385       *
 386       * @param   Form    $form  A Form object.
 387       * @param   mixed   $data  The data expected for the form.
 388       * @param   string  $group The name of the plugin group to import.
 389       *
 390       * @return  mixed
 391       *
 392       * @since   1.6
 393       *
 394       * @throws  \Exception if there is an error in the form event.
 395       *
 396       * @see     \Joomla\CMS\Form\FormField
 397       */
 398      protected function preprocessForm(Form $form, $data, $group = 'content')
 399      {
 400          $lang = Factory::getLanguage();
 401          $component = $this->getState('category.component');
 402          $section = $this->getState('category.section');
 403          $extension = Factory::getApplication()->input->get('extension', null);
 404  
 405          // Get the component form if it exists
 406          $name = 'category' . ($section ? ('.' . $section) : '');
 407  
 408          // Looking first in the component forms folder
 409          $path = Path::clean(JPATH_ADMINISTRATOR . "/components/$component/forms/$name.xml");
 410  
 411          // Looking in the component models/forms folder (J! 3)
 412          if (!file_exists($path)) {
 413              $path = Path::clean(JPATH_ADMINISTRATOR . "/components/$component/models/forms/$name.xml");
 414          }
 415  
 416          // Old way: looking in the component folder
 417          if (!file_exists($path)) {
 418              $path = Path::clean(JPATH_ADMINISTRATOR . "/components/$component/$name.xml");
 419          }
 420  
 421          if (file_exists($path)) {
 422              $lang->load($component, JPATH_BASE);
 423              $lang->load($component, JPATH_BASE . '/components/' . $component);
 424  
 425              if (!$form->loadFile($path, false)) {
 426                  throw new \Exception(Text::_('JERROR_LOADFILE_FAILED'));
 427              }
 428          }
 429  
 430          $componentInterface = Factory::getApplication()->bootComponent($component);
 431  
 432          if ($componentInterface instanceof CategoryServiceInterface) {
 433              $componentInterface->prepareForm($form, $data);
 434          } else {
 435              // Try to find the component helper.
 436              $eName = str_replace('com_', '', $component);
 437              $path = Path::clean(JPATH_ADMINISTRATOR . "/components/$component/helpers/category.php");
 438  
 439              if (file_exists($path)) {
 440                  $cName = ucfirst($eName) . ucfirst($section) . 'HelperCategory';
 441  
 442                  \JLoader::register($cName, $path);
 443  
 444                  if (class_exists($cName) && \is_callable(array($cName, 'onPrepareForm'))) {
 445                      $lang->load($component, JPATH_BASE, null, false, false)
 446                          || $lang->load($component, JPATH_BASE . '/components/' . $component, null, false, false)
 447                          || $lang->load($component, JPATH_BASE, $lang->getDefault(), false, false)
 448                          || $lang->load($component, JPATH_BASE . '/components/' . $component, $lang->getDefault(), false, false);
 449                      \call_user_func_array(array($cName, 'onPrepareForm'), array(&$form));
 450  
 451                      // Check for an error.
 452                      if ($form instanceof \Exception) {
 453                          $this->setError($form->getMessage());
 454  
 455                          return false;
 456                      }
 457                  }
 458              }
 459          }
 460  
 461          // Set the access control rules field component value.
 462          $form->setFieldAttribute('rules', 'component', $component);
 463          $form->setFieldAttribute('rules', 'section', $name);
 464  
 465          // Association category items
 466          if ($this->getAssoc()) {
 467              $languages = LanguageHelper::getContentLanguages(false, false, null, 'ordering', 'asc');
 468  
 469              if (\count($languages) > 1) {
 470                  $addform = new \SimpleXMLElement('<form />');
 471                  $fields = $addform->addChild('fields');
 472                  $fields->addAttribute('name', 'associations');
 473                  $fieldset = $fields->addChild('fieldset');
 474                  $fieldset->addAttribute('name', 'item_associations');
 475  
 476                  foreach ($languages as $language) {
 477                      $field = $fieldset->addChild('field');
 478                      $field->addAttribute('name', $language->lang_code);
 479                      $field->addAttribute('type', 'modal_category');
 480                      $field->addAttribute('language', $language->lang_code);
 481                      $field->addAttribute('label', $language->title);
 482                      $field->addAttribute('translate_label', 'false');
 483                      $field->addAttribute('extension', $extension);
 484                      $field->addAttribute('select', 'true');
 485                      $field->addAttribute('new', 'true');
 486                      $field->addAttribute('edit', 'true');
 487                      $field->addAttribute('clear', 'true');
 488                      $field->addAttribute('propagate', 'true');
 489                  }
 490  
 491                  $form->load($addform, false);
 492              }
 493          }
 494  
 495          // Trigger the default form events.
 496          parent::preprocessForm($form, $data, $group);
 497      }
 498  
 499      /**
 500       * Method to save the form data.
 501       *
 502       * @param   array  $data  The form data.
 503       *
 504       * @return  boolean  True on success.
 505       *
 506       * @since   1.6
 507       */
 508      public function save($data)
 509      {
 510          $table      = $this->getTable();
 511          $input      = Factory::getApplication()->input;
 512          $pk         = (!empty($data['id'])) ? $data['id'] : (int) $this->getState($this->getName() . '.id');
 513          $isNew      = true;
 514          $context    = $this->option . '.' . $this->name;
 515  
 516          if (!empty($data['tags']) && $data['tags'][0] != '') {
 517              $table->newTags = $data['tags'];
 518          }
 519  
 520          // Include the plugins for the save events.
 521          PluginHelper::importPlugin($this->events_map['save']);
 522  
 523          // Load the row if saving an existing category.
 524          if ($pk > 0) {
 525              $table->load($pk);
 526              $isNew = false;
 527          }
 528  
 529          // Set the new parent id if parent id not matched OR while New/Save as Copy .
 530          if ($table->parent_id != $data['parent_id'] || $data['id'] == 0) {
 531              $table->setLocation($data['parent_id'], 'last-child');
 532          }
 533  
 534          // Alter the title for save as copy
 535          if ($input->get('task') == 'save2copy') {
 536              $origTable = clone $this->getTable();
 537              $origTable->load($input->getInt('id'));
 538  
 539              if ($data['title'] == $origTable->title) {
 540                  [$title, $alias] = $this->generateNewTitle($data['parent_id'], $data['alias'], $data['title']);
 541                  $data['title'] = $title;
 542                  $data['alias'] = $alias;
 543              } else {
 544                  if ($data['alias'] == $origTable->alias) {
 545                      $data['alias'] = '';
 546                  }
 547              }
 548  
 549              $data['published'] = 0;
 550          }
 551  
 552          // Bind the data.
 553          if (!$table->bind($data)) {
 554              $this->setError($table->getError());
 555  
 556              return false;
 557          }
 558  
 559          // Bind the rules.
 560          if (isset($data['rules'])) {
 561              $rules = new Rules($data['rules']);
 562              $table->setRules($rules);
 563          }
 564  
 565          // Check the data.
 566          if (!$table->check()) {
 567              $this->setError($table->getError());
 568  
 569              return false;
 570          }
 571  
 572          // Trigger the before save event.
 573          $result = Factory::getApplication()->triggerEvent($this->event_before_save, array($context, &$table, $isNew, $data));
 574  
 575          if (\in_array(false, $result, true)) {
 576              $this->setError($table->getError());
 577  
 578              return false;
 579          }
 580  
 581          // Store the data.
 582          if (!$table->store()) {
 583              $this->setError($table->getError());
 584  
 585              return false;
 586          }
 587  
 588          $assoc = $this->getAssoc();
 589  
 590          if ($assoc) {
 591              // Adding self to the association
 592              $associations = $data['associations'] ?? array();
 593  
 594              // Unset any invalid associations
 595              $associations = ArrayHelper::toInteger($associations);
 596  
 597              foreach ($associations as $tag => $id) {
 598                  if (!$id) {
 599                      unset($associations[$tag]);
 600                  }
 601              }
 602  
 603              // Detecting all item menus
 604              $allLanguage = $table->language == '*';
 605  
 606              if ($allLanguage && !empty($associations)) {
 607                  Factory::getApplication()->enqueueMessage(Text::_('COM_CATEGORIES_ERROR_ALL_LANGUAGE_ASSOCIATED'), 'notice');
 608              }
 609  
 610              // Get associationskey for edited item
 611              $db    = $this->getDatabase();
 612              $id    = (int) $table->id;
 613              $query = $db->getQuery(true)
 614                  ->select($db->quoteName('key'))
 615                  ->from($db->quoteName('#__associations'))
 616                  ->where($db->quoteName('context') . ' = :associationscontext')
 617                  ->where($db->quoteName('id') . ' = :id')
 618                  ->bind(':associationscontext', $this->associationsContext)
 619                  ->bind(':id', $id, ParameterType::INTEGER);
 620              $db->setQuery($query);
 621              $oldKey = $db->loadResult();
 622  
 623              if ($associations || $oldKey !== null) {
 624                  $where = [];
 625  
 626                  // Deleting old associations for the associated items
 627                  $query = $db->getQuery(true)
 628                      ->delete($db->quoteName('#__associations'))
 629                      ->where($db->quoteName('context') . ' = :associationscontext')
 630                      ->bind(':associationscontext', $this->associationsContext);
 631  
 632                  if ($associations) {
 633                      $where[] = $db->quoteName('id') . ' IN (' . implode(',', $query->bindArray(array_values($associations))) . ')';
 634                  }
 635  
 636                  if ($oldKey !== null) {
 637                      $where[] = $db->quoteName('key') . ' = :oldKey';
 638                      $query->bind(':oldKey', $oldKey);
 639                  }
 640  
 641                  $query->extendWhere('AND', $where, 'OR');
 642              }
 643  
 644              $db->setQuery($query);
 645  
 646              try {
 647                  $db->execute();
 648              } catch (\RuntimeException $e) {
 649                  $this->setError($e->getMessage());
 650  
 651                  return false;
 652              }
 653  
 654              // Adding self to the association
 655              if (!$allLanguage) {
 656                  $associations[$table->language] = (int) $table->id;
 657              }
 658  
 659              if (\count($associations) > 1) {
 660                  // Adding new association for these items
 661                  $key = md5(json_encode($associations));
 662                  $query->clear()
 663                      ->insert($db->quoteName('#__associations'))
 664                      ->columns(
 665                          [
 666                              $db->quoteName('id'),
 667                              $db->quoteName('context'),
 668                              $db->quoteName('key'),
 669                          ]
 670                      );
 671  
 672                  foreach ($associations as $id) {
 673                      $id = (int) $id;
 674  
 675                      $query->values(
 676                          implode(
 677                              ',',
 678                              $query->bindArray(
 679                                  [$id, $this->associationsContext, $key],
 680                                  [ParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING]
 681                              )
 682                          )
 683                      );
 684                  }
 685  
 686                  $db->setQuery($query);
 687  
 688                  try {
 689                      $db->execute();
 690                  } catch (\RuntimeException $e) {
 691                      $this->setError($e->getMessage());
 692  
 693                      return false;
 694                  }
 695              }
 696          }
 697  
 698          // Trigger the after save event.
 699          Factory::getApplication()->triggerEvent($this->event_after_save, array($context, &$table, $isNew, $data));
 700  
 701          // Rebuild the path for the category:
 702          if (!$table->rebuildPath($table->id)) {
 703              $this->setError($table->getError());
 704  
 705              return false;
 706          }
 707  
 708          // Rebuild the paths of the category's children:
 709          if (!$table->rebuild($table->id, $table->lft, $table->level, $table->path)) {
 710              $this->setError($table->getError());
 711  
 712              return false;
 713          }
 714  
 715          $this->setState($this->getName() . '.id', $table->id);
 716  
 717          if (Factory::getApplication()->input->get('task') == 'editAssociations') {
 718              return $this->redirectToAssociations($data);
 719          }
 720  
 721          // Clear the cache
 722          $this->cleanCache();
 723  
 724          return true;
 725      }
 726  
 727      /**
 728       * Method to change the published state of one or more records.
 729       *
 730       * @param   array    $pks    A list of the primary keys to change.
 731       * @param   integer  $value  The value of the published state.
 732       *
 733       * @return  boolean  True on success.
 734       *
 735       * @since   2.5
 736       */
 737      public function publish(&$pks, $value = 1)
 738      {
 739          if (parent::publish($pks, $value)) {
 740              $extension = Factory::getApplication()->input->get('extension');
 741  
 742              // Include the content plugins for the change of category state event.
 743              PluginHelper::importPlugin('content');
 744  
 745              // Trigger the onCategoryChangeState event.
 746              Factory::getApplication()->triggerEvent('onCategoryChangeState', array($extension, $pks, $value));
 747  
 748              return true;
 749          }
 750      }
 751  
 752      /**
 753       * Method rebuild the entire nested set tree.
 754       *
 755       * @return  boolean  False on failure or error, true otherwise.
 756       *
 757       * @since   1.6
 758       */
 759      public function rebuild()
 760      {
 761          // Get an instance of the table object.
 762          $table = $this->getTable();
 763  
 764          if (!$table->rebuild()) {
 765              $this->setError($table->getError());
 766  
 767              return false;
 768          }
 769  
 770          // Clear the cache
 771          $this->cleanCache();
 772  
 773          return true;
 774      }
 775  
 776      /**
 777       * Method to save the reordered nested set tree.
 778       * First we save the new order values in the lft values of the changed ids.
 779       * Then we invoke the table rebuild to implement the new ordering.
 780       *
 781       * @param   array    $idArray   An array of primary key ids.
 782       * @param   integer  $lftArray  The lft value
 783       *
 784       * @return  boolean  False on failure or error, True otherwise
 785       *
 786       * @since   1.6
 787       */
 788      public function saveorder($idArray = null, $lftArray = null)
 789      {
 790          // Get an instance of the table object.
 791          $table = $this->getTable();
 792  
 793          if (!$table->saveorder($idArray, $lftArray)) {
 794              $this->setError($table->getError());
 795  
 796              return false;
 797          }
 798  
 799          // Clear the cache
 800          $this->cleanCache();
 801  
 802          return true;
 803      }
 804  
 805      /**
 806       * Batch flip category ordering.
 807       *
 808       * @param   integer  $value     The new category.
 809       * @param   array    $pks       An array of row IDs.
 810       * @param   array    $contexts  An array of item contexts.
 811       *
 812       * @return  mixed    An array of new IDs on success, boolean false on failure.
 813       *
 814       * @since   3.6.3
 815       */
 816      protected function batchFlipordering($value, $pks, $contexts)
 817      {
 818          $successful = array();
 819  
 820          $db = $this->getDatabase();
 821          $query = $db->getQuery(true);
 822  
 823          /**
 824           * For each category get the max ordering value
 825           * Re-order with max - ordering
 826           */
 827          foreach ($pks as $id) {
 828              $query->select('MAX(' . $db->quoteName('ordering') . ')')
 829                  ->from($db->quoteName('#__content'))
 830                  ->where($db->quoteName('catid') . ' = :catid')
 831                  ->bind(':catid', $id, ParameterType::INTEGER);
 832  
 833              $db->setQuery($query);
 834  
 835              $max = (int) $db->loadResult();
 836              $max++;
 837  
 838              $query->clear();
 839  
 840              $query->update($db->quoteName('#__content'))
 841                  ->set($db->quoteName('ordering') . ' = :max - ' . $db->quoteName('ordering'))
 842                  ->where($db->quoteName('catid') . ' = :catid')
 843                  ->bind(':max', $max, ParameterType::INTEGER)
 844                  ->bind(':catid', $id, ParameterType::INTEGER);
 845  
 846              $db->setQuery($query);
 847  
 848              if ($db->execute()) {
 849                  $successful[] = $id;
 850              }
 851          }
 852  
 853          return empty($successful) ? false : $successful;
 854      }
 855  
 856      /**
 857       * Batch copy categories to a new category.
 858       *
 859       * @param   integer  $value     The new category.
 860       * @param   array    $pks       An array of row IDs.
 861       * @param   array    $contexts  An array of item contexts.
 862       *
 863       * @return  mixed    An array of new IDs on success, boolean false on failure.
 864       *
 865       * @since   1.6
 866       */
 867      protected function batchCopy($value, $pks, $contexts)
 868      {
 869          $type = new UCMType();
 870          $this->type = $type->getTypeByAlias($this->typeAlias);
 871  
 872          // $value comes as {parent_id}.{extension}
 873          $parts = explode('.', $value);
 874          $parentId = (int) ArrayHelper::getValue($parts, 0, 1);
 875  
 876          $db = $this->getDatabase();
 877          $extension = Factory::getApplication()->input->get('extension', '', 'word');
 878          $newIds = array();
 879  
 880          // Check that the parent exists
 881          if ($parentId) {
 882              if (!$this->table->load($parentId)) {
 883                  if ($error = $this->table->getError()) {
 884                      // Fatal error
 885                      $this->setError($error);
 886  
 887                      return false;
 888                  } else {
 889                      // Non-fatal error
 890                      $this->setError(Text::_('JGLOBAL_BATCH_MOVE_PARENT_NOT_FOUND'));
 891                      $parentId = 0;
 892                  }
 893              }
 894  
 895              // Check that user has create permission for parent category
 896              if ($parentId == $this->table->getRootId()) {
 897                  $canCreate = $this->user->authorise('core.create', $extension);
 898              } else {
 899                  $canCreate = $this->user->authorise('core.create', $extension . '.category.' . $parentId);
 900              }
 901  
 902              if (!$canCreate) {
 903                  // Error since user cannot create in parent category
 904                  $this->setError(Text::_('COM_CATEGORIES_BATCH_CANNOT_CREATE'));
 905  
 906                  return false;
 907              }
 908          }
 909  
 910          // If the parent is 0, set it to the ID of the root item in the tree
 911          if (empty($parentId)) {
 912              if (!$parentId = $this->table->getRootId()) {
 913                  $this->setError($this->table->getError());
 914  
 915                  return false;
 916              } elseif (!$this->user->authorise('core.create', $extension)) {
 917                  // Make sure we can create in root
 918                  $this->setError(Text::_('COM_CATEGORIES_BATCH_CANNOT_CREATE'));
 919  
 920                  return false;
 921              }
 922          }
 923  
 924          // We need to log the parent ID
 925          $parents = array();
 926  
 927          // Calculate the emergency stop count as a precaution against a runaway loop bug
 928          $query = $db->getQuery(true)
 929              ->select('COUNT(' . $db->quoteName('id') . ')')
 930              ->from($db->quoteName('#__categories'));
 931          $db->setQuery($query);
 932  
 933          try {
 934              $count = $db->loadResult();
 935          } catch (\RuntimeException $e) {
 936              $this->setError($e->getMessage());
 937  
 938              return false;
 939          }
 940  
 941          // Parent exists so let's proceed
 942          while (!empty($pks) && $count > 0) {
 943              // Pop the first id off the stack
 944              $pk = array_shift($pks);
 945  
 946              $this->table->reset();
 947  
 948              // Check that the row actually exists
 949              if (!$this->table->load($pk)) {
 950                  if ($error = $this->table->getError()) {
 951                      // Fatal error
 952                      $this->setError($error);
 953  
 954                      return false;
 955                  } else {
 956                      // Not fatal error
 957                      $this->setError(Text::sprintf('JGLOBAL_BATCH_MOVE_ROW_NOT_FOUND', $pk));
 958                      continue;
 959                  }
 960              }
 961  
 962              // Copy is a bit tricky, because we also need to copy the children
 963              $lft = (int) $this->table->lft;
 964              $rgt = (int) $this->table->rgt;
 965              $query->clear()
 966                  ->select($db->quoteName('id'))
 967                  ->from($db->quoteName('#__categories'))
 968                  ->where($db->quoteName('lft') . ' > :lft')
 969                  ->where($db->quoteName('rgt') . ' < :rgt')
 970                  ->bind(':lft', $lft, ParameterType::INTEGER)
 971                  ->bind(':rgt', $rgt, ParameterType::INTEGER);
 972              $db->setQuery($query);
 973              $childIds = $db->loadColumn();
 974  
 975              // Add child ID's to the array only if they aren't already there.
 976              foreach ($childIds as $childId) {
 977                  if (!\in_array($childId, $pks)) {
 978                      $pks[] = $childId;
 979                  }
 980              }
 981  
 982              // Make a copy of the old ID, Parent ID and Asset ID
 983              $oldId       = $this->table->id;
 984              $oldParentId = $this->table->parent_id;
 985              $oldAssetId  = $this->table->asset_id;
 986  
 987              // Reset the id because we are making a copy.
 988              $this->table->id = 0;
 989  
 990              // If we a copying children, the Old ID will turn up in the parents list
 991              // otherwise it's a new top level item
 992              $this->table->parent_id = $parents[$oldParentId] ?? $parentId;
 993  
 994              // Set the new location in the tree for the node.
 995              $this->table->setLocation($this->table->parent_id, 'last-child');
 996  
 997              // @TODO: Deal with ordering?
 998              // $this->table->ordering = 1;
 999              $this->table->level = null;
1000              $this->table->asset_id = null;
1001              $this->table->lft = null;
1002              $this->table->rgt = null;
1003  
1004              // Alter the title & alias
1005              [$title, $alias] = $this->generateNewTitle($this->table->parent_id, $this->table->alias, $this->table->title);
1006              $this->table->title  = $title;
1007              $this->table->alias  = $alias;
1008  
1009              // Unpublish because we are making a copy
1010              $this->table->published = 0;
1011  
1012              // Store the row.
1013              if (!$this->table->store()) {
1014                  $this->setError($this->table->getError());
1015  
1016                  return false;
1017              }
1018  
1019              // Get the new item ID
1020              $newId = $this->table->get('id');
1021  
1022              // Add the new ID to the array
1023              $newIds[$pk] = $newId;
1024  
1025              // Copy rules
1026              $query->clear()
1027                  ->update($db->quoteName('#__assets', 't'))
1028                  ->join(
1029                      'INNER',
1030                      $db->quoteName('#__assets', 's'),
1031                      $db->quoteName('s.id') . ' = :oldid'
1032                  )
1033                  ->bind(':oldid', $oldAssetId, ParameterType::INTEGER)
1034                  ->set($db->quoteName('t.rules') . ' = ' . $db->quoteName('s.rules'))
1035                  ->where($db->quoteName('t.id') . ' = :assetid')
1036                  ->bind(':assetid', $this->table->asset_id, ParameterType::INTEGER);
1037              $db->setQuery($query)->execute();
1038  
1039              // Now we log the old 'parent' to the new 'parent'
1040              $parents[$oldId] = $this->table->id;
1041              $count--;
1042          }
1043  
1044          // Rebuild the hierarchy.
1045          if (!$this->table->rebuild()) {
1046              $this->setError($this->table->getError());
1047  
1048              return false;
1049          }
1050  
1051          // Rebuild the tree path.
1052          if (!$this->table->rebuildPath($this->table->id)) {
1053              $this->setError($this->table->getError());
1054  
1055              return false;
1056          }
1057  
1058          return $newIds;
1059      }
1060  
1061      /**
1062       * Batch move categories to a new category.
1063       *
1064       * @param   integer  $value     The new category ID.
1065       * @param   array    $pks       An array of row IDs.
1066       * @param   array    $contexts  An array of item contexts.
1067       *
1068       * @return  boolean  True on success.
1069       *
1070       * @since   1.6
1071       */
1072      protected function batchMove($value, $pks, $contexts)
1073      {
1074          $parentId = (int) $value;
1075          $type = new UCMType();
1076          $this->type = $type->getTypeByAlias($this->typeAlias);
1077  
1078          $db = $this->getDatabase();
1079          $query = $db->getQuery(true);
1080          $extension = Factory::getApplication()->input->get('extension', '', 'word');
1081  
1082          // Check that the parent exists.
1083          if ($parentId) {
1084              if (!$this->table->load($parentId)) {
1085                  if ($error = $this->table->getError()) {
1086                      // Fatal error.
1087                      $this->setError($error);
1088  
1089                      return false;
1090                  } else {
1091                      // Non-fatal error.
1092                      $this->setError(Text::_('JGLOBAL_BATCH_MOVE_PARENT_NOT_FOUND'));
1093                      $parentId = 0;
1094                  }
1095              }
1096  
1097              // Check that user has create permission for parent category.
1098              if ($parentId == $this->table->getRootId()) {
1099                  $canCreate = $this->user->authorise('core.create', $extension);
1100              } else {
1101                  $canCreate = $this->user->authorise('core.create', $extension . '.category.' . $parentId);
1102              }
1103  
1104              if (!$canCreate) {
1105                  // Error since user cannot create in parent category
1106                  $this->setError(Text::_('COM_CATEGORIES_BATCH_CANNOT_CREATE'));
1107  
1108                  return false;
1109              }
1110  
1111              // Check that user has edit permission for every category being moved
1112              // Note that the entire batch operation fails if any category lacks edit permission
1113              foreach ($pks as $pk) {
1114                  if (!$this->user->authorise('core.edit', $extension . '.category.' . $pk)) {
1115                      // Error since user cannot edit this category
1116                      $this->setError(Text::_('COM_CATEGORIES_BATCH_CANNOT_EDIT'));
1117  
1118                      return false;
1119                  }
1120              }
1121          }
1122  
1123          // We are going to store all the children and just move the category
1124          $children = array();
1125  
1126          // Parent exists so let's proceed
1127          foreach ($pks as $pk) {
1128              // Check that the row actually exists
1129              if (!$this->table->load($pk)) {
1130                  if ($error = $this->table->getError()) {
1131                      // Fatal error
1132                      $this->setError($error);
1133  
1134                      return false;
1135                  } else {
1136                      // Not fatal error
1137                      $this->setError(Text::sprintf('JGLOBAL_BATCH_MOVE_ROW_NOT_FOUND', $pk));
1138                      continue;
1139                  }
1140              }
1141  
1142              // Set the new location in the tree for the node.
1143              $this->table->setLocation($parentId, 'last-child');
1144  
1145              // Check if we are moving to a different parent
1146              if ($parentId != $this->table->parent_id) {
1147                  $lft = (int) $this->table->lft;
1148                  $rgt = (int) $this->table->rgt;
1149  
1150                  // Add the child node ids to the children array.
1151                  $query->clear()
1152                      ->select($db->quoteName('id'))
1153                      ->from($db->quoteName('#__categories'))
1154                      ->where($db->quoteName('lft') . ' BETWEEN :lft AND :rgt')
1155                      ->bind(':lft', $lft, ParameterType::INTEGER)
1156                      ->bind(':rgt', $rgt, ParameterType::INTEGER);
1157                  $db->setQuery($query);
1158  
1159                  try {
1160                      $children = array_merge($children, (array) $db->loadColumn());
1161                  } catch (\RuntimeException $e) {
1162                      $this->setError($e->getMessage());
1163  
1164                      return false;
1165                  }
1166              }
1167  
1168              // Store the row.
1169              if (!$this->table->store()) {
1170                  $this->setError($this->table->getError());
1171  
1172                  return false;
1173              }
1174  
1175              // Rebuild the tree path.
1176              if (!$this->table->rebuildPath()) {
1177                  $this->setError($this->table->getError());
1178  
1179                  return false;
1180              }
1181          }
1182  
1183          // Process the child rows
1184          if (!empty($children)) {
1185              // Remove any duplicates and sanitize ids.
1186              $children = array_unique($children);
1187              $children = ArrayHelper::toInteger($children);
1188          }
1189  
1190          return true;
1191      }
1192  
1193      /**
1194       * Custom clean the cache of com_content and content modules
1195       *
1196       * @param   string   $group     Cache group name.
1197       * @param   integer  $clientId  @deprecated   5.0   No longer used.
1198       *
1199       * @return  void
1200       *
1201       * @since   1.6
1202       */
1203      protected function cleanCache($group = null, $clientId = 0)
1204      {
1205          $extension = Factory::getApplication()->input->get('extension');
1206  
1207          switch ($extension) {
1208              case 'com_content':
1209                  parent::cleanCache('com_content');
1210                  parent::cleanCache('mod_articles_archive');
1211                  parent::cleanCache('mod_articles_categories');
1212                  parent::cleanCache('mod_articles_category');
1213                  parent::cleanCache('mod_articles_latest');
1214                  parent::cleanCache('mod_articles_news');
1215                  parent::cleanCache('mod_articles_popular');
1216                  break;
1217              default:
1218                  parent::cleanCache($extension);
1219                  break;
1220          }
1221      }
1222  
1223      /**
1224       * Method to change the title & alias.
1225       *
1226       * @param   integer  $parentId  The id of the parent.
1227       * @param   string   $alias     The alias.
1228       * @param   string   $title     The title.
1229       *
1230       * @return  array    Contains the modified title and alias.
1231       *
1232       * @since   1.7
1233       */
1234      protected function generateNewTitle($parentId, $alias, $title)
1235      {
1236          // Alter the title & alias
1237          $table = $this->getTable();
1238  
1239          while ($table->load(array('alias' => $alias, 'parent_id' => $parentId))) {
1240              $title = StringHelper::increment($title);
1241              $alias = StringHelper::increment($alias, 'dash');
1242          }
1243  
1244          return array($title, $alias);
1245      }
1246  
1247      /**
1248       * Method to determine if a category association is available.
1249       *
1250       * @return  boolean True if a category association is available; false otherwise.
1251       */
1252      public function getAssoc()
1253      {
1254          if (!\is_null($this->hasAssociation)) {
1255              return $this->hasAssociation;
1256          }
1257  
1258          $extension = $this->getState('category.extension', '');
1259  
1260          $this->hasAssociation = Associations::isEnabled();
1261          $extension = explode('.', $extension);
1262          $component = array_shift($extension);
1263          $cname = str_replace('com_', '', $component);
1264  
1265          if (!$this->hasAssociation || !$component || !$cname) {
1266              $this->hasAssociation = false;
1267  
1268              return $this->hasAssociation;
1269          }
1270  
1271          $componentObject = $this->bootComponent($component);
1272  
1273          if ($componentObject instanceof AssociationServiceInterface && $componentObject instanceof CategoryServiceInterface) {
1274              $this->hasAssociation = true;
1275  
1276              return $this->hasAssociation;
1277          }
1278  
1279          $hname = $cname . 'HelperAssociation';
1280          \JLoader::register($hname, JPATH_SITE . '/components/' . $component . '/helpers/association.php');
1281  
1282          $this->hasAssociation = class_exists($hname) && !empty($hname::$category_association);
1283  
1284          return $this->hasAssociation;
1285      }
1286  }


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