diff --git a/administrator/language/en-GB/plg_workflow_images.ini b/administrator/language/en-GB/plg_workflow_images.ini new file mode 100644 index 0000000000000..8203149db29c3 --- /dev/null +++ b/administrator/language/en-GB/plg_workflow_images.ini @@ -0,0 +1,21 @@ +; Joomla! Project +; Copyright (C) 2005 - 2020 Open Source Matters. All rights reserved. +; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php +; Note : All ini files need to be saved as UTF-8 + +PLG_WORKFLOW_IMAGES="Workflow - Images" +PLG_WORKFLOW_IMAGES_FULL_ARTICLE_IMAGE_HEIGHT_LABEL="Full Article Image Height" +PLG_WORKFLOW_IMAGES_FULL_ARTICLE_IMAGE_INVALID_EXTENSION="Invalid File Extension of Full Article Image" +PLG_WORKFLOW_IMAGES_FULL_ARTICLE_IMAGE_INVALID_TYPE="Full Article Image is not of type image" +PLG_WORKFLOW_IMAGES_FULL_ARTICLE_IMAGE_REQUIRED="Full Article Image required to execute this transition" +PLG_WORKFLOW_IMAGES_FULL_ARTICLE_IMAGE_SETTING_LABEL="Full Article Image required" +PLG_WORKFLOW_IMAGES_FULL_ARTICLE_IMAGE_WIDTH_LABEL="Full Article Image Width" +PLG_WORKFLOW_IMAGES_IMAGE_SETTING_LABEL="Image Settings" +PLG_WORKFLOW_IMAGES_INTRO_IMAGE_HEIGHT_LABEL="Intro Image Height" +PLG_WORKFLOW_IMAGES_INTRO_IMAGE_INVALID_EXTENSION="Invalid File Extension of Intro Image" +PLG_WORKFLOW_IMAGES_INTRO_IMAGE_INVALID_TYPE="Intro Image is not of type image" +PLG_WORKFLOW_IMAGES_INTRO_IMAGE_REQUIRED="Intro Image required to execute this transition" +PLG_WORKFLOW_IMAGES_INTRO_IMAGE_SETTING_LABEL="Intro Image required" +PLG_WORKFLOW_IMAGES_INTRO_IMAGE_WIDTH_LABEL="Intro Image Width" +PLG_WORKFLOW_IMAGES_RESIZE_SETTING_LABEL="Resize Settings" + diff --git a/administrator/language/en-GB/plg_workflow_images.sys.ini b/administrator/language/en-GB/plg_workflow_images.sys.ini new file mode 100644 index 0000000000000..fd3a2dc880182 --- /dev/null +++ b/administrator/language/en-GB/plg_workflow_images.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; Copyright (C) 2005 - 2020 Open Source Matters. All rights reserved. +; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php +; Note : All ini files need to be saved as UTF-8 + +PLG_WORKFLOW_IMAGES="Workflow - Images" +PLG_WORKFLOW_IMAGES_XML_DESCRIPTION="Add image options to the workflow transitions for your items" diff --git a/plugins/workflow/images/forms/action.xml b/plugins/workflow/images/forms/action.xml new file mode 100644 index 0000000000000..5552ffc4ff276 --- /dev/null +++ b/plugins/workflow/images/forms/action.xml @@ -0,0 +1,119 @@ + +
diff --git a/plugins/workflow/images/images.php b/plugins/workflow/images/images.php new file mode 100644 index 0000000000000..15771f724d14c --- /dev/null +++ b/plugins/workflow/images/images.php @@ -0,0 +1,423 @@ + 'onContentPrepareForm', + 'onWorkflowBeforeTransition' => 'onWorkflowBeforeTransition', + 'onWorkflowAfterTransition' => 'onWorkflowAfterTransition', + 'onContentBeforeSave' => 'onContentBeforeSave', + 'onWorkflowFunctionalityUsed' => 'onWorkflowFunctionalityUsed', + ]; + } + + /** + * The form event. + * + * @param EventInterface $event The event + * + * @since 4.0.0 + */ + public function onContentPrepareForm(EventInterface $event) + { + $form = $event->getArgument('0'); + $data = $event->getArgument('1'); + + $context = $form->getName(); + + // Extend the transition form + if ($context === 'com_workflow.transition') { + $this->enhanceWorkflowTransitionForm($form, $data); + + return; + } + + return; + } + + + /** + * Check if we can execute the transition + * + * @param WorkflowTransitionEvent $event + * + * @return boolean + * + * @throws Exception + * @throws Exception + * @throws Exception + * @throws Exception + * @throws Exception + * @throws Exception + * @since 4.0.0 + */ + public function onWorkflowBeforeTransition(WorkflowTransitionEvent $event) + { + $context = $event->getArgument('extension'); + $extensionName = $event->getArgument('extensionName'); + $transition = $event->getArgument('transition'); + $pks = $event->getArgument('pks'); + + //get Values from Form + $introImageRequired = $transition->options->get('images_intro_image_settings'); + $fullArticleImageRequired = $transition->options->get('images_full_article_image_settings'); + //$allowedExtensions = str_replace(',','|',$transition->options->get('fileExtensions')); + + + if ( + !$this->isSupported($context) + || !is_numeric($introImageRequired) + || !is_numeric($fullArticleImageRequired) + ) { + return true; + } + + $component = $this->app->bootComponent($extensionName); + + $options = [ + 'ignore_request' => true, + // We already have triggered onContentBeforeChangeState, so use our own + 'event_before_change_state' => 'onWorkflowBeforeChangeState' + ]; + + // Get model + + $modelName = $component->getModelName($context); + + $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), $options); + + + + foreach ($pks as $pk) { + $introImage = $model->getItem($pk)->images['image_intro']; + $introImagePath = JPATH_ROOT . "/" . $introImage; + + $fullArticleImage = $model->getItem($pk)->images["image_fulltext"]; + $fullArticleImagePath = JPATH_ROOT . "/" . $fullArticleImage; + + if ($introImageRequired) { + if (!$introImage || !file_exists($introImagePath)) { + Factory::getApplication()->enqueueMessage(Text::_('PLG_WORKFLOW_IMAGES_INTRO_IMAGE_REQUIRED')); + $event->setStopTransition(); + + return false; + } + } + + if ($fullArticleImageRequired) { + if (!$fullArticleImage || !file_exists($fullArticleImagePath)) { + Factory::getApplication()->enqueueMessage(Text::_('PLG_WORKFLOW_IMAGES_FULL_ARTICLE_IMAGE_REQUIRED')); + $event->setStopTransition(); + + return false; + } + } + + $mimetypes = array('image/jpeg', 'image/png', 'image/gif'); + + if (!in_array(MediaHelper::getMimeType($introImagePath, true), $mimetypes)) { + Factory::getApplication()->enqueueMessage(Text::_('PLG_WORKFLOW_IMAGES_INTRO_IMAGE_INVALID_TYPE')); + $event->setStopTransition(); + return false; + } + /* + if(!preg_match("/\.(?:$allowedExtensions)$/i", $introImagePath)){ + Factory::getApplication()->enqueueMessage(Text::_('PLG_WORKFLOW_IMAGES_INTRO_IMAGE_INVALID_EXTENSION')); + $event->setStopTransition(); + return false; + } + */ + if (!in_array(MediaHelper::getMimeType($fullArticleImagePath, true), $mimetypes)) { + Factory::getApplication()->enqueueMessage(Text::_('PLG_WORKFLOW_IMAGES_FULL_ARTICLE_IMAGE_INVALID_TYPE')); + $event->setStopTransition(); + + return false; + } + /* + if(!preg_match("/\.(?:$allowedExtensions)$/i", $fullArticleImagePath)){ + Factory::getApplication()->enqueueMessage(Text::_('PLG_WORKFLOW_IMAGES_INTRO_IMAGE_INVALID_EXTENSION')); + $event->setStopTransition(); + return false; + } + */ + } + + if ( + !$this->isSupported($context) || + !is_numeric($introImageRequired) || + !is_numeric($fullArticleImageRequired) + ) { + return true; + } + return true; + } + + /** + * Change State of an item. Used to disable state change + * + * @param WorkflowTransitionEvent $event + * + * @return boolean + * + * @since 4.0.0 + */ + public function onWorkflowAfterTransition(WorkflowTransitionEvent $event) + { + $context = $event->getArgument('extension'); + $extensionName = $event->getArgument('extensionName'); + $transition = $event->getArgument('transition'); + $pks = $event->getArgument('pks'); + + $introImageRequired = $transition->options->get('images_intro_image_settings'); + $fullArticleImageRequired = $transition->options->get('images_full_article_image_settings'); + + $resizeFullArticleImage = $transition->options->get('resizeFullArticleImage'); + $resizeIntroImage = $transition->options->get('resizeIntroImage'); + + if (!$this->isSupported($context)) { + return true; + } + + $component = $this->app->bootComponent($extensionName); + + $fullArticleWidth = $transition->options->get('fullArticleImageWidth'); + $fullArticleHeight = $transition->options->get('fullArticleImageHeight'); + + $introWidth = $transition->options->get('introImageWidth'); + $introHeight = $transition->options->get('introImageHeight'); + + $options = [ + 'ignore_request' => true, + // We already have triggered onContentBeforeChangeState, so use our own + 'event_before_change_state' => 'onWorkflowBeforeChangeState' + ]; + + $modelName = $component->getModelName($context); + + $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), $options); + + foreach ($pks as $pk) { + $introImagePath = JPATH_ROOT . "/" . $model->getItem($pk)->images['image_intro']; + $fullArticleImagePath = JPATH_ROOT . "/" . $model->getItem($pk)->images["image_fulltext"]; + + if ($introImageRequired && $resizeIntroImage) { + //if(!preg_match("/\.(?:'jpg|png|gif')$/i", $introImagePath) || !getimagesize($introImagePath)){ + if (!getimagesize($introImagePath)) { + // Image can't get resized + return true; + } elseif (($model->getItem($pk)->images['image_intro'])) { + $image = new Image(); + $image->loadFile($introImagePath); + if (!$introWidth) { + $introWidth = getimagesize($introImagePath)[0]; + } + if (!$introHeight) { + $introHeight = getimagesize($introImagePath)[1]; + } + $newImage = $image->cropResize($introWidth, $introHeight, true); + $newImage->toFile(JPATH_ROOT . "/images/" . "intro_image_resized.jpeg", IMAGETYPE_JPEG); + } + } + + if ($fullArticleImageRequired && $resizeFullArticleImage) { + //if(!preg_match("/\.(?:'jpg|png|gif')$/i", $fullArticleImagePath) || !getimagesize($fullArticleImagePath)){ + if (!getimagesize($fullArticleImagePath)) { + return true; + } elseif (($model->getItem($pk)->images["image_fulltext"])) { + $image = new Image(); + $image->loadFile($fullArticleImagePath); + if (!$fullArticleWidth) { + $fullArticleWidth = getimagesize($fullArticleImagePath)[0]; + } + if (!$fullArticleHeight) { + $fullArticleHeight = getimagesize($fullArticleImagePath)[1]; + } + $newImage = $image->cropResize($fullArticleWidth, $fullArticleHeight, true); + $newImage->toFile(JPATH_ROOT . "/images/" . "full_article_image_resized.jpeg", IMAGETYPE_JPEG); + } + } + } + + return true; + } + + /** + * Change State of an item. Used to disable state change + * + * @param EventInterface $event + * + * @return boolean + * + * @throws Exception + * @since 4.0.0 + */ + public function onContentBeforeChangeState(EventInterface $event) + { + $context = $event->getArgument('0'); + $pks = $event->getArgument('1'); + + if (!$this->isSupported($context)) { + return true; + } + + // We have whitelisted the pks, so we're the one who triggered + // With onWorkflowBeforeTransition => free pass + if ($this->app->get('plgWorkflowPublishing.' . $context) === $pks) { + return true; + } + + throw new Exception(Text::_('PLG_WORKFLOW_PUBLISHING_CHANGE_STATE_NOT_ALLOWED')); + } + + /** + * The save event. + * + * @param EventInterface $event + * + * @return boolean + * + * @since 4.0.0 + */ + public function onContentBeforeSave(EventInterface $event) + { + $context = $event->getArgument('0'); + + if (!$this->isSupported($context)) { + return true; + } + + return true; + } + + /** + * Check if the current plugin should execute workflow related activities + * + * @param string $context + * + * @return boolean + * + * @since 4.0.0 + */ + protected function isSupported($context) + { + if (!$this->checkWhiteAndBlacklist($context) || !$this->checkExtensionSupport($context, $this->supportFunctionality)) { + return false; + } + + $parts = explode('.', $context); + + // We need at least the extension + view for loading the table fields + if (count($parts) < 2) { + return false; + } + + $component = $this->app->bootComponent($parts[0]); + + if ( + !$component instanceof WorkflowServiceInterface + || !$component->isWorkflowActive($context) + || !$component->supportFunctionality($this->supportFunctionality, $context) + ) { + return false; + } + + $modelName = $component->getModelName($context); + + $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); + + if (!$model instanceof DatabaseModelInterface || !method_exists($model, 'publish')) { + return false; + } + + $table = $model->getTable(); + + if (!$table instanceof TableInterface || !$table->hasField('published')) { + return false; + } + + return true; + } + + /** + * If plugin supports the functionality we set the used variable + * + * @param WorkflowFunctionalityUsedEvent $event + * + * @since 4.0.0 + */ + public function onWorkflowFunctionalityUsed(WorkflowFunctionalityUsedEvent $event) + { + $functionality = $event->getArgument('functionality'); + + if ($functionality !== 'core.state') { + return; + } + + $event->setUsed(); + } +} diff --git a/plugins/workflow/images/images.xml b/plugins/workflow/images/images.xml new file mode 100644 index 0000000000000..c64189d43f80c --- /dev/null +++ b/plugins/workflow/images/images.xml @@ -0,0 +1,43 @@ + +