[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/MVC/Model/ -> AdminModel.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\MVC\Model;
  11  
  12  use Joomla\CMS\Component\ComponentHelper;
  13  use Joomla\CMS\Event\Model\BeforeBatchEvent;
  14  use Joomla\CMS\Factory;
  15  use Joomla\CMS\Form\FormFactoryInterface;
  16  use Joomla\CMS\Language\Associations;
  17  use Joomla\CMS\Language\LanguageHelper;
  18  use Joomla\CMS\Language\Text;
  19  use Joomla\CMS\Log\Log;
  20  use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
  21  use Joomla\CMS\Object\CMSObject;
  22  use Joomla\CMS\Plugin\PluginHelper;
  23  use Joomla\CMS\Router\Route;
  24  use Joomla\CMS\Table\Table;
  25  use Joomla\CMS\Table\TableInterface;
  26  use Joomla\CMS\Tag\TaggableTableInterface;
  27  use Joomla\CMS\UCM\UCMType;
  28  use Joomla\Database\ParameterType;
  29  use Joomla\Registry\Registry;
  30  use Joomla\String\StringHelper;
  31  use Joomla\Utilities\ArrayHelper;
  32  
  33  // phpcs:disable PSR1.Files.SideEffects
  34  \defined('JPATH_PLATFORM') or die;
  35  // phpcs:enable PSR1.Files.SideEffects
  36  
  37  /**
  38   * Prototype admin model.
  39   *
  40   * @since  1.6
  41   */
  42  abstract class AdminModel extends FormModel
  43  {
  44      /**
  45       * The type alias for this content type (for example, 'com_content.article').
  46       *
  47       * @var    string
  48       * @since  3.8.6
  49       */
  50      public $typeAlias;
  51  
  52      /**
  53       * The prefix to use with controller messages.
  54       *
  55       * @var    string
  56       * @since  1.6
  57       */
  58      protected $text_prefix = null;
  59  
  60      /**
  61       * The event to trigger after deleting the data.
  62       *
  63       * @var    string
  64       * @since  1.6
  65       */
  66      protected $event_after_delete = null;
  67  
  68      /**
  69       * The event to trigger after saving the data.
  70       *
  71       * @var    string
  72       * @since  1.6
  73       */
  74      protected $event_after_save = null;
  75  
  76      /**
  77       * The event to trigger before deleting the data.
  78       *
  79       * @var    string
  80       * @since  1.6
  81       */
  82      protected $event_before_delete = null;
  83  
  84      /**
  85       * The event to trigger before saving the data.
  86       *
  87       * @var    string
  88       * @since  1.6
  89       */
  90      protected $event_before_save = null;
  91  
  92      /**
  93       * The event to trigger before changing the published state of the data.
  94       *
  95       * @var    string
  96       * @since  4.0.0
  97       */
  98      protected $event_before_change_state = null;
  99  
 100      /**
 101       * The event to trigger after changing the published state of the data.
 102       *
 103       * @var    string
 104       * @since  1.6
 105       */
 106      protected $event_change_state = null;
 107  
 108      /**
 109       * The event to trigger before batch.
 110       *
 111       * @var    string
 112       * @since  4.0.0
 113       */
 114      protected $event_before_batch = null;
 115  
 116      /**
 117       * Batch copy/move command. If set to false,
 118       * the batch copy/move command is not supported
 119       *
 120       * @var    string
 121       * @since  3.4
 122       */
 123      protected $batch_copymove = 'category_id';
 124  
 125      /**
 126       * Allowed batch commands
 127       *
 128       * @var    array
 129       * @since  3.4
 130       */
 131      protected $batch_commands = array(
 132          'assetgroup_id' => 'batchAccess',
 133          'language_id' => 'batchLanguage',
 134          'tag' => 'batchTag'
 135      );
 136  
 137      /**
 138       * The context used for the associations table
 139       *
 140       * @var     string
 141       * @since   3.4.4
 142       */
 143      protected $associationsContext = null;
 144  
 145      /**
 146       * A flag to indicate if member variables for batch actions (and saveorder) have been initialized
 147       *
 148       * @var     object
 149       * @since   3.8.2
 150       */
 151      protected $batchSet = null;
 152  
 153      /**
 154       * The user performing the actions (re-usable in batch methods & saveorder(), initialized via initBatch())
 155       *
 156       * @var     object
 157       * @since   3.8.2
 158       */
 159      protected $user = null;
 160  
 161      /**
 162       * A JTable instance (of appropriate type) to manage the DB records (re-usable in batch methods & saveorder(), initialized via initBatch())
 163       *
 164       * @var     Table
 165       * @since   3.8.2
 166       */
 167      protected $table = null;
 168  
 169      /**
 170       * The class name of the JTable instance managing the DB records (re-usable in batch methods & saveorder(), initialized via initBatch())
 171       *
 172       * @var     string
 173       * @since   3.8.2
 174       */
 175      protected $tableClassName = null;
 176  
 177      /**
 178       * UCM Type corresponding to the current model class (re-usable in batch action methods, initialized via initBatch())
 179       *
 180       * @var     object
 181       * @since   3.8.2
 182       */
 183      protected $contentType = null;
 184  
 185      /**
 186       * DB data of UCM Type corresponding to the current model class (re-usable in batch action methods, initialized via initBatch())
 187       *
 188       * @var     object
 189       * @since   3.8.2
 190       */
 191      protected $type = null;
 192  
 193      /**
 194       * Constructor.
 195       *
 196       * @param   array                 $config       An array of configuration options (name, state, dbo, table_path, ignore_request).
 197       * @param   MVCFactoryInterface   $factory      The factory.
 198       * @param   FormFactoryInterface  $formFactory  The form factory.
 199       *
 200       * @since   1.6
 201       * @throws  \Exception
 202       */
 203      public function __construct($config = array(), MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null)
 204      {
 205          parent::__construct($config, $factory, $formFactory);
 206  
 207          if (isset($config['event_after_delete'])) {
 208              $this->event_after_delete = $config['event_after_delete'];
 209          } elseif (empty($this->event_after_delete)) {
 210              $this->event_after_delete = 'onContentAfterDelete';
 211          }
 212  
 213          if (isset($config['event_after_save'])) {
 214              $this->event_after_save = $config['event_after_save'];
 215          } elseif (empty($this->event_after_save)) {
 216              $this->event_after_save = 'onContentAfterSave';
 217          }
 218  
 219          if (isset($config['event_before_delete'])) {
 220              $this->event_before_delete = $config['event_before_delete'];
 221          } elseif (empty($this->event_before_delete)) {
 222              $this->event_before_delete = 'onContentBeforeDelete';
 223          }
 224  
 225          if (isset($config['event_before_save'])) {
 226              $this->event_before_save = $config['event_before_save'];
 227          } elseif (empty($this->event_before_save)) {
 228              $this->event_before_save = 'onContentBeforeSave';
 229          }
 230  
 231          if (isset($config['event_before_change_state'])) {
 232              $this->event_before_change_state = $config['event_before_change_state'];
 233          } elseif (empty($this->event_before_change_state)) {
 234              $this->event_before_change_state = 'onContentBeforeChangeState';
 235          }
 236  
 237          if (isset($config['event_change_state'])) {
 238              $this->event_change_state = $config['event_change_state'];
 239          } elseif (empty($this->event_change_state)) {
 240              $this->event_change_state = 'onContentChangeState';
 241          }
 242  
 243          if (isset($config['event_before_batch'])) {
 244              $this->event_before_batch = $config['event_before_batch'];
 245          } elseif (empty($this->event_before_batch)) {
 246              $this->event_before_batch = 'onBeforeBatch';
 247          }
 248  
 249          $config['events_map'] = $config['events_map'] ?? array();
 250  
 251          $this->events_map = array_merge(
 252              array(
 253                  'delete'       => 'content',
 254                  'save'         => 'content',
 255                  'change_state' => 'content',
 256                  'validate'     => 'content',
 257              ),
 258              $config['events_map']
 259          );
 260  
 261          // Guess the \Text message prefix. Defaults to the option.
 262          if (isset($config['text_prefix'])) {
 263              $this->text_prefix = strtoupper($config['text_prefix']);
 264          } elseif (empty($this->text_prefix)) {
 265              $this->text_prefix = strtoupper($this->option);
 266          }
 267      }
 268  
 269      /**
 270       * Method to perform batch operations on an item or a set of items.
 271       *
 272       * @param   array  $commands  An array of commands to perform.
 273       * @param   array  $pks       An array of item ids.
 274       * @param   array  $contexts  An array of item contexts.
 275       *
 276       * @return  boolean  Returns true on success, false on failure.
 277       *
 278       * @since   1.7
 279       */
 280      public function batch($commands, $pks, $contexts)
 281      {
 282          // Sanitize ids.
 283          $pks = array_unique($pks);
 284          $pks = ArrayHelper::toInteger($pks);
 285  
 286          // Remove any values of zero.
 287          if (array_search(0, $pks, true)) {
 288              unset($pks[array_search(0, $pks, true)]);
 289          }
 290  
 291          if (empty($pks)) {
 292              $this->setError(Text::_('JGLOBAL_NO_ITEM_SELECTED'));
 293  
 294              return false;
 295          }
 296  
 297          $done = false;
 298  
 299          // Initialize re-usable member properties
 300          $this->initBatch();
 301  
 302          if ($this->batch_copymove && !empty($commands[$this->batch_copymove])) {
 303              $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c');
 304  
 305              if ($cmd === 'c') {
 306                  $result = $this->batchCopy($commands[$this->batch_copymove], $pks, $contexts);
 307  
 308                  if (\is_array($result)) {
 309                      foreach ($result as $old => $new) {
 310                          $contexts[$new] = $contexts[$old];
 311                      }
 312  
 313                      $pks = array_values($result);
 314                  } else {
 315                      return false;
 316                  }
 317              } elseif ($cmd === 'm' && !$this->batchMove($commands[$this->batch_copymove], $pks, $contexts)) {
 318                  return false;
 319              }
 320  
 321              $done = true;
 322          }
 323  
 324          foreach ($this->batch_commands as $identifier => $command) {
 325              if (!empty($commands[$identifier])) {
 326                  if (!$this->$command($commands[$identifier], $pks, $contexts)) {
 327                      return false;
 328                  }
 329  
 330                  $done = true;
 331              }
 332          }
 333  
 334          if (!$done) {
 335              $this->setError(Text::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION'));
 336  
 337              return false;
 338          }
 339  
 340          // Clear the cache
 341          $this->cleanCache();
 342  
 343          return true;
 344      }
 345  
 346      /**
 347       * Batch access level changes for a group of rows.
 348       *
 349       * @param   integer  $value     The new value matching an Asset Group ID.
 350       * @param   array    $pks       An array of row IDs.
 351       * @param   array    $contexts  An array of item contexts.
 352       *
 353       * @return  boolean  True if successful, false otherwise and internal error is set.
 354       *
 355       * @since   1.7
 356       */
 357      protected function batchAccess($value, $pks, $contexts)
 358      {
 359          // Initialize re-usable member properties, and re-usable local variables
 360          $this->initBatch();
 361  
 362          foreach ($pks as $pk) {
 363              if ($this->user->authorise('core.edit', $contexts[$pk])) {
 364                  $this->table->reset();
 365                  $this->table->load($pk);
 366                  $this->table->access = (int) $value;
 367  
 368                  $event = new BeforeBatchEvent(
 369                      $this->event_before_batch,
 370                      ['src' => $this->table, 'type' => 'access']
 371                  );
 372                  $this->dispatchEvent($event);
 373  
 374                  // Check the row.
 375                  if (!$this->table->check()) {
 376                      $this->setError($this->table->getError());
 377  
 378                      return false;
 379                  }
 380  
 381                  if (!$this->table->store()) {
 382                      $this->setError($this->table->getError());
 383  
 384                      return false;
 385                  }
 386              } else {
 387                  $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT'));
 388  
 389                  return false;
 390              }
 391          }
 392  
 393          // Clean the cache
 394          $this->cleanCache();
 395  
 396          return true;
 397      }
 398  
 399      /**
 400       * Batch copy items to a new category or current.
 401       *
 402       * @param   integer  $value     The new category.
 403       * @param   array    $pks       An array of row IDs.
 404       * @param   array    $contexts  An array of item contexts.
 405       *
 406       * @return  array|boolean  An array of new IDs on success, boolean false on failure.
 407       *
 408       * @since   1.7
 409       */
 410      protected function batchCopy($value, $pks, $contexts)
 411      {
 412          // Initialize re-usable member properties, and re-usable local variables
 413          $this->initBatch();
 414  
 415          $categoryId = $value;
 416  
 417          if (!$this->checkCategoryId($categoryId)) {
 418              return false;
 419          }
 420  
 421          $newIds = array();
 422          $db     = $this->getDbo();
 423  
 424          // Parent exists so let's proceed
 425          while (!empty($pks)) {
 426              // Pop the first ID off the stack
 427              $pk = array_shift($pks);
 428  
 429              $this->table->reset();
 430  
 431              // Check that the row actually exists
 432              if (!$this->table->load($pk)) {
 433                  if ($error = $this->table->getError()) {
 434                      // Fatal error
 435                      $this->setError($error);
 436  
 437                      return false;
 438                  } else {
 439                      // Not fatal error
 440                      $this->setError(Text::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk));
 441                      continue;
 442                  }
 443              }
 444  
 445              // Check for asset_id
 446              if ($this->table->hasField($this->table->getColumnAlias('asset_id'))) {
 447                  $oldAssetId = $this->table->asset_id;
 448              }
 449  
 450              $this->generateTitle($categoryId, $this->table);
 451  
 452              // Reset the ID because we are making a copy
 453              $this->table->id = 0;
 454  
 455              // Unpublish because we are making a copy
 456              if (isset($this->table->published)) {
 457                  $this->table->published = 0;
 458              } elseif (isset($this->table->state)) {
 459                  $this->table->state = 0;
 460              }
 461  
 462              $hitsAlias = $this->table->getColumnAlias('hits');
 463  
 464              if (isset($this->table->$hitsAlias)) {
 465                  $this->table->$hitsAlias = 0;
 466              }
 467  
 468              // New category ID
 469              $this->table->catid = $categoryId;
 470  
 471              $event = new BeforeBatchEvent(
 472                  $this->event_before_batch,
 473                  ['src' => $this->table, 'type' => 'copy']
 474              );
 475              $this->dispatchEvent($event);
 476  
 477              // @todo: Deal with ordering?
 478              // $this->table->ordering = 1;
 479  
 480              // Check the row.
 481              if (!$this->table->check()) {
 482                  $this->setError($this->table->getError());
 483  
 484                  return false;
 485              }
 486  
 487              // Store the row.
 488              if (!$this->table->store()) {
 489                  $this->setError($this->table->getError());
 490  
 491                  return false;
 492              }
 493  
 494              // Get the new item ID
 495              $newId = $this->table->get('id');
 496  
 497              if (!empty($oldAssetId)) {
 498                  $dbType = strtolower($db->getServerType());
 499  
 500                  // Copy rules
 501                  $query = $db->getQuery(true);
 502                  $query->clear()
 503                      ->update($db->quoteName('#__assets', 't'));
 504  
 505                  if ($dbType === 'mysql') {
 506                      $query->set($db->quoteName('t.rules') . ' = ' . $db->quoteName('s.rules'));
 507                  } else {
 508                      $query->set($db->quoteName('rules') . ' = ' . $db->quoteName('s.rules'));
 509                  }
 510  
 511                  $query->join(
 512                      'INNER',
 513                      $db->quoteName('#__assets', 's'),
 514                      $db->quoteName('s.id') . ' = :oldassetid'
 515                  )
 516                      ->where($db->quoteName('t.id') . ' = :assetid')
 517                      ->bind(':oldassetid', $oldAssetId, ParameterType::INTEGER)
 518                      ->bind(':assetid', $this->table->asset_id, ParameterType::INTEGER);
 519  
 520                  $db->setQuery($query)->execute();
 521              }
 522  
 523              $this->cleanupPostBatchCopy($this->table, $newId, $pk);
 524  
 525              // Add the new ID to the array
 526              $newIds[$pk] = $newId;
 527          }
 528  
 529          // Clean the cache
 530          $this->cleanCache();
 531  
 532          return $newIds;
 533      }
 534  
 535      /**
 536       * Function that can be overridden to do any data cleanup after batch copying data
 537       *
 538       * @param   TableInterface  $table  The table object containing the newly created item
 539       * @param   integer         $newId  The id of the new item
 540       * @param   integer         $oldId  The original item id
 541       *
 542       * @return  void
 543       *
 544       * @since  3.8.12
 545       */
 546      protected function cleanupPostBatchCopy(TableInterface $table, $newId, $oldId)
 547      {
 548      }
 549  
 550      /**
 551       * Batch language changes for a group of rows.
 552       *
 553       * @param   string  $value     The new value matching a language.
 554       * @param   array   $pks       An array of row IDs.
 555       * @param   array   $contexts  An array of item contexts.
 556       *
 557       * @return  boolean  True if successful, false otherwise and internal error is set.
 558       *
 559       * @since   2.5
 560       */
 561      protected function batchLanguage($value, $pks, $contexts)
 562      {
 563          // Initialize re-usable member properties, and re-usable local variables
 564          $this->initBatch();
 565  
 566          foreach ($pks as $pk) {
 567              if ($this->user->authorise('core.edit', $contexts[$pk])) {
 568                  $this->table->reset();
 569                  $this->table->load($pk);
 570                  $this->table->language = $value;
 571  
 572                  $event = new BeforeBatchEvent(
 573                      $this->event_before_batch,
 574                      ['src' => $this->table, 'type' => 'language']
 575                  );
 576                  $this->dispatchEvent($event);
 577  
 578                  // Check the row.
 579                  if (!$this->table->check()) {
 580                      $this->setError($this->table->getError());
 581  
 582                      return false;
 583                  }
 584  
 585                  if (!$this->table->store()) {
 586                      $this->setError($this->table->getError());
 587  
 588                      return false;
 589                  }
 590              } else {
 591                  $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT'));
 592  
 593                  return false;
 594              }
 595          }
 596  
 597          // Clean the cache
 598          $this->cleanCache();
 599  
 600          return true;
 601      }
 602  
 603      /**
 604       * Batch move items to a new category
 605       *
 606       * @param   integer  $value     The new category ID.
 607       * @param   array    $pks       An array of row IDs.
 608       * @param   array    $contexts  An array of item contexts.
 609       *
 610       * @return  boolean  True if successful, false otherwise and internal error is set.
 611       *
 612       * @since   1.7
 613       */
 614      protected function batchMove($value, $pks, $contexts)
 615      {
 616          // Initialize re-usable member properties, and re-usable local variables
 617          $this->initBatch();
 618  
 619          $categoryId = (int) $value;
 620  
 621          if (!$this->checkCategoryId($categoryId)) {
 622              return false;
 623          }
 624  
 625          // Parent exists so we proceed
 626          foreach ($pks as $pk) {
 627              if (!$this->user->authorise('core.edit', $contexts[$pk])) {
 628                  $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT'));
 629  
 630                  return false;
 631              }
 632  
 633              // Check that the row actually exists
 634              if (!$this->table->load($pk)) {
 635                  if ($error = $this->table->getError()) {
 636                      // Fatal error
 637                      $this->setError($error);
 638  
 639                      return false;
 640                  } else {
 641                      // Not fatal error
 642                      $this->setError(Text::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk));
 643                      continue;
 644                  }
 645              }
 646  
 647              // Set the new category ID
 648              $this->table->catid = $categoryId;
 649  
 650              $event = new BeforeBatchEvent(
 651                  $this->event_before_batch,
 652                  ['src' => $this->table, 'type' => 'move']
 653              );
 654              $this->dispatchEvent($event);
 655  
 656              // Check the row.
 657              if (!$this->table->check()) {
 658                  $this->setError($this->table->getError());
 659  
 660                  return false;
 661              }
 662  
 663              // Store the row.
 664              if (!$this->table->store()) {
 665                  $this->setError($this->table->getError());
 666  
 667                  return false;
 668              }
 669          }
 670  
 671          // Clean the cache
 672          $this->cleanCache();
 673  
 674          return true;
 675      }
 676  
 677      /**
 678       * Batch tag a list of item.
 679       *
 680       * @param   integer  $value     The value of the new tag.
 681       * @param   array    $pks       An array of row IDs.
 682       * @param   array    $contexts  An array of item contexts.
 683       *
 684       * @return  boolean  True if successful, false otherwise and internal error is set.
 685       *
 686       * @since   3.1
 687       */
 688      protected function batchTag($value, $pks, $contexts)
 689      {
 690          // Initialize re-usable member properties, and re-usable local variables
 691          $this->initBatch();
 692          $tags = array($value);
 693  
 694          foreach ($pks as $pk) {
 695              if ($this->user->authorise('core.edit', $contexts[$pk])) {
 696                  $this->table->reset();
 697                  $this->table->load($pk);
 698  
 699                  $setTagsEvent = \Joomla\CMS\Event\AbstractEvent::create(
 700                      'onTableSetNewTags',
 701                      array(
 702                          'subject'     => $this->table,
 703                          'newTags'     => $tags,
 704                          'replaceTags' => false,
 705                      )
 706                  );
 707  
 708                  try {
 709                      $this->table->getDispatcher()->dispatch('onTableSetNewTags', $setTagsEvent);
 710                  } catch (\RuntimeException $e) {
 711                      $this->setError($e->getMessage());
 712  
 713                      return false;
 714                  }
 715              } else {
 716                  $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT'));
 717  
 718                  return false;
 719              }
 720          }
 721  
 722          // Clean the cache
 723          $this->cleanCache();
 724  
 725          return true;
 726      }
 727  
 728      /**
 729       * Method to test whether a record can be deleted.
 730       *
 731       * @param   object  $record  A record object.
 732       *
 733       * @return  boolean  True if allowed to delete the record. Defaults to the permission for the component.
 734       *
 735       * @since   1.6
 736       */
 737      protected function canDelete($record)
 738      {
 739          return Factory::getUser()->authorise('core.delete', $this->option);
 740      }
 741  
 742      /**
 743       * Method to test whether a record can have its state changed.
 744       *
 745       * @param   object  $record  A record object.
 746       *
 747       * @return  boolean  True if allowed to change the state of the record. Defaults to the permission for the component.
 748       *
 749       * @since   1.6
 750       */
 751      protected function canEditState($record)
 752      {
 753          return Factory::getUser()->authorise('core.edit.state', $this->option);
 754      }
 755  
 756      /**
 757       * Method override to check-in a record or an array of record
 758       *
 759       * @param   mixed  $pks  The ID of the primary key or an array of IDs
 760       *
 761       * @return  integer|boolean  Boolean false if there is an error, otherwise the count of records checked in.
 762       *
 763       * @since   1.6
 764       */
 765      public function checkin($pks = array())
 766      {
 767          $pks = (array) $pks;
 768          $table = $this->getTable();
 769          $count = 0;
 770  
 771          if (empty($pks)) {
 772              $pks = array((int) $this->getState($this->getName() . '.id'));
 773          }
 774  
 775          $checkedOutField = $table->getColumnAlias('checked_out');
 776  
 777          // Check in all items.
 778          foreach ($pks as $pk) {
 779              if ($table->load($pk)) {
 780                  if ($table->{$checkedOutField} > 0) {
 781                      if (!parent::checkin($pk)) {
 782                          return false;
 783                      }
 784  
 785                      $count++;
 786                  }
 787              } else {
 788                  $this->setError($table->getError());
 789  
 790                  return false;
 791              }
 792          }
 793  
 794          return $count;
 795      }
 796  
 797      /**
 798       * Method override to check-out a record.
 799       *
 800       * @param   integer  $pk  The ID of the primary key.
 801       *
 802       * @return  boolean  True if successful, false if an error occurs.
 803       *
 804       * @since   1.6
 805       */
 806      public function checkout($pk = null)
 807      {
 808          $pk = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id');
 809  
 810          return parent::checkout($pk);
 811      }
 812  
 813      /**
 814       * Method to delete one or more records.
 815       *
 816       * @param   array  &$pks  An array of record primary keys.
 817       *
 818       * @return  boolean  True if successful, false if an error occurs.
 819       *
 820       * @since   1.6
 821       */
 822      public function delete(&$pks)
 823      {
 824          $pks   = ArrayHelper::toInteger((array) $pks);
 825          $table = $this->getTable();
 826  
 827          // Include the plugins for the delete events.
 828          PluginHelper::importPlugin($this->events_map['delete']);
 829  
 830          // Iterate the items to delete each one.
 831          foreach ($pks as $i => $pk) {
 832              if ($table->load($pk)) {
 833                  if ($this->canDelete($table)) {
 834                      $context = $this->option . '.' . $this->name;
 835  
 836                      // Trigger the before delete event.
 837                      $result = Factory::getApplication()->triggerEvent($this->event_before_delete, array($context, $table));
 838  
 839                      if (\in_array(false, $result, true)) {
 840                          $this->setError($table->getError());
 841  
 842                          return false;
 843                      }
 844  
 845                      // Multilanguage: if associated, delete the item in the _associations table
 846                      if ($this->associationsContext && Associations::isEnabled()) {
 847                          $db = $this->getDbo();
 848                          $query = $db->getQuery(true)
 849                              ->select(
 850                                  [
 851                                      'COUNT(*) AS ' . $db->quoteName('count'),
 852                                      $db->quoteName('as1.key'),
 853                                  ]
 854                              )
 855                              ->from($db->quoteName('#__associations', 'as1'))
 856                              ->join('LEFT', $db->quoteName('#__associations', 'as2'), $db->quoteName('as1.key') . ' = ' . $db->quoteName('as2.key'))
 857                              ->where(
 858                                  [
 859                                      $db->quoteName('as1.context') . ' = :context',
 860                                      $db->quoteName('as1.id') . ' = :pk',
 861                                  ]
 862                              )
 863                              ->bind(':context', $this->associationsContext)
 864                              ->bind(':pk', $pk, ParameterType::INTEGER)
 865                              ->group($db->quoteName('as1.key'));
 866  
 867                          $db->setQuery($query);
 868                          $row = $db->loadAssoc();
 869  
 870                          if (!empty($row['count'])) {
 871                              $query = $db->getQuery(true)
 872                                  ->delete($db->quoteName('#__associations'))
 873                                  ->where(
 874                                      [
 875                                          $db->quoteName('context') . ' = :context',
 876                                          $db->quoteName('key') . ' = :key',
 877                                      ]
 878                                  )
 879                                  ->bind(':context', $this->associationsContext)
 880                                  ->bind(':key', $row['key']);
 881  
 882                              if ($row['count'] > 2) {
 883                                  $query->where($db->quoteName('id') . ' = :pk')
 884                                      ->bind(':pk', $pk, ParameterType::INTEGER);
 885                              }
 886  
 887                              $db->setQuery($query);
 888                              $db->execute();
 889                          }
 890                      }
 891  
 892                      if (!$table->delete($pk)) {
 893                          $this->setError($table->getError());
 894  
 895                          return false;
 896                      }
 897  
 898                      // Trigger the after event.
 899                      Factory::getApplication()->triggerEvent($this->event_after_delete, array($context, $table));
 900                  } else {
 901                      // Prune items that you can't change.
 902                      unset($pks[$i]);
 903                      $error = $this->getError();
 904  
 905                      if ($error) {
 906                          Log::add($error, Log::WARNING, 'jerror');
 907  
 908                          return false;
 909                      } else {
 910                          Log::add(Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), Log::WARNING, 'jerror');
 911  
 912                          return false;
 913                      }
 914                  }
 915              } else {
 916                  $this->setError($table->getError());
 917  
 918                  return false;
 919              }
 920          }
 921  
 922          // Clear the component's cache
 923          $this->cleanCache();
 924  
 925          return true;
 926      }
 927  
 928      /**
 929       * Method to change the title & alias.
 930       *
 931       * @param   integer  $categoryId  The id of the category.
 932       * @param   string   $alias       The alias.
 933       * @param   string   $title       The title.
 934       *
 935       * @return  array  Contains the modified title and alias.
 936       *
 937       * @since   1.7
 938       */
 939      protected function generateNewTitle($categoryId, $alias, $title)
 940      {
 941          // Alter the title & alias
 942          $table      = $this->getTable();
 943          $aliasField = $table->getColumnAlias('alias');
 944          $catidField = $table->getColumnAlias('catid');
 945          $titleField = $table->getColumnAlias('title');
 946  
 947          while ($table->load(array($aliasField => $alias, $catidField => $categoryId))) {
 948              if ($title === $table->$titleField) {
 949                  $title = StringHelper::increment($title);
 950              }
 951  
 952              $alias = StringHelper::increment($alias, 'dash');
 953          }
 954  
 955          return array($title, $alias);
 956      }
 957  
 958      /**
 959       * Method to get a single record.
 960       *
 961       * @param   integer  $pk  The id of the primary key.
 962       *
 963       * @return  CMSObject|boolean  Object on success, false on failure.
 964       *
 965       * @since   1.6
 966       */
 967      public function getItem($pk = null)
 968      {
 969          $pk = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id');
 970          $table = $this->getTable();
 971  
 972          if ($pk > 0) {
 973              // Attempt to load the row.
 974              $return = $table->load($pk);
 975  
 976              // Check for a table object error.
 977              if ($return === false) {
 978                  // If there was no underlying error, then the false means there simply was not a row in the db for this $pk.
 979                  if (!$table->getError()) {
 980                      $this->setError(Text::_('JLIB_APPLICATION_ERROR_NOT_EXIST'));
 981                  } else {
 982                      $this->setError($table->getError());
 983                  }
 984  
 985                  return false;
 986              }
 987          }
 988  
 989          // Convert to the CMSObject before adding other data.
 990          $properties = $table->getProperties(1);
 991          $item = ArrayHelper::toObject($properties, CMSObject::class);
 992  
 993          if (property_exists($item, 'params')) {
 994              $registry = new Registry($item->params);
 995              $item->params = $registry->toArray();
 996          }
 997  
 998          return $item;
 999      }
