[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/plugins/content/joomla/ -> joomla.php (source)

   1  <?php
   2  
   3  /**
   4   * @package     Joomla.Plugin
   5   * @subpackage  Content.joomla
   6   *
   7   * @copyright   (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
   8   * @license     GNU General Public License version 2 or later; see LICENSE.txt
   9  
  10   * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
  11   */
  12  
  13  use Joomla\CMS\Application\CMSApplicationInterface;
  14  use Joomla\CMS\Component\ComponentHelper;
  15  use Joomla\CMS\Language\Language;
  16  use Joomla\CMS\Language\Text;
  17  use Joomla\CMS\Plugin\CMSPlugin;
  18  use Joomla\CMS\Table\CoreContent;
  19  use Joomla\CMS\User\User;
  20  use Joomla\CMS\Workflow\WorkflowServiceInterface;
  21  use Joomla\Component\Workflow\Administrator\Table\StageTable;
  22  use Joomla\Component\Workflow\Administrator\Table\WorkflowTable;
  23  use Joomla\Database\DatabaseDriver;
  24  use Joomla\Database\ParameterType;
  25  use Joomla\Utilities\ArrayHelper;
  26  
  27  // phpcs:disable PSR1.Files.SideEffects
  28  \defined('_JEXEC') or die;
  29  // phpcs:enable PSR1.Files.SideEffects
  30  
  31  /**
  32   * Example Content Plugin
  33   *
  34   * @since  1.6
  35   */
  36  class PlgContentJoomla extends CMSPlugin
  37  {
  38      /**
  39       * Application object
  40       *
  41       * @var    CMSApplicationInterface
  42       * @since  4.0.0
  43       */
  44      protected $app;
  45  
  46      /**
  47       * Database Driver Instance
  48       *
  49       * @var    DatabaseDriver
  50       * @since  4.0.0
  51       */
  52      protected $db;
  53  
  54      /**
  55       * The save event.
  56       *
  57       * @param   string   $context  The context
  58       * @param   object   $table    The item
  59       * @param   boolean  $isNew    Is new item
  60       * @param   array    $data     The validated data
  61       *
  62       * @return  boolean
  63       *
  64       * @since   4.0.0
  65       */
  66      public function onContentBeforeSave($context, $table, $isNew, $data)
  67      {
  68          if ($context === 'com_menus.item') {
  69              return $this->checkMenuItemBeforeSave($context, $table, $isNew, $data);
  70          }
  71  
  72          // Check we are handling the frontend edit form.
  73          if (!in_array($context, ['com_workflow.stage', 'com_workflow.workflow']) || $isNew) {
  74              return true;
  75          }
  76  
  77          $item = clone $table;
  78  
  79          $item->load($table->id);
  80  
  81          if ($item->published != -2 && $data['published'] == -2) {
  82              switch ($context) {
  83                  case 'com_workflow.workflow':
  84                      return $this->_canDeleteWorkflow($item->id);
  85  
  86                  case 'com_workflow.stage':
  87                      return $this->_canDeleteStage($item->id);
  88              }
  89          }
  90  
  91          return true;
  92      }
  93  
  94      /**
  95       * Example after save content method
  96       * Article is passed by reference, but after the save, so no changes will be saved.
  97       * Method is called right after the content is saved
  98       *
  99       * @param   string   $context  The context of the content passed to the plugin (added in 1.6)
 100       * @param   object   $article  A JTableContent object
 101       * @param   boolean  $isNew    If the content is just about to be created
 102       *
 103       * @return  void
 104       *
 105       * @since   1.6
 106       */
 107      public function onContentAfterSave($context, $article, $isNew): void
 108      {
 109          // Check we are handling the frontend edit form.
 110          if ($context !== 'com_content.form') {
 111              return;
 112          }
 113  
 114          // Check if this function is enabled.
 115          if (!$this->params->def('email_new_fe', 1)) {
 116              return;
 117          }
 118  
 119          // Check this is a new article.
 120          if (!$isNew) {
 121              return;
 122          }
 123  
 124          $db = $this->db;
 125          $query = $db->getQuery(true)
 126              ->select($db->quoteName('id'))
 127              ->from($db->quoteName('#__users'))
 128              ->where($db->quoteName('sendEmail') . ' = 1')
 129              ->where($db->quoteName('block') . ' = 0');
 130          $db->setQuery($query);
 131          $users = (array) $db->loadColumn();
 132  
 133          if (empty($users)) {
 134              return;
 135          }
 136  
 137          $user = $this->app->getIdentity();
 138  
 139          // Messaging for new items
 140  
 141          $default_language = ComponentHelper::getParams('com_languages')->get('administrator');
 142          $debug = $this->app->get('debug_lang');
 143  
 144          foreach ($users as $user_id) {
 145              if ($user_id != $user->id) {
 146                  // Load language for messaging
 147                  $receiver = User::getInstance($user_id);
 148                  $lang = Language::getInstance($receiver->getParam('admin_language', $default_language), $debug);
 149                  $lang->load('com_content');
 150                  $message = array(
 151                      'user_id_to' => $user_id,
 152                      'subject' => $lang->_('COM_CONTENT_NEW_ARTICLE'),
 153                      'message' => sprintf($lang->_('COM_CONTENT_ON_NEW_CONTENT'), $user->get('name'), $article->title),
 154                  );
 155                  $model_message = $this->app->bootComponent('com_messages')->getMVCFactory()
 156                      ->createModel('Message', 'Administrator');
 157                  $model_message->save($message);
 158              }
 159          }
 160      }
 161  
 162      /**
 163       * Don't allow categories to be deleted if they contain items or subcategories with items
 164       *
 165       * @param   string  $context  The context for the content passed to the plugin.
 166       * @param   object  $data     The data relating to the content that was deleted.
 167       *
 168       * @return  boolean
 169       *
 170       * @since   1.6
 171       */
 172      public function onContentBeforeDelete($context, $data)
 173      {
 174          // Skip plugin if we are deleting something other than categories
 175          if (!in_array($context, ['com_categories.category', 'com_workflow.stage', 'com_workflow.workflow'])) {
 176              return true;
 177          }
 178  
 179          switch ($context) {
 180              case 'com_categories.category':
 181                  return $this->_canDeleteCategories($data);
 182  
 183              case 'com_workflow.workflow':
 184                  return $this->_canDeleteWorkflow($data->id);
 185  
 186              case 'com_workflow.stage':
 187                  return $this->_canDeleteStage($data->id);
 188          }
 189      }
 190  
 191      /**
 192       * Don't allow workflows/stages to be deleted if they contain items
 193       *
 194       * @param   string  $context  The context for the content passed to the plugin.
 195       * @param   object  $pks      The IDs of the records which will be changed.
 196       * @param   object  $value    The new state.
 197       *
 198       * @return  boolean
 199       *
 200       * @since   4.0.0
 201       */
 202      public function onContentBeforeChangeState($context, $pks, $value)
 203      {
 204          if ($value != -2 || !in_array($context, ['com_workflow.workflow', 'com_workflow.stage'])) {
 205              return true;
 206          }
 207  
 208          $result = true;
 209  
 210          foreach ($pks as $id) {
 211              switch ($context) {
 212                  case 'com_workflow.workflow':
 213                      return $result && $this->_canDeleteWorkflow($id);
 214  
 215                  case 'com_workflow.stage':
 216                      $result = $result && $this->_canDeleteStage($id);
 217              }
 218          }
 219  
 220          return true;
 221      }
 222  
 223      /**
 224       * Checks if a given category can be deleted
 225       *
 226       * @param   object  $data  The category object
 227       *
 228       * @return  boolean
 229       */
 230      private function _canDeleteCategories($data)
 231      {
 232          // Check if this function is enabled.
 233          if (!$this->params->def('check_categories', 1)) {
 234              return true;
 235          }
 236  
 237          $extension = $this->app->input->getString('extension');
 238  
 239          // Default to true if not a core extension
 240          $result = true;
 241  
 242          $tableInfo = array(
 243              'com_banners' => array('table_name' => '#__banners'),
 244              'com_contact' => array('table_name' => '#__contact_details'),
 245              'com_content' => array('table_name' => '#__content'),
 246              'com_newsfeeds' => array('table_name' => '#__newsfeeds'),
 247              'com_users' => array('table_name' => '#__user_notes'),
 248              'com_weblinks' => array('table_name' => '#__weblinks'),
 249          );
 250  
 251          // Now check to see if this is a known core extension
 252          if (isset($tableInfo[$extension])) {
 253              // Get table name for known core extensions
 254              $table = $tableInfo[$extension]['table_name'];
 255  
 256              // See if this category has any content items
 257              $count = $this->_countItemsInCategory($table, $data->get('id'));
 258  
 259              // Return false if db error
 260              if ($count === false) {
 261                  $result = false;
 262              } else {
 263                  // Show error if items are found in the category
 264                  if ($count > 0) {
 265                      $msg = Text::sprintf('COM_CATEGORIES_DELETE_NOT_ALLOWED', $data->get('title'))
 266                          . ' ' . Text::plural('COM_CATEGORIES_N_ITEMS_ASSIGNED', $count);
 267                      $this->app->enqueueMessage($msg, 'error');
 268                      $result = false;
 269                  }
 270  
 271                  // Check for items in any child categories (if it is a leaf, there are no child categories)
 272                  if (!$data->isLeaf()) {
 273                      $count = $this->_countItemsInChildren($table, $data->get('id'), $data);
 274  
 275                      if ($count === false) {
 276                          $result = false;
 277                      } elseif ($count > 0) {
 278                          $msg = Text::sprintf('COM_CATEGORIES_DELETE_NOT_ALLOWED', $data->get('title'))
 279                              . ' ' . Text::plural('COM_CATEGORIES_HAS_SUBCATEGORY_ITEMS', $count);
 280                          $this->app->enqueueMessage($msg, 'error');
 281                          $result = false;
 282                      }
 283                  }
 284              }
 285          }
 286  
 287          return $result;
 288      }
 289  
 290      /**
 291       * Checks if a given workflow can be deleted
 292       *
 293       * @param   int  $pk  The stage ID
 294       *
 295       * @return  boolean
 296       *
 297       * @since  4.0.0
 298       */
 299      private function _canDeleteWorkflow($pk)
 300      {
 301          // Check if this workflow is the default stage
 302          $table = new WorkflowTable($this->db);
 303  
 304          $table->load($pk);
 305  
 306          if (empty($table->id)) {
 307              return true;
 308          }
 309  
 310          if ($table->default) {
 311              throw new Exception(Text::_('COM_WORKFLOW_MSG_DELETE_IS_DEFAULT'));
 312          }
 313  
 314          $parts = explode('.', $table->extension);
 315  
 316          $component = $this->app->bootComponent($parts[0]);
 317  
 318          $section = '';
 319  
 320          if (!empty($parts[1])) {
 321              $section = $parts[1];
 322          }
 323  
 324          // No core interface => we're ok
 325          if (!$component instanceof WorkflowServiceInterface) {
 326              return true;
 327          }
 328  
 329          /** @var \Joomla\Component\Workflow\Administrator\Model\StagesModel $model */
 330          $model = $this->app->bootComponent('com_workflow')->getMVCFactory()
 331              ->createModel('Stages', 'Administrator', ['ignore_request' => true]);
 332  
 333          $model->setState('filter.workflow_id', $pk);
 334          $model->setState('filter.extension', $table->extension);
 335  
 336          $stages = $model->getItems();
 337  
 338          $stage_ids = array_column($stages, 'id');
 339  
 340          $result = $this->_countItemsInStage($stage_ids, $table->extension);
 341  
 342          // Return false if db error
 343          if ($result > 0) {
 344              throw new Exception(Text::_('COM_WORKFLOW_MSG_DELETE_WORKFLOW_IS_ASSIGNED'));
 345          }
 346  
 347          return true;
 348      }
 349  
 350      /**
 351       * Checks if a given stage can be deleted
 352       *
 353       * @param   int  $pk  The stage ID
 354       *
 355       * @return  boolean
 356       *
 357       * @since  4.0.0
 358       */
 359      private function _canDeleteStage($pk)
 360      {
 361          $table = new StageTable($this->db);
 362  
 363          $table->load($pk);
 364  
 365          if (empty($table->id)) {
 366              return true;
 367          }
 368  
 369          // Check if this stage is the default stage
 370          if ($table->default) {
 371              throw new Exception(Text::_('COM_WORKFLOW_MSG_DELETE_IS_DEFAULT'));
 372          }
 373  
 374          $workflow = new WorkflowTable($this->db);
 375  
 376          $workflow->load($table->workflow_id);
 377  
 378          if (empty($workflow->id)) {
 379              return true;
 380          }
 381  
 382          $parts = explode('.', $workflow->extension);
 383  
 384          $component = $this->app->bootComponent($parts[0]);
 385  
 386          // No core interface => we're ok
 387          if (!$component instanceof WorkflowServiceInterface) {
 388              return true;
 389          }
 390  
 391          $stage_ids = [$table->id];
 392  
 393          $result = $this->_countItemsInStage($stage_ids, $workflow->extension);
 394  
 395          // Return false if db error
 396          if ($result > 0) {
 397              throw new Exception(Text::_('COM_WORKFLOW_MSG_DELETE_STAGE_IS_ASSIGNED'));
 398          }
 399  
 400          return true;
 401      }
 402  
 403      /**
 404       * Get count of items in a category
 405       *
 406       * @param   string   $table  table name of component table (column is catid)
 407       * @param   integer  $catid  id of the category to check
 408       *
 409       * @return  mixed  count of items found or false if db error
 410       *
 411       * @since   1.6
 412       */
 413      private function _countItemsInCategory($table, $catid)
 414      {
 415          $db = $this->db;
 416          $query = $db->getQuery(true);
 417  
 418          // Count the items in this category
 419          $query->select('COUNT(' . $db->quoteName('id') . ')')
 420              ->from($db->quoteName($table))
 421              ->where($db->quoteName('catid') . ' = :catid')
 422              ->bind(':catid', $catid, ParameterType::INTEGER);
 423          $db->setQuery($query);
 424  
 425          try {
 426              $count = $db->loadResult();
 427          } catch (RuntimeException $e) {
 428              $this->app->enqueueMessage($e->getMessage(), 'error');
 429  
 430              return false;
 431          }
 432  
 433          return $count;
 434      }
 435  
 436      /**
 437       * Get count of items in assigned to a stage
 438       *
 439       * @param   array   $stageIds   The stage ids to test for
 440       * @param   string  $extension  The extension of the workflow
 441       *
 442       * @return  bool
 443       *
 444       * @since   4.0.0
 445       */
 446      private function _countItemsInStage(array $stageIds, string $extension): bool
 447      {
 448          $db = $this->db;
 449  
 450          $parts = explode('.', $extension);
 451  
 452          $stageIds = ArrayHelper::toInteger($stageIds);
 453          $stageIds = array_filter($stageIds);
 454  
 455          $section = '';
 456  
 457          if (!empty($parts[1])) {
 458              $section = $parts[1];
 459          }
 460  
 461          $component = $this->app->bootComponent($parts[0]);
 462  
 463          $table = $component->getWorkflowTableBySection($section);
 464  
 465          if (empty($stageIds) || !$table) {
 466              return false;
 467          }
 468  
 469          $query = $db->getQuery(true);
 470  
 471          $query->select('COUNT(' . $db->quoteName('b.id') . ')')
 472              ->from($db->quoteName('#__workflow_associations', 'wa'))
 473              ->from($db->quoteName('#__workflow_stages', 's'))
 474              ->from($db->quoteName($table, 'b'))
 475              ->where($db->quoteName('wa.stage_id') . ' = ' . $db->quoteName('s.id'))
 476              ->where($db->quoteName('wa.item_id') . ' = ' . $db->quoteName('b.id'))
 477              ->whereIn($db->quoteName('s.id'), $stageIds);
 478  
 479          try {
 480              return (int) $db->setQuery($query)->loadResult();
 481          } catch (Exception $e) {
 482              $this->app->enqueueMessage($e->getMessage(), 'error');
 483          }
 484  
 485          return false;
 486      }
 487  
 488      /**
 489       * Get count of items in a category's child categories
 490       *
 491       * @param   string   $table  table name of component table (column is catid)
 492       * @param   integer  $catid  id of the category to check
 493       * @param   object   $data   The data relating to the content that was deleted.
 494       *
 495       * @return  mixed  count of items found or false if db error
 496       *
 497       * @since   1.6
 498       */
 499      private function _countItemsInChildren($table, $catid, $data)
 500      {
 501          $db = $this->db;
 502  
 503          // Create subquery for list of child categories
 504          $childCategoryTree = $data->getTree();
 505  
 506          // First element in tree is the current category, so we can skip that one
 507          unset($childCategoryTree[0]);
 508          $childCategoryIds = array();
 509  
 510          foreach ($childCategoryTree as $node) {
 511              $childCategoryIds[] = (int) $node->id;
 512          }
 513  
 514          // Make sure we only do the query if we have some categories to look in
 515          if (count($childCategoryIds)) {
 516              // Count the items in this category
 517              $query = $db->getQuery(true)
 518                  ->select('COUNT(' . $db->quoteName('id') . ')')
 519                  ->from($db->quoteName($table))
 520                  ->whereIn($db->quoteName('catid'), $childCategoryIds);
 521              $db->setQuery($query);
 522  
 523              try {
 524                  $count = $db->loadResult();
 525              } catch (RuntimeException $e) {
 526                  $this->app->enqueueMessage($e->getMessage(), 'error');
 527  
 528                  return false;
 529              }
 530  
 531              return $count;
 532          } else // If we didn't have any categories to check, return 0
 533          {
 534              return 0;
 535          }
 536      }
 537  
 538      /**
 539       * Change the state in core_content if the stage in a table is changed
 540       *
 541       * @param   string   $context  The context for the content passed to the plugin.
 542       * @param   array    $pks      A list of primary key ids of the content that has changed stage.
 543       * @param   integer  $value    The value of the condition that the content has been changed to
 544       *
 545       * @return  boolean
 546       *
 547       * @since   3.1
 548       */
 549      public function onContentChangeState($context, $pks, $value)
 550      {
 551          $pks = ArrayHelper::toInteger($pks);
 552  
 553          if ($context === 'com_workflow.stage' && $value == -2) {
 554              foreach ($pks as $pk) {
 555                  if (!$this->_canDeleteStage($pk)) {
 556                      return false;
 557                  }
 558              }
 559  
 560              return true;
 561          }
 562  
 563          $db = $this->db;
 564          $query = $db->getQuery(true)
 565              ->select($db->quoteName('core_content_id'))
 566              ->from($db->quoteName('#__ucm_content'))
 567              ->where($db->quoteName('core_type_alias') . ' = :context')
 568              ->whereIn($db->quoteName('core_content_item_id'), $pks)
 569              ->bind(':context', $context);
 570          $db->setQuery($query);
 571          $ccIds = $db->loadColumn();
 572  
 573          $cctable = new CoreContent($db);
 574          $cctable->publish($ccIds, $value);
 575  
 576          return true;
 577      }
 578  
 579      /**
 580       * The save event.
 581       *
 582       * @param   string   $context  The context
 583       * @param   object   $table    The item
 584       * @param   boolean  $isNew    Is new item
 585       * @param   array    $data     The validated data
 586       *
 587       * @return  boolean
 588       *
 589       * @since   3.9.12
 590       */
 591      private function checkMenuItemBeforeSave($context, $table, $isNew, $data)
 592      {
 593          // Check we are handling the frontend edit form.
 594          if ($context === 'com_menus.item') {
 595              return true;
 596          }
 597  
 598          // Special case for Create article menu item
 599          if ($table->link !== 'index.php?option=com_content&view=form&layout=edit') {
 600              return true;
 601          }
 602  
 603          // Display error if catid is not set when enable_category is enabled
 604          $params = json_decode($table->params, true);
 605  
 606          if ($params['enable_category'] == 1 && empty($params['catid'])) {
 607              $table->setError(Text::_('COM_CONTENT_CREATE_ARTICLE_ERROR'));
 608  
 609              return false;
 610          }
 611  
 612          return true;
 613      }
 614  }


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