[ 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 Workflow.Featuring 6 * 7 * @copyright (C) 2020 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\Event\AbstractEvent; 15 use Joomla\CMS\Event\Table\BeforeStoreEvent; 16 use Joomla\CMS\Event\View\DisplayEvent; 17 use Joomla\CMS\Event\Workflow\WorkflowFunctionalityUsedEvent; 18 use Joomla\CMS\Event\Workflow\WorkflowTransitionEvent; 19 use Joomla\CMS\Factory; 20 use Joomla\CMS\Form\Form; 21 use Joomla\CMS\Language\Text; 22 use Joomla\CMS\MVC\Model\DatabaseModelInterface; 23 use Joomla\CMS\Plugin\CMSPlugin; 24 use Joomla\CMS\Table\ContentHistory; 25 use Joomla\CMS\Table\TableInterface; 26 use Joomla\CMS\Workflow\WorkflowPluginTrait; 27 use Joomla\CMS\Workflow\WorkflowServiceInterface; 28 use Joomla\Component\Content\Administrator\Event\Model\FeatureEvent; 29 use Joomla\Event\EventInterface; 30 use Joomla\Event\SubscriberInterface; 31 use Joomla\Registry\Registry; 32 use Joomla\String\Inflector; 33 34 // phpcs:disable PSR1.Files.SideEffects 35 \defined('_JEXEC') or die; 36 // phpcs:enable PSR1.Files.SideEffects 37 38 /** 39 * Workflow Featuring Plugin 40 * 41 * @since 4.0.0 42 */ 43 class PlgWorkflowFeaturing extends CMSPlugin implements SubscriberInterface 44 { 45 use WorkflowPluginTrait; 46 47 /** 48 * Load the language file on instantiation. 49 * 50 * @var boolean 51 * @since 4.0.0 52 */ 53 protected $autoloadLanguage = true; 54 55 /** 56 * Loads the CMS Application for direct access 57 * 58 * @var CMSApplicationInterface 59 * @since 4.0.0 60 */ 61 protected $app; 62 63 /** 64 * The name of the supported functionality to check against 65 * 66 * @var string 67 * @since 4.0.0 68 */ 69 protected $supportFunctionality = 'core.featured'; 70 71 /** 72 * Returns an array of events this subscriber will listen to. 73 * 74 * @return array 75 * 76 * @since 4.0.0 77 */ 78 public static function getSubscribedEvents(): array 79 { 80 return [ 81 'onAfterDisplay' => 'onAfterDisplay', 82 'onContentBeforeChangeFeatured' => 'onContentBeforeChangeFeatured', 83 'onContentBeforeSave' => 'onContentBeforeSave', 84 'onContentPrepareForm' => 'onContentPrepareForm', 85 'onContentVersioningPrepareTable' => 'onContentVersioningPrepareTable', 86 'onTableBeforeStore' => 'onTableBeforeStore', 87 'onWorkflowAfterTransition' => 'onWorkflowAfterTransition', 88 'onWorkflowBeforeTransition' => 'onWorkflowBeforeTransition', 89 'onWorkflowFunctionalityUsed' => 'onWorkflowFunctionalityUsed', 90 ]; 91 } 92 93 /** 94 * The form event. 95 * 96 * @param EventInterface $event The event 97 * 98 * @since 4.0.0 99 */ 100 public function onContentPrepareForm(EventInterface $event) 101 { 102 $form = $event->getArgument('0'); 103 $data = $event->getArgument('1'); 104 105 $context = $form->getName(); 106 107 // Extend the transition form 108 if ($context === 'com_workflow.transition') { 109 $this->enhanceWorkflowTransitionForm($form, $data); 110 111 return; 112 } 113 114 $this->enhanceItemForm($form, $data); 115 } 116 117 /** 118 * Disable certain fields in the item form view, when we want to take over this function in the transition 119 * Check also for the workflow implementation and if the field exists 120 * 121 * @param Form $form The form 122 * @param stdClass $data The data 123 * 124 * @return boolean 125 * 126 * @since 4.0.0 127 */ 128 protected function enhanceItemForm(Form $form, $data) 129 { 130 $context = $form->getName(); 131 132 if (!$this->isSupported($context)) { 133 return true; 134 } 135 136 $parts = explode('.', $context); 137 138 $component = $this->app->bootComponent($parts[0]); 139 140 $modelName = $component->getModelName($context); 141 142 $table = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]) 143 ->getTable(); 144 145 $fieldname = $table->getColumnAlias('featured'); 146 147 $options = $form->getField($fieldname)->options; 148 149 $value = $data->$fieldname ?? $form->getValue($fieldname, null, 0); 150 151 $text = '-'; 152 153 $textclass = 'body'; 154 155 switch ($value) { 156 case 1: 157 $textclass = 'success'; 158 break; 159 160 case 0: 161 case -2: 162 $textclass = 'danger'; 163 } 164 165 if (!empty($options)) { 166 foreach ($options as $option) { 167 if ($option->value == $value) { 168 $text = $option->text; 169 170 break; 171 } 172 } 173 } 174 175 $form->setFieldAttribute($fieldname, 'type', 'spacer'); 176 177 $label = '<span class="text-' . $textclass . '">' . htmlentities($text, ENT_COMPAT, 'UTF-8') . '</span>'; 178 $form->setFieldAttribute( 179 $fieldname, 180 'label', 181 Text::sprintf('PLG_WORKFLOW_FEATURING_FEATURED', $label) 182 ); 183 184 return true; 185 } 186 187 /** 188 * Manipulate the generic list view 189 * 190 * @param DisplayEvent $event 191 * 192 * @since 4.0.0 193 */ 194 public function onAfterDisplay(DisplayEvent $event) 195 { 196 $app = Factory::getApplication(); 197 198 if (!$app->isClient('administrator')) { 199 return; 200 } 201 202 $component = $event->getArgument('extensionName'); 203 $section = $event->getArgument('section'); 204 205 // We need the single model context for checking for workflow 206 $singularsection = Inflector::singularize($section); 207 208 if (!$this->isSupported($component . '.' . $singularsection)) { 209 return true; 210 } 211 212 // List of related batch functions we need to hide 213 $states = [ 214 'featured', 215 'unfeatured', 216 ]; 217 218 $js = " 219 document.addEventListener('DOMContentLoaded', function() 220 { 221 var dropdown = document.getElementById('toolbar-status-group'); 222 223 if (!dropdown) 224 { 225 return; 226 } 227 228 " . json_encode($states) . ".forEach((action) => { 229 var button = document.getElementById('status-group-children-' + action); 230 231 if (button) 232 { 233 button.classList.add('d-none'); 234 } 235 }); 236 237 }); 238 "; 239 240 $app->getDocument()->addScriptDeclaration($js); 241 242 return true; 243 } 244 245 /** 246 * Check if we can execute the transition 247 * 248 * @param WorkflowTransitionEvent $event 249 * 250 * @return boolean 251 * 252 * @since 4.0.0 253 */ 254 public function onWorkflowBeforeTransition(WorkflowTransitionEvent $event) 255 { 256 $context = $event->getArgument('extension'); 257 $transition = $event->getArgument('transition'); 258 $pks = $event->getArgument('pks'); 259 260 if (!$this->isSupported($context) || !is_numeric($transition->options->get('featuring'))) { 261 return true; 262 } 263 264 $value = $transition->options->get('featuring'); 265 266 if (!is_numeric($value)) { 267 return true; 268 } 269 270 /** 271 * Here it becomes tricky. We would like to use the component models featured method, so we will 272 * Execute the normal "onContentBeforeChangeFeatured" plugins. But they could cancel the execution, 273 * So we have to precheck and cancel the whole transition stuff if not allowed. 274 */ 275 $this->app->set('plgWorkflowFeaturing.' . $context, $pks); 276 277 // Trigger the change state event. 278 $eventResult = $this->app->getDispatcher()->dispatch( 279 'onContentBeforeChangeFeatured', 280 AbstractEvent::create( 281 'onContentBeforeChangeFeatured', 282 [ 283 'eventClass' => 'Joomla\Component\Content\Administrator\Event\Model\FeatureEvent', 284 'subject' => $this, 285 'extension' => $context, 286 'pks' => $pks, 287 'value' => $value, 288 'abort' => false, 289 'abortReason' => '', 290 ] 291 ) 292 ); 293 294 // Release allowed pks, the job is done 295 $this->app->set('plgWorkflowFeaturing.' . $context, []); 296 297 if ($eventResult->getArgument('abort')) { 298 $event->setStopTransition(); 299 300 return false; 301 } 302 303 return true; 304 } 305 306 /** 307 * Change Feature State of an item. Used to disable feature state change 308 * 309 * @param WorkflowTransitionEvent $event 310 * 311 * @return void 312 * 313 * @since 4.0.0 314 */ 315 public function onWorkflowAfterTransition(WorkflowTransitionEvent $event): void 316 { 317 $context = $event->getArgument('extension'); 318 $extensionName = $event->getArgument('extensionName'); 319 $transition = $event->getArgument('transition'); 320 $pks = $event->getArgument('pks'); 321 322 if (!$this->isSupported($context)) { 323 return; 324 } 325 326 $component = $this->app->bootComponent($extensionName); 327 328 $value = $transition->options->get('featuring'); 329 330 if (!is_numeric($value)) { 331 return; 332 } 333 334 $options = [ 335 'ignore_request' => true, 336 // We already have triggered onContentBeforeChangeFeatured, so use our own 337 'event_before_change_featured' => 'onWorkflowBeforeChangeFeatured', 338 ]; 339 340 $modelName = $component->getModelName($context); 341 342 $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), $options); 343 344 $model->featured($pks, $value); 345 } 346 347 /** 348 * Change Feature State of an item. Used to disable Feature state change 349 * 350 * @param FeatureEvent $event 351 * 352 * @return boolean 353 * 354 * @throws Exception 355 * @since 4.0.0 356 */ 357 public function onContentBeforeChangeFeatured(FeatureEvent $event) 358 { 359 $extension = $event->getArgument('extension'); 360 $pks = $event->getArgument('pks'); 361 362 if (!$this->isSupported($extension)) { 363 return true; 364 } 365 366 // We have allowed the pks, so we're the one who triggered 367 // With onWorkflowBeforeTransition => free pass 368 if ($this->app->get('plgWorkflowFeaturing.' . $extension) === $pks) { 369 return true; 370 } 371 372 $event->setAbort('PLG_WORKFLOW_FEATURING_CHANGE_STATE_NOT_ALLOWED'); 373 } 374 375 /** 376 * The save event. 377 * 378 * @param EventInterface $event 379 * 380 * @return boolean 381 * 382 * @since 4.0.0 383 */ 384 public function onContentBeforeSave(EventInterface $event) 385 { 386 $context = $event->getArgument('0'); 387 388 /** @var TableInterface $table */ 389 $table = $event->getArgument('1'); 390 $isNew = $event->getArgument('2'); 391 $data = $event->getArgument('3'); 392 393 if (!$this->isSupported($context)) { 394 return true; 395 } 396 397 $keyName = $table->getColumnAlias('featured'); 398 399 // Check for the old value 400 $article = clone $table; 401 402 $article->load($table->id); 403 404 /** 405 * We don't allow the change of the feature state when we use the workflow 406 * As we're setting the field to disabled, no value should be there at all 407 */ 408 if (isset($data[$keyName])) { 409 $this->app->enqueueMessage(Text::_('PLG_WORKFLOW_FEATURING_CHANGE_STATE_NOT_ALLOWED'), 'error'); 410 411 return false; 412 } 413 414 return true; 415 } 416 417 /** 418 * We remove the featured field from the versioning 419 * 420 * @param EventInterface $event 421 * 422 * @return boolean 423 * 424 * @since 4.0.0 425 */ 426 public function onContentVersioningPrepareTable(EventInterface $event) 427 { 428 $subject = $event->getArgument('subject'); 429 $context = $event->getArgument('extension'); 430 431 if (!$this->isSupported($context)) { 432 return true; 433 } 434 435 $parts = explode('.', $context); 436 437 $component = $this->app->bootComponent($parts[0]); 438 439 $modelName = $component->getModelName($context); 440 441 $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); 442 443 $table = $model->getTable(); 444 445 $subject->ignoreChanges[] = $table->getColumnAlias('featured'); 446 } 447 448 /** 449 * Pre-processor for $table->store($updateNulls) 450 * 451 * @param BeforeStoreEvent $event The event to handle 452 * 453 * @return void 454 * 455 * @since 4.0.0 456 */ 457 public function onTableBeforeStore(BeforeStoreEvent $event) 458 { 459 $subject = $event->getArgument('subject'); 460 461 if (!($subject instanceof ContentHistory)) { 462 return; 463 } 464 465 $parts = explode('.', $subject->item_id); 466 467 $typeAlias = $parts[0] . (isset($parts[1]) ? '.' . $parts[1] : ''); 468 469 if (!$this->isSupported($typeAlias)) { 470 return; 471 } 472 473 $component = $this->app->bootComponent($parts[0]); 474 475 $modelName = $component->getModelName($typeAlias); 476 477 $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); 478 479 $table = $model->getTable(); 480 481 $field = $table->getColumnAlias('featured'); 482 483 $versionData = new Registry($subject->version_data); 484 485 $versionData->remove($field); 486 487 $subject->version_data = $versionData->toString(); 488 } 489 490 /** 491 * Check if the current plugin should execute workflow related activities 492 * 493 * @param string $context 494 * 495 * @return boolean 496 * 497 * @since 4.0.0 498 */ 499 protected function isSupported($context) 500 { 501 if (!$this->checkAllowedAndForbiddenlist($context) || !$this->checkExtensionSupport($context, $this->supportFunctionality)) { 502 return false; 503 } 504 505 $parts = explode('.', $context); 506 507 // We need at least the extension + view for loading the table fields 508 if (count($parts) < 2) { 509 return false; 510 } 511 512 $component = $this->app->bootComponent($parts[0]); 513 514 if ( 515 !$component instanceof WorkflowServiceInterface 516 || !$component->isWorkflowActive($context) 517 || !$component->supportFunctionality($this->supportFunctionality, $context) 518 ) { 519 return false; 520 } 521 522 $modelName = $component->getModelName($context); 523 524 $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); 525 526 if (!$model instanceof DatabaseModelInterface || !method_exists($model, 'featured')) { 527 return false; 528 } 529 530 $table = $model->getTable(); 531 532 if (!$table instanceof TableInterface || !$table->hasField('featured')) { 533 return false; 534 } 535 536 return true; 537 } 538 539 /** 540 * If plugin supports the functionality we set the used variable 541 * 542 * @param WorkflowFunctionalityUsedEvent $event 543 * 544 * @since 4.0.0 545 */ 546 public function onWorkflowFunctionalityUsed(WorkflowFunctionalityUsedEvent $event) 547 { 548 $functionality = $event->getArgument('functionality'); 549 550 if ($functionality !== 'core.featured') { 551 return; 552 } 553 554 $event->setUsed(); 555 } 556 }
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 |