[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Administrator 5 * @subpackage com_content 6 * 7 * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\Component\Content\Administrator\Model; 12 13 use Joomla\CMS\Date\Date; 14 use Joomla\CMS\Event\AbstractEvent; 15 use Joomla\CMS\Factory; 16 use Joomla\CMS\Filter\InputFilter; 17 use Joomla\CMS\Filter\OutputFilter; 18 use Joomla\CMS\Form\Form; 19 use Joomla\CMS\Form\FormFactoryInterface; 20 use Joomla\CMS\Helper\TagsHelper; 21 use Joomla\CMS\Language\Associations; 22 use Joomla\CMS\Language\LanguageHelper; 23 use Joomla\CMS\Language\Text; 24 use Joomla\CMS\MVC\Factory\MVCFactoryInterface; 25 use Joomla\CMS\MVC\Model\AdminModel; 26 use Joomla\CMS\MVC\Model\WorkflowBehaviorTrait; 27 use Joomla\CMS\MVC\Model\WorkflowModelInterface; 28 use Joomla\CMS\Plugin\PluginHelper; 29 use Joomla\CMS\String\PunycodeHelper; 30 use Joomla\CMS\Table\TableInterface; 31 use Joomla\CMS\Tag\TaggableTableInterface; 32 use Joomla\CMS\UCM\UCMType; 33 use Joomla\CMS\Versioning\VersionableModelTrait; 34 use Joomla\CMS\Workflow\Workflow; 35 use Joomla\Component\Categories\Administrator\Helper\CategoriesHelper; 36 use Joomla\Component\Fields\Administrator\Helper\FieldsHelper; 37 use Joomla\Database\ParameterType; 38 use Joomla\Registry\Registry; 39 use Joomla\Utilities\ArrayHelper; 40 41 // phpcs:disable PSR1.Files.SideEffects 42 \defined('_JEXEC') or die; 43 // phpcs:enable PSR1.Files.SideEffects 44 45 /** 46 * Item Model for an Article. 47 * 48 * @since 1.6 49 */ 50 51 class ArticleModel extends AdminModel implements WorkflowModelInterface 52 { 53 use WorkflowBehaviorTrait; 54 use VersionableModelTrait; 55 56 /** 57 * The prefix to use with controller messages. 58 * 59 * @var string 60 * @since 1.6 61 */ 62 protected $text_prefix = 'COM_CONTENT'; 63 64 /** 65 * The type alias for this content type (for example, 'com_content.article'). 66 * 67 * @var string 68 * @since 3.2 69 */ 70 public $typeAlias = 'com_content.article'; 71 72 /** 73 * The context used for the associations table 74 * 75 * @var string 76 * @since 3.4.4 77 */ 78 protected $associationsContext = 'com_content.item'; 79 80 /** 81 * The event to trigger before changing featured status one or more items. 82 * 83 * @var string 84 * @since 4.0.0 85 */ 86 protected $event_before_change_featured = null; 87 88 /** 89 * The event to trigger after changing featured status one or more items. 90 * 91 * @var string 92 * @since 4.0.0 93 */ 94 protected $event_after_change_featured = null; 95 96 /** 97 * Constructor. 98 * 99 * @param array $config An array of configuration options (name, state, dbo, table_path, ignore_request). 100 * @param MVCFactoryInterface $factory The factory. 101 * @param FormFactoryInterface $formFactory The form factory. 102 * 103 * @since 1.6 104 * @throws \Exception 105 */ 106 public function __construct($config = array(), MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null) 107 { 108 $config['events_map'] = $config['events_map'] ?? []; 109 110 $config['events_map'] = array_merge( 111 ['featured' => 'content'], 112 $config['events_map'] 113 ); 114 115 parent::__construct($config, $factory, $formFactory); 116 117 // Set the featured status change events 118 $this->event_before_change_featured = $config['event_before_change_featured'] ?? $this->event_before_change_featured; 119 $this->event_before_change_featured = $this->event_before_change_featured ?? 'onContentBeforeChangeFeatured'; 120 $this->event_after_change_featured = $config['event_after_change_featured'] ?? $this->event_after_change_featured; 121 $this->event_after_change_featured = $this->event_after_change_featured ?? 'onContentAfterChangeFeatured'; 122 123 $this->setUpWorkflow('com_content.article'); 124 } 125 126 /** 127 * Function that can be overridden to do any data cleanup after batch copying data 128 * 129 * @param TableInterface $table The table object containing the newly created item 130 * @param integer $newId The id of the new item 131 * @param integer $oldId The original item id 132 * 133 * @return void 134 * 135 * @since 3.8.12 136 */ 137 protected function cleanupPostBatchCopy(TableInterface $table, $newId, $oldId) 138 { 139 // Check if the article was featured and update the #__content_frontpage table 140 if ($table->featured == 1) { 141 $db = $this->getDatabase(); 142 $query = $db->getQuery(true) 143 ->select( 144 [ 145 $db->quoteName('featured_up'), 146 $db->quoteName('featured_down'), 147 ] 148 ) 149 ->from($db->quoteName('#__content_frontpage')) 150 ->where($db->quoteName('content_id') . ' = :oldId') 151 ->bind(':oldId', $oldId, ParameterType::INTEGER); 152 153 $featured = $db->setQuery($query)->loadObject(); 154 155 if ($featured) { 156 $query = $db->getQuery(true) 157 ->insert($db->quoteName('#__content_frontpage')) 158 ->values(':newId, 0, :featuredUp, :featuredDown') 159 ->bind(':newId', $newId, ParameterType::INTEGER) 160 ->bind(':featuredUp', $featured->featured_up, $featured->featured_up ? ParameterType::STRING : ParameterType::NULL) 161 ->bind(':featuredDown', $featured->featured_down, $featured->featured_down ? ParameterType::STRING : ParameterType::NULL); 162 163 $db->setQuery($query); 164 $db->execute(); 165 } 166 } 167 168 $this->workflowCleanupBatchMove($oldId, $newId); 169 170 $oldItem = $this->getTable(); 171 $oldItem->load($oldId); 172 $fields = FieldsHelper::getFields('com_content.article', $oldItem, true); 173 174 $fieldsData = array(); 175 176 if (!empty($fields)) { 177 $fieldsData['com_fields'] = array(); 178 179 foreach ($fields as $field) { 180 $fieldsData['com_fields'][$field->name] = $field->rawvalue; 181 } 182 } 183 184 Factory::getApplication()->triggerEvent('onContentAfterSave', array('com_content.article', &$this->table, false, $fieldsData)); 185 } 186 187 /** 188 * Batch move categories to a new category. 189 * 190 * @param integer $value The new category ID. 191 * @param array $pks An array of row IDs. 192 * @param array $contexts An array of item contexts. 193 * 194 * @return boolean True on success. 195 * 196 * @since 3.8.6 197 */ 198 protected function batchMove($value, $pks, $contexts) 199 { 200 if (empty($this->batchSet)) { 201 // Set some needed variables. 202 $this->user = Factory::getUser(); 203 $this->table = $this->getTable(); 204 $this->tableClassName = get_class($this->table); 205 $this->contentType = new UCMType(); 206 $this->type = $this->contentType->getTypeByTable($this->tableClassName); 207 } 208 209 $categoryId = (int) $value; 210 211 if (!$this->checkCategoryId($categoryId)) { 212 return false; 213 } 214 215 PluginHelper::importPlugin('system'); 216 217 // Parent exists so we proceed 218 foreach ($pks as $pk) { 219 if (!$this->user->authorise('core.edit', $contexts[$pk])) { 220 $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); 221 222 return false; 223 } 224 225 // Check that the row actually exists 226 if (!$this->table->load($pk)) { 227 if ($error = $this->table->getError()) { 228 // Fatal error 229 $this->setError($error); 230 231 return false; 232 } else { 233 // Not fatal error 234 $this->setError(Text::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); 235 continue; 236 } 237 } 238 239 $fields = FieldsHelper::getFields('com_content.article', $this->table, true); 240 241 $fieldsData = array(); 242 243 if (!empty($fields)) { 244 $fieldsData['com_fields'] = array(); 245 246 foreach ($fields as $field) { 247 $fieldsData['com_fields'][$field->name] = $field->rawvalue; 248 } 249 } 250 251 // Set the new category ID 252 $this->table->catid = $categoryId; 253 254 // We don't want to modify tags - so remove the associated tags helper 255 if ($this->table instanceof TaggableTableInterface) { 256 $this->table->clearTagsHelper(); 257 } 258 259 // Check the row. 260 if (!$this->table->check()) { 261 $this->setError($this->table->getError()); 262 263 return false; 264 } 265 266 // Store the row. 267 if (!$this->table->store()) { 268 $this->setError($this->table->getError()); 269 270 return false; 271 } 272 273 // Run event for moved article 274 Factory::getApplication()->triggerEvent('onContentAfterSave', array('com_content.article', &$this->table, false, $fieldsData)); 275 } 276 277 // Clean the cache 278 $this->cleanCache(); 279 280 return true; 281 } 282 283 /** 284 * Method to test whether a record can be deleted. 285 * 286 * @param object $record A record object. 287 * 288 * @return boolean True if allowed to delete the record. Defaults to the permission set in the component. 289 * 290 * @since 1.6 291 */ 292 protected function canDelete($record) 293 { 294 if (empty($record->id) || ($record->state != -2)) { 295 return false; 296 } 297 298 return Factory::getUser()->authorise('core.delete', 'com_content.article.' . (int) $record->id); 299 } 300 301 /** 302 * Method to test whether a record can have its state edited. 303 * 304 * @param object $record A record object. 305 * 306 * @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component. 307 * 308 * @since 1.6 309 */ 310 protected function canEditState($record) 311 { 312 $user = Factory::getUser(); 313 314 // Check for existing article. 315 if (!empty($record->id)) { 316 return $user->authorise('core.edit.state', 'com_content.article.' . (int) $record->id); 317 } 318 319 // New article, so check against the category. 320 if (!empty($record->catid)) { 321 return $user->authorise('core.edit.state', 'com_content.category.' . (int) $record->catid); 322 } 323 324 // Default to component settings if neither article nor category known. 325 return parent::canEditState($record); 326 } 327 328 /** 329 * Prepare and sanitise the table data prior to saving. 330 * 331 * @param \Joomla\CMS\Table\Table $table A Table object. 332 * 333 * @return void 334 * 335 * @since 1.6 336 */ 337 protected function prepareTable($table) 338 { 339 // Set the publish date to now 340 if ($table->state == Workflow::CONDITION_PUBLISHED && (int) $table->publish_up == 0) { 341 $table->publish_up = Factory::getDate()->toSql(); 342 } 343 344 if ($table->state == Workflow::CONDITION_PUBLISHED && intval($table->publish_down) == 0) { 345 $table->publish_down = null; 346 } 347 348 // Increment the content version number. 349 $table->version++; 350 351 // Reorder the articles within the category so the new article is first 352 if (empty($table->id)) { 353 $table->reorder('catid = ' . (int) $table->catid . ' AND state >= 0'); 354 } 355 } 356 357 /** 358 * Method to change the published state of one or more records. 359 * 360 * @param array &$pks A list of the primary keys to change. 361 * @param integer $value The value of the published state. 362 * 363 * @return boolean True on success. 364 * 365 * @since 4.0.0 366 */ 367 public function publish(&$pks, $value = 1) 368 { 369 $this->workflowBeforeStageChange(); 370 371 return parent::publish($pks, $value); 372 } 373 374 /** 375 * Method to get a single record. 376 * 377 * @param integer $pk The id of the primary key. 378 * 379 * @return mixed Object on success, false on failure. 380 */ 381 public function getItem($pk = null) 382 { 383 if ($item = parent::getItem($pk)) { 384 // Convert the params field to an array. 385 $registry = new Registry($item->attribs); 386 $item->attribs = $registry->toArray(); 387 388 // Convert the metadata field to an array. 389 $registry = new Registry($item->metadata); 390 $item->metadata = $registry->toArray(); 391 392 // Convert the images field to an array. 393 $registry = new Registry($item->images); 394 $item->images = $registry->toArray(); 395 396 // Convert the urls field to an array. 397 $registry = new Registry($item->urls); 398 $item->urls = $registry->toArray(); 399 400 $item->articletext = ($item->fulltext !== null && trim($item->fulltext) != '') ? $item->introtext . '<hr id="system-readmore">' . $item->fulltext : $item->introtext; 401 402 if (!empty($item->id)) { 403 $item->tags = new TagsHelper(); 404 $item->tags->getTagIds($item->id, 'com_content.article'); 405 406 $item->featured_up = null; 407 $item->featured_down = null; 408 409 if ($item->featured) { 410 // Get featured dates. 411 $db = $this->getDatabase(); 412 $query = $db->getQuery(true) 413 ->select( 414 [ 415 $db->quoteName('featured_up'), 416 $db->quoteName('featured_down'), 417 ] 418 ) 419 ->from($db->quoteName('#__content_frontpage')) 420 ->where($db->quoteName('content_id') . ' = :id') 421 ->bind(':id', $item->id, ParameterType::INTEGER); 422 423 $featured = $db->setQuery($query)->loadObject(); 424 425 if ($featured) { 426 $item->featured_up = $featured->featured_up; 427 $item->featured_down = $featured->featured_down; 428 } 429 } 430 } 431 } 432 433 // Load associated content items 434 $assoc = Associations::isEnabled(); 435 436 if ($assoc) { 437 $item->associations = array(); 438 439 if ($item->id != null) { 440 $associations = Associations::getAssociations('com_content', '#__content', 'com_content.item', $item->id); 441 442 foreach ($associations as $tag => $association) { 443 $item->associations[$tag] = $association->id; 444 } 445 } 446 } 447 448 return $item; 449 } 450 451 /** 452 * Method to get the record form. 453 * 454 * @param array $data Data for the form. 455 * @param boolean $loadData True if the form is to load its own data (default case), false if not. 456 * 457 * @return Form|boolean A Form object on success, false on failure 458 * 459 * @since 1.6 460 */ 461 public function getForm($data = array(), $loadData = true) 462 { 463 $app = Factory::getApplication(); 464 465 // Get the form. 466 $form = $this->loadForm('com_content.article', 'article', array('control' => 'jform', 'load_data' => $loadData)); 467 468 if (empty($form)) { 469 return false; 470 } 471 472 // Object uses for checking edit state permission of article 473 $record = new \stdClass(); 474 475 // Get ID of the article from input, for frontend, we use a_id while backend uses id 476 $articleIdFromInput = $app->isClient('site') 477 ? $app->input->getInt('a_id', 0) 478 : $app->input->getInt('id', 0); 479 480 // On edit article, we get ID of article from article.id state, but on save, we use data from input 481 $id = (int) $this->getState('article.id', $articleIdFromInput); 482 483 $record->id = $id; 484 485 // For new articles we load the potential state + associations 486 if ($id == 0 && $formField = $form->getField('catid')) { 487 $assignedCatids = $data['catid'] ?? $form->getValue('catid'); 488 489 $assignedCatids = is_array($assignedCatids) 490 ? (int) reset($assignedCatids) 491 : (int) $assignedCatids; 492 493 // Try to get the category from the category field 494 if (empty($assignedCatids)) { 495 $assignedCatids = $formField->getAttribute('default', null); 496 497 if (!$assignedCatids) { 498 // Choose the first category available 499 $catOptions = $formField->options; 500 501 if ($catOptions && !empty($catOptions[0]->value)) { 502 $assignedCatids = (int) $catOptions[0]->value; 503 } 504 } 505 } 506 507 // Activate the reload of the form when category is changed 508 $form->setFieldAttribute('catid', 'refresh-enabled', true); 509 $form->setFieldAttribute('catid', 'refresh-cat-id', $assignedCatids); 510 $form->setFieldAttribute('catid', 'refresh-section', 'article'); 511 512 // Store ID of the category uses for edit state permission check 513 $record->catid = $assignedCatids; 514 } else { 515 // Get the category which the article is being added to 516 if (!empty($data['catid'])) { 517 $catId = (int) $data['catid']; 518 } else { 519 $catIds = $form->getValue('catid'); 520 521 $catId = is_array($catIds) 522 ? (int) reset($catIds) 523 : (int) $catIds; 524 525 if (!$catId) { 526 $catId = (int) $form->getFieldAttribute('catid', 'default', 0); 527 } 528 } 529 530 $record->catid = $catId; 531 } 532 533 // Modify the form based on Edit State access controls. 534 if (!$this->canEditState($record)) { 535 // Disable fields for display. 536 $form->setFieldAttribute('featured', 'disabled', 'true'); 537 $form->setFieldAttribute('featured_up', 'disabled', 'true'); 538 $form->setFieldAttribute('featured_down', 'disabled', 'true'); 539 $form->setFieldAttribute('ordering', 'disabled', 'true'); 540 $form->setFieldAttribute('publish_up', 'disabled', 'true'); 541 $form->setFieldAttribute('publish_down', 'disabled', 'true'); 542 $form->setFieldAttribute('state', 'disabled', 'true'); 543 544 // Disable fields while saving. 545 // The controller has already verified this is an article you can edit. 546 $form->setFieldAttribute('featured', 'filter', 'unset'); 547 $form->setFieldAttribute('featured_up', 'filter', 'unset'); 548 $form->setFieldAttribute('featured_down', 'filter', 'unset'); 549 $form->setFieldAttribute('ordering', 'filter', 'unset'); 550 $form->setFieldAttribute('publish_up', 'filter', 'unset'); 551 $form->setFieldAttribute('publish_down', 'filter', 'unset'); 552 $form->setFieldAttribute('state', 'filter', 'unset'); 553 } 554 555 // Don't allow to change the created_by user if not allowed to access com_users. 556 if (!Factory::getUser()->authorise('core.manage', 'com_users')) { 557 $form->setFieldAttribute('created_by', 'filter', 'unset'); 558 } 559 560 return $form; 561 } 562 563 /** 564 * Method to get the data that should be injected in the form. 565 * 566 * @return mixed The data for the form. 567 * 568 * @since 1.6 569 */ 570 protected function loadFormData() 571 { 572 // Check the session for previously entered form data. 573 $app = Factory::getApplication(); 574 $data = $app->getUserState('com_content.edit.article.data', array()); 575 576 if (empty($data)) { 577 $data = $this->getItem(); 578 579 // Pre-select some filters (Status, Category, Language, Access) in edit form if those have been selected in Article Manager: Articles 580 if ($this->getState('article.id') == 0) { 581 $filters = (array) $app->getUserState('com_content.articles.filter'); 582 $data->set( 583 'state', 584 $app->input->getInt( 585 'state', 586 ((isset($filters['published']) && $filters['published'] !== '') ? $filters['published'] : null) 587 ) 588 ); 589 $data->set('catid', $app->input->getInt('catid', (!empty($filters['category_id']) ? $filters['category_id'] : null))); 590 591 if ($app->isClient('administrator')) { 592 $data->set('language', $app->input->getString('language', (!empty($filters['language']) ? $filters['language'] : null))); 593 } 594 595 $data->set( 596 'access', 597 $app->input->getInt('access', (!empty($filters['access']) ? $filters['access'] : $app->get('access'))) 598 ); 599 } 600 } 601 602 // If there are params fieldsets in the form it will fail with a registry object 603 if (isset($data->params) && $data->params instanceof Registry) { 604 $data->params = $data->params->toArray(); 605 } 606 607 $this->preprocessData('com_content.article', $data); 608 609 return $data; 610 } 611 612 /** 613 * Method to validate the form data. 614 * 615 * @param Form $form The form to validate against. 616 * @param array $data The data to validate. 617 * @param string $group The name of the field group to validate. 618 * 619 * @return array|boolean Array of filtered data if valid, false otherwise. 620 * 621 * @see \Joomla\CMS\Form\FormRule 622 * @see JFilterInput 623 * @since 3.7.0 624 */ 625 public function validate($form, $data, $group = null) 626 { 627 if (!Factory::getUser()->authorise('core.admin', 'com_content')) { 628 if (isset($data['rules'])) { 629 unset($data['rules']); 630 } 631 } 632 633 return parent::validate($form, $data, $group); 634 } 635 636 /** 637 * Method to save the form data. 638 * 639 * @param array $data The form data. 640 * 641 * @return boolean True on success. 642 * 643 * @since 1.6 644 */ 645 public function save($data) 646 { 647 $app = Factory::getApplication(); 648 $input = $app->input; 649 $filter = InputFilter::getInstance(); 650 651 if (isset($data['metadata']) && isset($data['metadata']['author'])) { 652 $data['metadata']['author'] = $filter->clean($data['metadata']['author'], 'TRIM'); 653 } 654 655 if (isset($data['created_by_alias'])) { 656 $data['created_by_alias'] = $filter->clean($data['created_by_alias'], 'TRIM'); 657 } 658 659 if (isset($data['images']) && is_array($data['images'])) { 660 $registry = new Registry($data['images']); 661 662 $data['images'] = (string) $registry; 663 } 664 665 $this->workflowBeforeSave(); 666 667 // Create new category, if needed. 668 $createCategory = true; 669 670 if (is_null($data['catid'])) { 671 // When there is no catid passed don't try to create one 672 $createCategory = false; 673 } 674 675 // If category ID is provided, check if it's valid. 676 if (is_numeric($data['catid']) && $data['catid']) { 677 $createCategory = !CategoriesHelper::validateCategoryId($data['catid'], 'com_content'); 678 } 679 680 // Save New Category 681 if ($createCategory && $this->canCreateCategory()) { 682 $category = [ 683 // Remove #new# prefix, if exists. 684 'title' => strpos($data['catid'], '#new#') === 0 ? substr($data['catid'], 5) : $data['catid'], 685 'parent_id' => 1, 686 'extension' => 'com_content', 687 'language' => $data['language'], 688 'published' => 1, 689 ]; 690 691 /** @var \Joomla\Component\Categories\Administrator\Model\CategoryModel $categoryModel */ 692 $categoryModel = Factory::getApplication()->bootComponent('com_categories') 693 ->getMVCFactory()->createModel('Category', 'Administrator', ['ignore_request' => true]); 694 695 // Create new category. 696 if (!$categoryModel->save($category)) { 697 $this->setError($categoryModel->getError()); 698 699 return false; 700 } 701 702 // Get the Category ID. 703 $data['catid'] = $categoryModel->getState('category.id'); 704 } 705 706 if (isset($data['urls']) && is_array($data['urls'])) { 707 $check = $input->post->get('jform', array(), 'array'); 708 709 foreach ($data['urls'] as $i => $url) { 710 if ($url != false && ($i == 'urla' || $i == 'urlb' || $i == 'urlc')) { 711 if (preg_match('~^#[a-zA-Z]{1}[a-zA-Z0-9-_:.]*$~', $check['urls'][$i]) == 1) { 712 $data['urls'][$i] = $check['urls'][$i]; 713 } else { 714 $data['urls'][$i] = PunycodeHelper::urlToPunycode($url); 715 } 716 } 717 } 718 719 unset($check); 720 721 $registry = new Registry($data['urls']); 722 723 $data['urls'] = (string) $registry; 724 } 725 726 // Alter the title for save as copy 727 if ($input->get('task') == 'save2copy') { 728 $origTable = $this->getTable(); 729 730 if ($app->isClient('site')) { 731 $origTable->load($input->getInt('a_id')); 732 733 if ($origTable->title === $data['title']) { 734 /** 735 * If title of article is not changed, set alias to original article alias so that Joomla! will generate 736 * new Title and Alias for the copied article 737 */ 738 $data['alias'] = $origTable->alias; 739 } else { 740 $data['alias'] = ''; 741 } 742 } else { 743 $origTable->load($input->getInt('id')); 744 } 745 746 if ($data['title'] == $origTable->title) { 747 list($title, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['title']); 748 $data['title'] = $title; 749 $data['alias'] = $alias; 750 } elseif ($data['alias'] == $origTable->alias) { 751 $data['alias'] = ''; 752 } 753 } 754 755 // Automatic handling of alias for empty fields 756 if (in_array($input->get('task'), array('apply', 'save', 'save2new')) && (!isset($data['id']) || (int) $data['id'] == 0)) { 757 if ($data['alias'] == null) { 758 if ($app->get('unicodeslugs') == 1) { 759 $data['alias'] = OutputFilter::stringUrlUnicodeSlug($data['title']); 760 } else { 761 $data['alias'] = OutputFilter::stringURLSafe($data['title']); 762 } 763 764 $table = $this->getTable(); 765 766 if ($table->load(array('alias' => $data['alias'], 'catid' => $data['catid']))) { 767 $msg = Text::_('COM_CONTENT_SAVE_WARNING'); 768 } 769 770 list($title, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['title']); 771 $data['alias'] = $alias; 772 773 if (isset($msg)) { 774 $app->enqueueMessage($msg, 'warning'); 775 } 776 } 777 } 778 779 if (parent::save($data)) { 780 // Check if featured is set and if not managed by workflow 781 if (isset($data['featured']) && !$this->bootComponent('com_content')->isFunctionalityUsed('core.featured', 'com_content.article')) { 782 if ( 783 !$this->featured( 784 $this->getState($this->getName() . '.id'), 785 $data['featured'], 786 $data['featured_up'] ?? null, 787 $data['featured_down'] ?? null 788 ) 789 ) { 790 return false; 791 } 792 } 793 794 $this->workflowAfterSave($data); 795 796 return true; 797 } 798 799 return false; 800 } 801 802 /** 803 * Method to toggle the featured setting of articles. 804 * 805 * @param array $pks The ids of the items to toggle. 806 * @param integer $value The value to toggle to. 807 * @param string|Date $featuredUp The date which item featured up. 808 * @param string|Date $featuredDown The date which item featured down. 809 * 810 * @return boolean True on success. 811 */ 812 public function featured($pks, $value = 0, $featuredUp = null, $featuredDown = null) 813 { 814 // Sanitize the ids. 815 $pks = (array) $pks; 816 $pks = ArrayHelper::toInteger($pks); 817 $value = (int) $value; 818 $context = $this->option . '.' . $this->name; 819 820 $this->workflowBeforeStageChange(); 821 822 // Include the plugins for the change of state event. 823 PluginHelper::importPlugin($this->events_map['featured']); 824 825 // Convert empty strings to null for the query. 826 if ($featuredUp === '') { 827 $featuredUp = null; 828 } 829 830 if ($featuredDown === '') { 831 $featuredDown = null; 832 } 833 834 if (empty($pks)) { 835 $this->setError(Text::_('COM_CONTENT_NO_ITEM_SELECTED')); 836 837 return false; 838 } 839 840 $table = $this->getTable('Featured', 'Administrator'); 841 842 // Trigger the before change state event. 843 $eventResult = Factory::getApplication()->getDispatcher()->dispatch( 844 $this->event_before_change_featured, 845 AbstractEvent::create( 846 $this->event_before_change_featured, 847 [ 848 'eventClass' => 'Joomla\Component\Content\Administrator\Event\Model\FeatureEvent', 849 'subject' => $this, 850 'extension' => $context, 851 'pks' => $pks, 852 'value' => $value, 853 ] 854 ) 855 ); 856 857 if ($eventResult->getArgument('abort', false)) { 858 $this->setError(Text::_($eventResult->getArgument('abortReason'))); 859 860 return false; 861 } 862 863 try { 864 $db = $this->getDatabase(); 865 $query = $db->getQuery(true) 866 ->update($db->quoteName('#__content')) 867 ->set($db->quoteName('featured') . ' = :featured') 868 ->whereIn($db->quoteName('id'), $pks) 869 ->bind(':featured', $value, ParameterType::INTEGER); 870 $db->setQuery($query); 871 $db->execute(); 872 873 if ($value === 0) { 874 // Adjust the mapping table. 875 // Clear the existing features settings. 876 $query = $db->getQuery(true) 877 ->delete($db->quoteName('#__content_frontpage')) 878 ->whereIn($db->quoteName('content_id'), $pks); 879 $db->setQuery($query); 880 $db->execute(); 881 } else { 882 // First, we find out which of our new featured articles are already featured. 883 $query = $db->getQuery(true) 884 ->select($db->quoteName('content_id')) 885 ->from($db->quoteName('#__content_frontpage')) 886 ->whereIn($db->quoteName('content_id'), $pks); 887 $db->setQuery($query); 888 889 $oldFeatured = $db->loadColumn(); 890 891 // Update old featured articles 892 if (count($oldFeatured)) { 893 $query = $db->getQuery(true) 894 ->update($db->quoteName('#__content_frontpage')) 895 ->set( 896 [ 897 $db->quoteName('featured_up') . ' = :featuredUp', 898 $db->quoteName('featured_down') . ' = :featuredDown', 899 ] 900 ) 901 ->whereIn($db->quoteName('content_id'), $oldFeatured) 902 ->bind(':featuredUp', $featuredUp, $featuredUp ? ParameterType::STRING : ParameterType::NULL) 903 ->bind(':featuredDown', $featuredDown, $featuredDown ? ParameterType::STRING : ParameterType::NULL); 904 $db->setQuery($query); 905 $db->execute(); 906 } 907 908 // We diff the arrays to get a list of the articles that are newly featured 909 $newFeatured = array_diff($pks, $oldFeatured); 910 911 // Featuring. 912 if ($newFeatured) { 913 $query = $db->getQuery(true) 914 ->insert($db->quoteName('#__content_frontpage')) 915 ->columns( 916 [ 917 $db->quoteName('content_id'), 918 $db->quoteName('ordering'), 919 $db->quoteName('featured_up'), 920 $db->quoteName('featured_down'), 921 ] 922 ); 923 924 $dataTypes = [ 925 ParameterType::INTEGER, 926 ParameterType::INTEGER, 927 $featuredUp ? ParameterType::STRING : ParameterType::NULL, 928 $featuredDown ? ParameterType::STRING : ParameterType::NULL, 929 ]; 930 931 foreach ($newFeatured as $pk) { 932 $query->values(implode(',', $query->bindArray([$pk, 0, $featuredUp, $featuredDown], $dataTypes))); 933 } 934 935 $db->setQuery($query); 936 $db->execute(); 937 } 938 } 939 } catch (\Exception $e) { 940 $this->setError($e->getMessage()); 941 942 return false; 943 } 944 945 $table->reorder(); 946 947 // Trigger the change state event. 948 Factory::getApplication()->getDispatcher()->dispatch( 949 $this->event_after_change_featured, 950 AbstractEvent::create( 951 $this->event_after_change_featured, 952 [ 953 'eventClass' => 'Joomla\Component\Content\Administrator\Event\Model\FeatureEvent', 954 'subject' => $this, 955 'extension' => $context, 956 'pks' => $pks, 957 'value' => $value, 958 ] 959 ) 960 ); 961 962 $this->cleanCache(); 963 964 return true; 965 } 966 967 /** 968 * A protected method to get a set of ordering conditions. 969 * 970 * @param object $table A record object. 971 * 972 * @return array An array of conditions to add to ordering queries. 973 * 974 * @since 1.6 975 */ 976 protected function getReorderConditions($table) 977 { 978 return [ 979 $this->getDatabase()->quoteName('catid') . ' = ' . (int) $table->catid, 980 ]; 981 } 982 983 /** 984 * Allows preprocessing of the Form object. 985 * 986 * @param Form $form The form object 987 * @param array $data The data to be merged into the form object 988 * @param string $group The plugin group to be executed 989 * 990 * @return void 991 * 992 * @since 3.0 993 */ 994 protected function preprocessForm(Form $form, $data, $group = 'content') 995 { 996 if ($this->canCreateCategory()) { 997 $form->setFieldAttribute('catid', 'allowAdd', 'true'); 998 999 // Add a prefix for categories created on the fly. 1000 $form->setFieldAttribute('catid', 'customPrefix', '#new#'); 1001 } 1002 1003 // Association content items 1004 if (Associations::isEnabled()) { 1005 $languages = LanguageHelper::getContentLanguages(false, false, null, 'ordering', 'asc'); 1006 1007 if (count($languages) > 1) { 1008 $addform = new \SimpleXMLElement('<form />'); 1009 $fields = $addform->addChild('fields'); 1010 $fields->addAttribute('name', 'associations'); 1011 $fieldset = $fields->addChild('fieldset'); 1012 $fieldset->addAttribute('name', 'item_associations'); 1013 1014 foreach ($languages as $language) { 1015 $field = $fieldset->addChild('field'); 1016 $field->addAttribute('name', $language->lang_code); 1017 $field->addAttribute('type', 'modal_article'); 1018 $field->addAttribute('language', $language->lang_code); 1019 $field->addAttribute('label', $language->title); 1020 $field->addAttribute('translate_label', 'false'); 1021 $field->addAttribute('select', 'true'); 1022 $field->addAttribute('new', 'true'); 1023 $field->addAttribute('edit', 'true'); 1024 $field->addAttribute('clear', 'true'); 1025 $field->addAttribute('propagate', 'true'); 1026 } 1027 1028 $form->load($addform, false); 1029 } 1030 } 1031 1032 $this->workflowPreprocessForm($form, $data); 1033 1034 parent::preprocessForm($form, $data, $group); 1035 } 1036 1037 /** 1038 * Custom clean the cache of com_content and content modules 1039 * 1040 * @param string $group The cache group 1041 * @param integer $clientId @deprecated 5.0 No longer used. 1042 * 1043 * @return void 1044 * 1045 * @since 1.6 1046 */ 1047 protected function cleanCache($group = null, $clientId = 0) 1048 { 1049 parent::cleanCache('com_content'); 1050 parent::cleanCache('mod_articles_archive'); 1051 parent::cleanCache('mod_articles_categories'); 1052 parent::cleanCache('mod_articles_category'); 1053 parent::cleanCache('mod_articles_latest'); 1054 parent::cleanCache('mod_articles_news'); 1055 parent::cleanCache('mod_articles_popular'); 1056 } 1057 1058 /** 1059 * Void hit function for pagebreak when editing content from frontend 1060 * 1061 * @return void 1062 * 1063 * @since 3.6.0 1064 */ 1065 public function hit() 1066 { 1067 } 1068 1069 /** 1070 * Is the user allowed to create an on the fly category? 1071 * 1072 * @return boolean 1073 * 1074 * @since 3.6.1 1075 */ 1076 private function canCreateCategory() 1077 { 1078 return Factory::getUser()->authorise('core.create', 'com_content'); 1079 } 1080 1081 /** 1082 * Delete #__content_frontpage items if the deleted articles was featured 1083 * 1084 * @param object $pks The primary key related to the contents that was deleted. 1085 * 1086 * @return boolean 1087 * 1088 * @since 3.7.0 1089 */ 1090 public function delete(&$pks) 1091 { 1092 $return = parent::delete($pks); 1093 1094 if ($return) { 1095 // Now check to see if this articles was featured if so delete it from the #__content_frontpage table 1096 $db = $this->getDatabase(); 1097 $query = $db->getQuery(true) 1098 ->delete($db->quoteName('#__content_frontpage')) 1099 ->whereIn($db->quoteName('content_id'), $pks); 1100 $db->setQuery($query); 1101 $db->execute(); 1102 1103 $this->workflow->deleteAssociation($pks); 1104 } 1105 1106 return $return; 1107 } 1108 }
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 |