1000  
1001      /**
1002       * A protected method to get a set of ordering conditions.
1003       *
1004       * @param   Table  $table  A Table object.
1005       *
1006       * @return  array  An array of conditions to add to ordering queries.
1007       *
1008       * @since   1.6
1009       */
1010      protected function getReorderConditions($table)
1011      {
1012          return [];
1013      }
1014  
1015      /**
1016       * Stock method to auto-populate the model state.
1017       *
1018       * @return  void
1019       *
1020       * @since   1.6
1021       */
1022      protected function populateState()
1023      {
1024          $table = $this->getTable();
1025          $key = $table->getKeyName();
1026  
1027          // Get the pk of the record from the request.
1028          $pk = Factory::getApplication()->input->getInt($key);
1029          $this->setState($this->getName() . '.id', $pk);
1030  
1031          // Load the parameters.
1032          $value = ComponentHelper::getParams($this->option);
1033          $this->setState('params', $value);
1034      }
1035  
1036      /**
1037       * Prepare and sanitise the table data prior to saving.
1038       *
1039       * @param   Table  $table  A reference to a Table object.
1040       *
1041       * @return  void
1042       *
1043       * @since   1.6
1044       */
1045      protected function prepareTable($table)
1046      {
1047          // Derived class will provide its own implementation if required.
1048      }
1049  
1050      /**
1051       * Method to change the published state of one or more records.
1052       *
1053       * @param   array    &$pks   A list of the primary keys to change.
1054       * @param   integer  $value  The value of the published state.
1055       *
1056       * @return  boolean  True on success.
1057       *
1058       * @since   1.6
1059       */
1060      public function publish(&$pks, $value = 1)
1061      {
1062          $user = Factory::getUser();
1063          $table = $this->getTable();
1064          $pks = (array) $pks;
1065  
1066          $context = $this->option . '.' . $this->name;
1067  
1068          // Include the plugins for the change of state event.
1069          PluginHelper::importPlugin($this->events_map['change_state']);
1070  
1071          // Access checks.
1072          foreach ($pks as $i => $pk) {
1073              $table->reset();
1074  
1075              if ($table->load($pk)) {
1076                  if (!$this->canEditState($table)) {
1077                      // Prune items that you can't change.
1078                      unset($pks[$i]);
1079  
1080                      Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror');
1081  
1082                      return false;
1083                  }
1084  
1085                  // If the table is checked out by another user, drop it and report to the user trying to change its state.
1086                  if ($table->hasField('checked_out') && $table->checked_out && ($table->checked_out != $user->id)) {
1087                      Log::add(Text::_('JLIB_APPLICATION_ERROR_CHECKIN_USER_MISMATCH'), Log::WARNING, 'jerror');
1088  
1089                      // Prune items that you can't change.
1090                      unset($pks[$i]);
1091  
1092                      return false;
1093                  }
1094  
1095                  /**
1096                   * Prune items that are already at the given state.  Note: Only models whose table correctly
1097                   * sets 'published' column alias (if different than published) will benefit from this
1098                   */
1099                  $publishedColumnName = $table->getColumnAlias('published');
1100  
1101                  if (property_exists($table, $publishedColumnName) && $table->get($publishedColumnName, $value) == $value) {
1102                      unset($pks[$i]);
1103                  }
1104              }
1105          }
1106  
1107          // Check if there are items to change
1108          if (!\count($pks)) {
1109              return true;
1110          }
1111  
1112          // Trigger the before change state event.
1113          $result = Factory::getApplication()->triggerEvent($this->event_before_change_state, array($context, $pks, $value));
1114  
1115          if (\in_array(false, $result, true)) {
1116              $this->setError($table->getError());
1117  
1118              return false;
1119          }
1120  
1121          // Attempt to change the state of the records.
1122          if (!$table->publish($pks, $value, $user->get('id'))) {
1123              $this->setError($table->getError());
1124  
1125              return false;
1126          }
1127  
1128          // Trigger the change state event.
1129          $result = Factory::getApplication()->triggerEvent($this->event_change_state, array($context, $pks, $value));
1130  
1131          if (\in_array(false, $result, true)) {
1132              $this->setError($table->getError());
1133  
1134              return false;
1135          }
1136  
1137          // Clear the component's cache
1138          $this->cleanCache();
1139  
1140          return true;
1141      }
1142  
1143      /**
1144       * Method to adjust the ordering of a row.
1145       *
1146       * Returns NULL if the user did not have edit
1147       * privileges for any of the selected primary keys.
1148       *
1149       * @param   integer  $pks    The ID of the primary key to move.
1150       * @param   integer  $delta  Increment, usually +1 or -1
1151       *
1152       * @return  boolean|null  False on failure or error, true on success, null if the $pk is empty (no items selected).
1153       *
1154       * @since   1.6
1155       */
1156      public function reorder($pks, $delta = 0)
1157      {
1158          $table = $this->getTable();
1159          $pks = (array) $pks;
1160          $result = true;
1161  
1162          $allowed = true;
1163  
1164          foreach ($pks as $i => $pk) {
1165              $table->reset();
1166  
1167              if ($table->load($pk) && $this->checkout($pk)) {
1168                  // Access checks.
1169                  if (!$this->canEditState($table)) {
1170                      // Prune items that you can't change.
1171                      unset($pks[$i]);
1172                      $this->checkin($pk);
1173                      Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror');
1174                      $allowed = false;
1175                      continue;
1176                  }
1177  
1178                  $where = $this->getReorderConditions($table);
1179  
1180                  if (!$table->move($delta, $where)) {
1181                      $this->setError($table->getError());
1182                      unset($pks[$i]);
1183                      $result = false;
1184                  }
1185  
1186                  $this->checkin($pk);
1187              } else {
1188                  $this->setError($table->getError());
1189                  unset($pks[$i]);
1190                  $result = false;
1191              }
1192          }
1193  
1194          if ($allowed === false && empty($pks)) {
1195              $result = null;
1196          }
1197  
1198          // Clear the component's cache
1199          if ($result == true) {
1200              $this->cleanCache();
1201          }
1202  
1203          return $result;
1204      }
1205  
1206      /**
1207       * Method to save the form data.
1208       *
1209       * @param   array  $data  The form data.
1210       *
1211       * @return  boolean  True on success, False on error.
1212       *
1213       * @since   1.6
1214       */
1215      public function save($data)
1216      {
1217          $table      = $this->getTable();
1218          $context    = $this->option . '.' . $this->name;
1219          $app        = Factory::getApplication();
1220  
1221          if (\array_key_exists('tags', $data) && \is_array($data['tags'])) {
1222              $table->newTags = $data['tags'];
1223          }
1224  
1225          $key = $table->getKeyName();
1226          $pk = (isset($data[$key])) ? $data[$key] : (int) $this->getState($this->getName() . '.id');
1227          $isNew = true;
1228  
1229          // Include the plugins for the save events.
1230          PluginHelper::importPlugin($this->events_map['save']);
1231  
1232          // Allow an exception to be thrown.
1233          try {
1234              // Load the row if saving an existing record.
1235              if ($pk > 0) {
1236                  $table->load($pk);
1237                  $isNew = false;
1238              }
1239  
1240              // Bind the data.
1241              if (!$table->bind($data)) {
1242                  $this->setError($table->getError());
1243  
1244                  return false;
1245              }
1246  
1247              // Prepare the row for saving
1248              $this->prepareTable($table);
1249  
1250              // Check the data.
1251              if (!$table->check()) {
1252                  $this->setError($table->getError());
1253  
1254                  return false;
1255              }
1256  
1257              // Trigger the before save event.
1258              $result = $app->triggerEvent($this->event_before_save, array($context, $table, $isNew, $data));
1259  
1260              if (\in_array(false, $result, true)) {
1261                  $this->setError($table->getError());
1262  
1263                  return false;
1264              }
1265  
1266              // Store the data.
1267              if (!$table->store()) {
1268                  $this->setError($table->getError());
1269  
1270                  return false;
1271              }
1272  
1273              // Clean the cache.
1274              $this->cleanCache();
1275  
1276              // Trigger the after save event.
1277              $app->triggerEvent($this->event_after_save, array($context, $table, $isNew, $data));
1278          } catch (\Exception $e) {
1279              $this->setError($e->getMessage());
1280  
1281              return false;
1282          }
1283  
1284          if (isset($table->$key)) {
1285              $this->setState($this->getName() . '.id', $table->$key);
1286          }
1287  
1288          $this->setState($this->getName() . '.new', $isNew);
1289  
1290          if ($this->associationsContext && Associations::isEnabled() && !empty($data['associations'])) {
1291              $associations = $data['associations'];
1292  
1293              // Unset any invalid associations
1294              $associations = ArrayHelper::toInteger($associations);
1295  
1296              // Unset any invalid associations
1297              foreach ($associations as $tag => $id) {
1298                  if (!$id) {
1299                      unset($associations[$tag]);
1300                  }
1301              }
1302  
1303              // Show a warning if the item isn't assigned to a language but we have associations.
1304              if ($associations && $table->language === '*') {
1305                  $app->enqueueMessage(
1306                      Text::_(strtoupper($this->option) . '_ERROR_ALL_LANGUAGE_ASSOCIATED'),
1307                      'warning'
1308                  );
1309              }
1310  
1311              // Get associationskey for edited item
1312              $db    = $this->getDbo();
1313              $id    = (int) $table->$key;
1314              $query = $db->getQuery(true)
1315                  ->select($db->quoteName('key'))
1316                  ->from($db->quoteName('#__associations'))
1317                  ->where($db->quoteName('context') . ' = :context')
1318                  ->where($db->quoteName('id') . ' = :id')
1319                  ->bind(':context', $this->associationsContext)
1320                  ->bind(':id', $id, ParameterType::INTEGER);
1321              $db->setQuery($query);
1322              $oldKey = $db->loadResult();
1323  
1324              if ($associations || $oldKey !== null) {
1325                  // Deleting old associations for the associated items
1326                  $query = $db->getQuery(true)
1327                      ->delete($db->quoteName('#__associations'))
1328                      ->where($db->quoteName('context') . ' = :context')
1329                      ->bind(':context', $this->associationsContext);
1330  
1331                  $where = [];
1332  
1333                  if ($associations) {
1334                      $where[] = $db->quoteName('id') . ' IN (' . implode(',', $query->bindArray(array_values($associations))) . ')';
1335                  }
1336  
1337                  if ($oldKey !== null) {
1338                      $where[] = $db->quoteName('key') . ' = :oldKey';
1339                      $query->bind(':oldKey', $oldKey);
1340                  }
1341  
1342                  $query->extendWhere('AND', $where, 'OR');
1343                  $db->setQuery($query);
1344                  $db->execute();
1345              }
1346  
1347              // Adding self to the association
1348              if ($table->language !== '*') {
1349                  $associations[$table->language] = (int) $table->$key;
1350              }
1351  
1352              if (\count($associations) > 1) {
1353                  // Adding new association for these items
1354                  $key   = md5(json_encode($associations));
1355                  $query = $db->getQuery(true)
1356                      ->insert($db->quoteName('#__associations'))
1357                      ->columns(
1358                          [
1359                              $db->quoteName('id'),
1360                              $db->quoteName('context'),
1361                              $db->quoteName('key'),
1362                          ]
1363                      );
1364  
1365                  foreach ($associations as $id) {
1366                      $query->values(
1367                          implode(
1368                              ',',
1369                              $query->bindArray(
1370                                  [$id, $this->associationsContext, $key],
1371                                  [ParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING]
1372                              )
1373                          )
1374                      );
1375                  }
1376  
1377                  $db->setQuery($query);
1378                  $db->execute();
1379              }
1380          }
1381  
1382          if ($app->input->get('task') == 'editAssociations') {
1383              return $this->redirectToAssociations($data);
1384          }
1385  
1386          return true;
1387      }
1388  
1389      /**
1390       * Saves the manually set order of records.
1391       *
1392       * @param   array    $pks    An array of primary key ids.
1393       * @param   integer  $order  +1 or -1
1394       *
1395       * @return  boolean  Boolean true on success, false on failure
1396       *
1397       * @since   1.6
1398       */
1399      public function saveorder($pks = array(), $order = null)
1400      {
1401          // Initialize re-usable member properties
1402          $this->initBatch();
1403  
1404          $conditions = array();
1405  
1406          if (empty($pks)) {
1407              Factory::getApplication()->enqueueMessage(Text::_($this->text_prefix . '_ERROR_NO_ITEMS_SELECTED'), 'error');
1408  
1409              return false;
1410          }
1411  
1412          $orderingField = $this->table->getColumnAlias('ordering');
1413  
1414          // Update ordering values
1415          foreach ($pks as $i => $pk) {
1416              $this->table->load((int) $pk);
1417  
1418              // We don't want to modify tags on reorder, not removing the tagsHelper removes all associated tags
1419              if ($this->table instanceof TaggableTableInterface) {
1420                  $this->table->clearTagsHelper();
1421              }
1422  
1423              // Access checks.
1424              if (!$this->canEditState($this->table)) {
1425                  // Prune items that you can't change.
1426                  unset($pks[$i]);
1427                  Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror');
1428              } elseif ($this->table->$orderingField != $order[$i]) {
1429                  $this->table->$orderingField = $order[$i];
1430  
1431                  if (!$this->table->store()) {
1432                      $this->setError($this->table->getError());
1433  
1434                      return false;
1435                  }
1436  
1437                  // Remember to reorder within position and client_id
1438                  $condition = $this->getReorderConditions($this->table);
1439                  $found = false;
1440  
1441                  foreach ($conditions as $cond) {
1442                      if ($cond[1] == $condition) {
1443                          $found = true;
1444                          break;
1445                      }
1446                  }
1447  
1448                  if (!$found) {
1449                      $key = $this->table->getKeyName();
1450                      $conditions[] = array($this->table->$key, $condition);
1451                  }
1452              }
1453          }
1454  
1455          // Execute reorder for each category.
1456          foreach ($conditions as $cond) {
1457              $this->table->load($cond[0]);
1458              $this->table->reorder($cond[1]);
1459          }
1460  
1461          // Clear the component's cache
1462          $this->cleanCache();
1463  
1464          return true;
1465      }
1466  
1467      /**
1468       * Method to check the validity of the category ID for batch copy and move
1469       *
1470       * @param   integer  $categoryId  The category ID to check
1471       *
1472       * @return  boolean
1473       *
1474       * @since   3.2
1475       */
1476      protected function checkCategoryId($categoryId)
1477      {
1478          // Check that the category exists
1479          if ($categoryId) {
1480              $categoryTable = Table::getInstance('Category');
1481  
1482              if (!$categoryTable->load($categoryId)) {
1483                  if ($error = $categoryTable->getError()) {
1484                      // Fatal error
1485                      $this->setError($error);
1486  
1487                      return false;
1488                  } else {
1489                      $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND'));
1490  
1491                      return false;
1492                  }
1493              }
1494          }
1495  
1496          if (empty($categoryId)) {
1497              $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND'));
1498  
1499              return false;
1500          }
1501  
1502          // Check that the user has create permission for the component
1503          $extension = Factory::getApplication()->input->get('option', '');
1504          $user = Factory::getUser();
1505  
1506          if (!$user->authorise('core.create', $extension . '.category.' . $categoryId)) {
1507              $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_CREATE'));
1508  
1509              return false;
1510          }
1511  
1512          return true;
1513      }
1514  
1515      /**
1516       * A method to preprocess generating a new title in order to allow tables with alternative names
1517       * for alias and title to use the batch move and copy methods
1518       *
1519       * @param   integer  $categoryId  The target category id
1520       * @param   Table    $table       The Table within which move or copy is taking place
1521       *
1522       * @return  void
1523       *
1524       * @since   3.2
1525       */
1526      public function generateTitle($categoryId, $table)
1527      {
1528          // Alter the title & alias
1529          $titleField         = $table->getColumnAlias('title');
1530          $aliasField         = $table->getColumnAlias('alias');
1531          $data               = $this->generateNewTitle($categoryId, $table->$aliasField, $table->$titleField);
1532          $table->$titleField = $data['0'];
1533          $table->$aliasField = $data['1'];
1534      }
1535  
1536      /**
1537       * Method to initialize member variables used by batch methods and other methods like saveorder()
1538       *
1539       * @return  void
1540       *
1541       * @since   3.8.2
1542       */
1543      public function initBatch()
1544      {
1545          if ($this->batchSet === null) {
1546              $this->batchSet = true;
1547  
1548              // Get current user
1549              $this->user = Factory::getUser();
1550  
1551              // Get table
1552              $this->table = $this->getTable();
1553  
1554              // Get table class name
1555              $tc = explode('\\', \get_class($this->table));
1556              $this->tableClassName = end($tc);
1557  
1558              // Get UCM Type data
1559              $this->contentType = new UCMType();
1560              $this->type = $this->contentType->getTypeByTable($this->tableClassName)
1561                  ?: $this->contentType->getTypeByAlias($this->typeAlias);
1562          }
1563      }
1564  
1565      /**
1566       * Method to load an item in com_associations.
1567       *
1568       * @param   array  $data  The form data.
1569       *
1570       * @return  boolean  True if successful, false otherwise.
1571       *
1572       * @since   3.9.0
1573       *
1574       * @deprecated 5.0  It is handled by regular save method now.
1575       */
1576      public function editAssociations($data)
1577      {
1578          // Save the item
1579          return $this->save($data);
1580      }
1581  
1582      /**
1583       * Method to load an item in com_associations.
1584       *
1585       * @param   array  $data  The form data.
1586       *
1587       * @return  boolean  True if successful, false otherwise.
1588       *
1589       * @throws \Exception
1590       * @since   3.9.17
1591       */
1592      protected function redirectToAssociations($data)
1593      {
1594          $app = Factory::getApplication();
1595          $id  = $data['id'];
1596  
1597          // Deal with categories associations
1598          if ($this->text_prefix === 'COM_CATEGORIES') {
1599              $extension       = $app->input->get('extension', 'com_content');
1600              $this->typeAlias = $extension . '.category';
1601              $component       = strtolower($this->text_prefix);
1602              $view            = 'category';
1603          } else {
1604              $aliasArray = explode('.', $this->typeAlias);
1605              $component  = $aliasArray[0];
1606              $view       = $aliasArray[1];
1607              $extension  = '';
1608          }
1609  
1610          // Menu item redirect needs admin client
1611          $client = $component === 'com_menus' ? '&client_id=0' : '';
1612  
1613          if ($id == 0) {
1614              $app->enqueueMessage(Text::_('JGLOBAL_ASSOCIATIONS_NEW_ITEM_WARNING'), 'error');
1615              $app->redirect(
1616                  Route::_('index.php?option=' . $component . '&view=' . $view . $client . '&layout=edit&id=' . $id . $extension, false)
1617              );
1618  
1619              return false;
1620          }
1621  
1622          if ($data['language'] === '*') {
1623              $app->enqueueMessage(Text::_('JGLOBAL_ASSOC_NOT_POSSIBLE'), 'notice');
1624              $app->redirect(
1625                  Route::_('index.php?option=' . $component . '&view=' . $view . $client . '&layout=edit&id=' . $id . $extension, false)
1626              );
1627  
1628              return false;
1629          }
1630  
1631          $languages = LanguageHelper::getContentLanguages(array(0, 1));
1632          $target    = '';
1633  
1634          /**
1635           * If the site contains only 2 languages and an association exists for the item
1636           * load directly the associated target item in the side by side view
1637           * otherwise select already the target language
1638           */
1639          if (count($languages) === 2) {
1640              foreach ($languages as $language) {
1641                  $lang_code[] = $language->lang_code;
1642              }
1643  
1644              $refLang    = array($data['language']);
1645              $targetLang = array_diff($lang_code, $refLang);
1646              $targetLang = implode(',', $targetLang);
1647              $targetId   = $data['associations'][$targetLang];
1648  
1649              if ($targetId) {
1650                  $target = '&target=' . $targetLang . '%3A' . $targetId . '%3Aedit';
1651              } else {
1652                  $target = '&target=' . $targetLang . '%3A0%3Aadd';
1653              }
1654          }
1655  
1656          $app->redirect(
1657              Route::_(
1658                  'index.php?option=com_associations&view=association&layout=edit&itemtype=' . $this->typeAlias
1659                  . '&task=association.edit&id=' . $id . $target,
1660                  false
1661              )
1662          );
1663  
1664          return true;
1665      }
1666  }


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