[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |