diff --git a/administrator/language/en-GB/plg_content_joomla.ini b/administrator/language/en-GB/plg_content_joomla.ini index 0cf5749dddb3f..c7f64edcf915a 100644 --- a/administrator/language/en-GB/plg_content_joomla.ini +++ b/administrator/language/en-GB/plg_content_joomla.ini @@ -8,8 +8,4 @@ PLG_CONTENT_JOOMLA_FIELD_CHECK_CATEGORIES_DESC="Check that categories are fully PLG_CONTENT_JOOMLA_FIELD_CHECK_CATEGORIES_LABEL="Check Category Deletion" PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_FE_DESC="Email users if 'Send email' is on when there is a new article submitted via the Frontend." PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_FE_LABEL="Email on New Site Article" -PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_STAGE_DESC="Email users if 'Send email' is on when there is a status change of an article." -PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_STAGE_LABEL="Email on transition execution" -PLG_CONTENT_JOOMLA_ON_STAGE_CHANGE_MSG="The status of an article has been changed by '%1$s' entitled '%2$s'." -PLG_CONTENT_JOOMLA_ON_STAGE_CHANGE_SUBJECT="Status of article has changed" PLG_CONTENT_JOOMLA_XML_DESCRIPTION="This plugin does category processing for core extensions; sends an email when new article is submitted in the Frontend or a transition is executed." diff --git a/administrator/language/en-GB/plg_workflow_notification.ini b/administrator/language/en-GB/plg_workflow_notification.ini new file mode 100644 index 0000000000000..162306c97b3a5 --- /dev/null +++ b/administrator/language/en-GB/plg_workflow_notification.ini @@ -0,0 +1,23 @@ +; Joomla! Project +; Copyright (C) 2005 - 2019 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 + +COM_WORKFLOW_NOTIFICATION_FIELDSET_LABEL="Notification" +PLG_WORKFLOW_NOTIFICATION="Workflow - Notification" +PLG_WORKFLOW_NOTIFICATION_ADDTEXT="The state has changed" +PLG_WORKFLOW_NOTIFICATION_ADDTEXT_AUTHOR_DESC="You can localise the text by using a Language key and make language overrides." +PLG_WORKFLOW_NOTIFICATION_ADDTEXT_AUTHOR_LABEL="Additional Message to Author" +PLG_WORKFLOW_NOTIFICATION_ADDTEXT_DESC="You can localise the text by using a Language key and make language overrides." +PLG_WORKFLOW_NOTIFICATION_ADDTEXT_LABEL="Additional Message Text" +PLG_WORKFLOW_NOTIFICATION_EMAIL_AUTHOR_LABEL="Email the Author" +PLG_WORKFLOW_NOTIFICATION_ON_TRANSITION_SUBJECT="The status of an '%1$s' has been changed"; +PLG_WORKFLOW_NOTIFICATION_ON_TRANSITION_MSG="Title: '%1$s', Changed by '%2$s', New State: '%3$s'." +PLG_WORKFLOW_NOTIFICATION_RECEIVERS_LABEL="More Receivers" +PLG_WORKFLOW_NOTIFICATION_RECEIVERS_SELECT="Select single receivers for the notification" +PLG_WORKFLOW_NOTIFICATION_SENDMAIL_LABEL="Send Notification" +PLG_WORKFLOW_NOTIFICATION_SENT="Notifications have been sent" +PLG_WORKFLOW_NOTIFICATION_USERGROUP_DESC="The users in this usergroup get an E-Mail if this transition has been performed" +PLG_WORKFLOW_NOTIFICATION_USERGROUP_LABEL="Usergroups" +PLG_WORKFLOW_NOTIFICATION_USERGROUP_SELECT="Select usergroups" +PLG_WORKFLOW_NOTIFICATION_XML_DESCRIPTION="Send Notification if a Transition has been performed in a Workflow" diff --git a/administrator/language/en-GB/plg_workflow_notification.sys.ini b/administrator/language/en-GB/plg_workflow_notification.sys.ini new file mode 100644 index 0000000000000..c585237338360 --- /dev/null +++ b/administrator/language/en-GB/plg_workflow_notification.sys.ini @@ -0,0 +1,7 @@ +; Joomla! Project +; Copyright (C) 2005 - 2019 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_NOTIFICATION="Workflow - Notification" +PLG_WORKFLOW_NOTIFICATION_XML_DESCRIPTION="Send Notification for Transitions in Publishing Workflow" \ No newline at end of file diff --git a/installation/sql/mysql/base.sql b/installation/sql/mysql/base.sql index 989334a8c214e..3a9743f5ba51e 100644 --- a/installation/sql/mysql/base.sql +++ b/installation/sql/mysql/base.sql @@ -359,7 +359,8 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, (0, 'plg_system_accessibility', 'plugin', 'accessibility', 'system', 0, 0, 1, 0, 1, '', '{}', 0, NULL, 0, 0), (0, 'plg_system_webauthn', 'plugin', 'webauthn', 'system', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0), (0, 'plg_workflow_publishing', 'plugin', 'publishing', 'workflow', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0), -(0, 'plg_workflow_featuring', 'plugin', 'featuring', 'workflow', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0); +(0, 'plg_workflow_featuring', 'plugin', 'featuring', 'workflow', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0), +(0, 'plg_workflow_notification', 'plugin', 'notification', 'workflow', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0); -- Templates INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES diff --git a/installation/sql/postgresql/base.sql b/installation/sql/postgresql/base.sql index 0788051ee42a2..205030176d08b 100644 --- a/installation/sql/postgresql/base.sql +++ b/installation/sql/postgresql/base.sql @@ -365,7 +365,8 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", (0, 'plg_system_accessibility', 'plugin', 'accessibility', 'system', 0, 0, 1, 0, 1, '', '{}', 0, NULL, 0, 0), (0, 'plg_system_webauthn', 'plugin', 'webauthn', 'system', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0), (0, 'plg_workflow_publishing', 'plugin', 'publishing', 'workflow', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0), -(0, 'plg_workflow_featuring', 'plugin', 'featuring', 'workflow', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0); +(0, 'plg_workflow_featuring', 'plugin', 'featuring', 'workflow', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0), +(0, 'plg_workflow_notification', 'plugin', 'notification', 'workflow', 0, 1, 1, 0, 1, '', '{}', 0, NULL, 0, 0); -- Templates INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "locked", "manifest_cache", "params", "checked_out", "checked_out_time", "ordering", "state") VALUES diff --git a/plugins/content/joomla/joomla.php b/plugins/content/joomla/joomla.php index 1c2ed441993b1..1c9cb000c87cf 100644 --- a/plugins/content/joomla/joomla.php +++ b/plugins/content/joomla/joomla.php @@ -616,7 +616,7 @@ public function onContentChangeState($context, $pks, $value) } // Check if this function is enabled. - if (!$this->params->def('email_new_stage', 0) || $context != 'com_content.article') + if ($context != 'com_content.article') { return true; } @@ -634,89 +634,6 @@ public function onContentChangeState($context, $pks, $value) $cctable = new CoreContent($db); $cctable->publish($ccIds, $value); - $query = $db->getQuery(true) - ->select($db->quoteName('id')) - ->from($db->quoteName('#__users')) - ->where($db->quoteName('sendEmail') . ' = 1') - ->where($db->quoteName('block') . ' = 0'); - - $users = (array) $db->setQuery($query)->loadColumn(); - - if (empty($users)) - { - return true; - } - - $user = $this->app->getIdentity(); - - // Messaging for changed items - $default_language = ComponentHelper::getParams('com_languages')->get('administrator'); - $debug = $this->app->get('debug_lang'); - - $article = new ArticleTable($db); - - $workflow = new Workflow(['extension' => 'com_content.article']); - - foreach ($pks as $pk) - { - if (!$article->load($pk)) - { - continue; - } - - $assoc = $workflow->getAssociation($pk); - $stageId = (int) $assoc->stage_id; - - // Load new transitions - $query = $db->getQuery(true) - ->select($db->quoteName('t.id')) - ->from($db->quoteName('#__workflow_transitions', 't')) - ->from($db->quoteName('#__workflow_stages', 's')) - ->where($db->quoteName('t.from_stage_id') . ' = :stageid') - ->where($db->quoteName('t.to_stage_id') . ' = ' . $db->quoteName('s.id')) - ->where($db->quoteName('t.published') . ' = 1') - ->where($db->quoteName('s.published') . ' = 1') - ->order($db->quoteName('t.ordering')) - ->bind(':stageid', $stageId, ParameterType::INTEGER); - - $transitions = $db->setQuery($query)->loadObjectList(); - - foreach ($users as $user_id) - { - if ($user_id != $user->id) - { - // Check if the user has available transitions - $items = array_filter( - $transitions, - function ($item) use ($user) - { - return $user->authorise('core.execute.transition', 'com_content.transition.' . $item->id); - } - ); - - if (!count($items)) - { - continue; - } - - // Load language for messaging - $receiver = User::getInstance($user_id); - $lang = Language::getInstance($receiver->getParam('admin_language', $default_language), $debug); - $lang->load('plg_content_joomla'); - - $message = array( - 'user_id_to' => $user_id, - 'subject' => $lang->_('PLG_CONTENT_JOOMLA_ON_STAGE_CHANGE_SUBJECT'), - 'message' => sprintf($lang->_('PLG_CONTENT_JOOMLA_ON_STAGE_CHANGE_MSG'), $user->name, $article->title), - ); - - $model_message = $this->app->bootComponent('com_messages') - ->getMVCFactory()->createModel('Message', 'Administrator'); - $model_message->save($message); - } - } - } - return true; } diff --git a/plugins/content/joomla/joomla.xml b/plugins/content/joomla/joomla.xml index 51572e9505904..8fbe01231966b 100644 --- a/plugins/content/joomla/joomla.xml +++ b/plugins/content/joomla/joomla.xml @@ -44,18 +44,6 @@ - - - - - diff --git a/plugins/workflow/notification/forms/workflow_notification.xml b/plugins/workflow/notification/forms/workflow_notification.xml new file mode 100644 index 0000000000000..9770512417abe --- /dev/null +++ b/plugins/workflow/notification/forms/workflow_notification.xml @@ -0,0 +1,70 @@ + +
+ +
+ + + + + + + + + + + + +
+
+
diff --git a/plugins/workflow/notification/notification.php b/plugins/workflow/notification/notification.php new file mode 100644 index 0000000000000..73ad63726b4a2 --- /dev/null +++ b/plugins/workflow/notification/notification.php @@ -0,0 +1,321 @@ +getName(); + + // Extend the transition form + if ($context !== 'com_workflow.transition') + { + return; + } + + return $this->enhanceTransitionForm($form, $data); + } + + /** + * Add different parameter options to the transition view, we need when executing the transition + * + * @param Form $form The form + * @param stdClass $data The data + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + protected function enhanceTransitionForm(Form $form, $data) + { + Form::addFormPath(__DIR__ . '/forms'); + + $form->loadFile('workflow_notification'); + + return true; + } + + /** + * Send a Notification to defined users a transion is performed + * + * @param string $context The context for the content passed to the plugin. + * @param array $pks A list of primary key ids of the content that has changed stage. + * @param object $data Object containing data about the transition + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + public function onWorkflowAfterTransition($context, $pks, $data) + { + $parts = explode('.', $context); + + // Check the extension + if (count($parts) < 2) + { + return false; + } + + $component = $this->app->bootComponent($parts[0]); + + if (!$component->isWorkflowActive($context)) + { + return false; + } + + // Check if send-mail is active + if (empty($data->options['notification_send_mail'])) + { + return true; + } + + // ID of the items whose state has changed. + $pks = ArrayHelper::toInteger($pks); + + if (empty($pks)) + { + return true; + } + + // Get UserIds of Receivers + $userIds = $this->getUsersFromGroup($data); + + // The active user + $user = $this->app->getIdentity(); + + // Prepare Language for messages + $default_language = ComponentHelper::getParams('com_languages')->get('administrator'); + $debug = $this->app->get('debug_lang'); + + $modelName = $component->getModelName($context); + $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); + + $authorId = 0; + + // Add author of the item to the receivers array if the param email-author is set + if (!empty($data->options['notification_email_author']) && !empty($item->created_by)) + { + $author = User::getInstance($item->created_by); + + if (!empty($author) && !$author->block) + { + if (!in_array($author->id, $userIds)) + { + $userIds[] = (int) $author->id; + $authorId = $author->id; + } + } + } + + // Don't send the notification to the active user + $key = array_search($user->id, $userIds); + + if (is_integer($key)) + { + unset($userIds[$key]); + } + + // Remove users with locked input box from the list of receivers + if (!empty($userIds)) + { + $userIds = $this->removeLocked($userIds); + } + + // If there are no receivers, stop here + if (empty($userIds)) + { + return true; + } + + // Get the model for private messages + $model_message = $this->app->bootComponent('com_messages') + ->getMVCFactory()->createModel('Message', 'Administrator'); + + // Get the title of the stage + $model_stage = $this->app->bootComponent('com_workflow') + ->getMVCFactory()->createModel('Stage', 'Administrator'); + + $toStage = $model_stage->getItem($data->to_stage_id)->title; + + foreach ($pks as $pk) + { + // Get the title of the item which has changed + $title =''; + + if (method_exists($model, 'getItem')) + { + $title = $model->getItem($pk)->title; + } + + // Send Email to receivers + foreach ($userIds as $user_id) + { + $receiver = User::getInstance($user_id); + + // Load language for messaging + $lang = Language::getInstance($user->getParam('admin_language', $default_language), $debug); + $lang->load('plg_workflow_notification'); + $messageText = sprintf($lang->_('PLG_WORKFLOW_NOTIFICATION_ON_TRANSITION_MSG'), $title, $user->name, $lang->_($toStage)); + + if (!empty($data->options['notification_text'] && $user_id !== $authorId)) + { + $messageText .= '
' . htmlspecialchars($lang->_($data->options['notification_text'])); + } + + if (!empty($data->options['notification_author_text'] && $user_id === $authorId)) + { + $messageText .= '
' . htmlspecialchars($lang->_($data->options['notification_text_author'])); + } + + $message = [ + 'id' => 0, + 'user_id_to' => $receiver->id, + 'subject' => sprintf($lang->_('PLG_WORKFLOW_NOTIFICATION_ON_TRANSITION_SUBJECT'), $modelName), + 'message' => $messageText, + ]; + + $model_message->save($message); + } + + $this->app->enqueueMessage(Text::_('PLG_WORKFLOW_NOTIFICATION_SENT'), 'message'); + } + + return true; + } + + /* + * Get user_ids of receivers + * + * @param object $data Object containing data about the transition + * + * @return array $userIds The receivers + * + * @since __DEPLOY_VERSION__ + */ + private function getUsersFromGroup($data): Array + { + $users = []; + + // Single userIds + if (!empty($data->options['notification_receivers'])) + { + $users = ArrayHelper::toInteger($data->options['notification_receivers']); + } + + // Usergroups + $groups = []; + + if (!empty($data->options['notification_groups'])) + { + $groups = ArrayHelper::toInteger($data->options['notification_groups']); + } + + $users2 = []; + + if (!empty($groups)) + { + // UserIds from usergroups + $model = Factory::getApplication()->bootComponent('com_users') + ->getMVCFactory()->createModel('Users', 'Administrator', ['ignore_request' => true]); + + $model->setState('list.select', 'id'); + $model->setState('filter.groups', $groups); + $model->setState('filter.state', 0); + $model->setState('filter.active', 1); + $model->setState('filter.sendEmail', 1); + + // Ids from usergroups + $groupUsers = $model->getItems(); + $users2 = ArrayHelper::getColumn($groupUsers, 'id'); + } + + // Merge userIds from individual entries and userIDs from groups + $userIds = array_unique(array_merge($users, $users2)); + + return $userIds; + } + + /* + * Remove receivers who have locked their message inputbox + * + * @param array $uerIds The userIds which must be checked + * + * @return array users with active message input box + * + * @since __DEPLOY_VERSION__ + */ + private function removeLocked($userIds): Array + { + // Check for locked inboxes would be better to have _cdf settings in the user_object or a filter in users model + $locked = []; + + $db = $this->db; + $query = $db->getQuery(true); + $query->select($db->quoteName('user_id')) + ->from($db->quoteName('#__messages_cfg')) + ->whereIn($db->quoteName('user_id'), $userIds) + ->where($db->quoteName('cfg_name') . '=' . $db->quote('locked')) + ->where($db->quoteName('cfg_value') . '=1'); + + $locked = $db->setQuery($query)->loadColumn(); + + return array_diff($userIds, $locked); + } +} diff --git a/plugins/workflow/notification/notification.xml b/plugins/workflow/notification/notification.xml new file mode 100644 index 0000000000000..84199d2433b33 --- /dev/null +++ b/plugins/workflow/notification/notification.xml @@ -0,0 +1,27 @@ + + + plg_workflow_notification + Joomla! Project + May 2020 + Copyright (C) 2005 - 2020 Open Source Matters. All rights reserved. + GNU General Public License version 2 or later; see LICENSE.txt + admin@joomla.org + www.joomla.org + 4.0.0 + PLG_WORKFLOW_NOTIFICATION_XML_DESCRIPTION + + notification.php + + + plg_workflow_notification.ini + plg_workflow_notification.sys.ini + + + +
+ +
+
+
+ +