Файловый менеджер - Редактировать - /home/lmsyaran/public_html/joomla4/plugins.tar
Назад
actionlog/joomla/joomla.php 0000644 00000071772 14735702223 0012021 0 ustar 00 <?php /** * @package Joomla.Plugins * @subpackage System.actionlogs * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\User\User; use Joomla\CMS\Version; use Joomla\Utilities\ArrayHelper; JLoader::register('ActionLogPlugin', JPATH_ADMINISTRATOR . '/components/com_actionlogs/libraries/actionlogplugin.php'); JLoader::register('ActionlogsHelper', JPATH_ADMINISTRATOR . '/components/com_actionlogs/helpers/actionlogs.php'); /** * Joomla! Users Actions Logging Plugin. * * @since 3.9.0 */ class PlgActionlogJoomla extends ActionLogPlugin { /** * Array of loggable extensions. * * @var array * @since 3.9.0 */ protected $loggableExtensions = array(); /** * Context aliases * * @var array * @since 3.9.0 */ protected $contextAliases = array('com_content.form' => 'com_content.article'); /** * Constructor. * * @param object &$subject The object to observe. * @param array $config An optional associative array of configuration settings. * * @since 3.9.0 */ public function __construct(&$subject, $config) { parent::__construct($subject, $config); $params = ComponentHelper::getComponent('com_actionlogs')->getParams(); $this->loggableExtensions = $params->get('loggable_extensions', array()); } /** * After save content logging method * This method adds a record to #__action_logs contains (message, date, context, user) * Method is called right after the content is saved * * @param string $context The context of the content passed to the plugin * @param object $article A JTableContent object * @param boolean $isNew If the content is just about to be created * * @return void * * @since 3.9.0 */ public function onContentAfterSave($context, $article, $isNew) { if (isset($this->contextAliases[$context])) { $context = $this->contextAliases[$context]; } $option = $this->app->input->getCmd('option'); if (!$this->checkLoggable($option)) { return; } $params = ActionlogsHelper::getLogContentTypeParams($context); // Not found a valid content type, don't process further if ($params === null) { return; } list(, $contentType) = explode('.', $params->type_alias); if ($isNew) { $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_ADDED'; $defaultLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_ADDED'; } else { $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_UPDATED'; $defaultLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_UPDATED'; } // If the content type doesn't has it own language key, use default language key if (!$this->app->getLanguage()->hasKey($messageLanguageKey)) { $messageLanguageKey = $defaultLanguageKey; } $id = empty($params->id_holder) ? 0 : $article->get($params->id_holder); $message = array( 'action' => $isNew ? 'add' : 'update', 'type' => $params->text_prefix . '_TYPE_' . $params->type_title, 'id' => $id, 'title' => $article->get($params->title_holder), 'itemlink' => ActionlogsHelper::getContentTypeLink($option, $contentType, $id, $params->id_holder, $article), ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * After delete content logging method * This method adds a record to #__action_logs contains (message, date, context, user) * Method is called right after the content is deleted * * @param string $context The context of the content passed to the plugin * @param object $article A JTableContent object * * @return void * * @since 3.9.0 */ public function onContentAfterDelete($context, $article) { $option = $this->app->input->get('option'); if (!$this->checkLoggable($option)) { return; } $params = ActionlogsHelper::getLogContentTypeParams($context); // Not found a valid content type, don't process further if ($params === null) { return; } // If the content type has it own language key, use it, otherwise, use default language key if ($this->app->getLanguage()->hasKey(strtoupper($params->text_prefix . '_' . $params->type_title . '_DELETED'))) { $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_DELETED'; } else { $messageLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_DELETED'; } $id = empty($params->id_holder) ? 0 : $article->get($params->id_holder); $message = array( 'action' => 'delete', 'type' => $params->text_prefix . '_TYPE_' . $params->type_title, 'id' => $id, 'title' => $article->get($params->title_holder) ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * On content change status logging method * This method adds a record to #__action_logs contains (message, date, context, user) * Method is called when the status of the article is changed * * @param string $context The context of the content passed to the plugin * @param array $pks An array of primary key ids of the content that has changed state. * @param integer $value The value of the state that the content has been changed to. * * @return void * * @since 3.9.0 */ public function onContentChangeState($context, $pks, $value) { $option = $this->app->input->getCmd('option'); if (!$this->checkLoggable($option)) { return; } $params = ActionlogsHelper::getLogContentTypeParams($context); // Not found a valid content type, don't process further if ($params === null) { return; } list(, $contentType) = explode('.', $params->type_alias); switch ($value) { case 0: $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_UNPUBLISHED'; $defaultLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_UNPUBLISHED'; $action = 'unpublish'; break; case 1: $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_PUBLISHED'; $defaultLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_PUBLISHED'; $action = 'publish'; break; case 2: $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_ARCHIVED'; $defaultLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_ARCHIVED'; $action = 'archive'; break; case -2: $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_TRASHED'; $defaultLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_TRASHED'; $action = 'trash'; break; default: $messageLanguageKey = ''; $defaultLanguageKey = ''; $action = ''; break; } // If the content type doesn't has it own language key, use default language key if (!$this->app->getLanguage()->hasKey($messageLanguageKey)) { $messageLanguageKey = $defaultLanguageKey; } $db = $this->db; $query = $db->getQuery(true) ->select($db->quoteName(array($params->title_holder, $params->id_holder))) ->from($db->quoteName($params->table_name)) ->where($db->quoteName($params->id_holder) . ' IN (' . implode(',', ArrayHelper::toInteger($pks)) . ')'); $db->setQuery($query); try { $items = $db->loadObjectList($params->id_holder); } catch (RuntimeException $e) { $items = array(); } $messages = array(); foreach ($pks as $pk) { $message = array( 'action' => $action, 'type' => $params->text_prefix . '_TYPE_' . $params->type_title, 'id' => $pk, 'title' => $items[$pk]->{$params->title_holder}, 'itemlink' => ActionlogsHelper::getContentTypeLink($option, $contentType, $pk, $params->id_holder, null) ); $messages[] = $message; } $this->addLog($messages, $messageLanguageKey, $context); } /** * On Saving application configuration logging method * Method is called when the application config is being saved * * @param JRegistry $config JRegistry object with the new config * * @return void * * @since 3.9.0 */ public function onApplicationAfterSave($config) { $option = $this->app->input->getCmd('option'); if (!$this->checkLoggable($option)) { return; } $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_APPLICATION_CONFIG_UPDATED'; $action = 'update'; $message = array( 'action' => $action, 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_APPLICATION_CONFIG', 'extension_name' => 'com_config.application', 'itemlink' => 'index.php?option=com_config' ); $this->addLog(array($message), $messageLanguageKey, 'com_config.application'); } /** * On installing extensions logging method * This method adds a record to #__action_logs contains (message, date, context, user) * Method is called when an extension is installed * * @param JInstaller $installer Installer object * @param integer $eid Extension Identifier * * @return void * * @since 3.9.0 */ public function onExtensionAfterInstall($installer, $eid) { $context = $this->app->input->get('option'); if (!$this->checkLoggable($context)) { return; } $manifest = $installer->get('manifest'); if ($manifest === null) { return; } $extensionType = $manifest->attributes()->type; // If the extension type has it own language key, use it, otherwise, use default language key if ($this->app->getLanguage()->hasKey(strtoupper('PLG_ACTIONLOG_JOOMLA_' . $extensionType . '_INSTALLED'))) { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_' . $extensionType . '_INSTALLED'; } else { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_EXTENSION_INSTALLED'; } $message = array( 'action' => 'install', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_' . $extensionType, 'id' => $eid, 'name' => (string) $manifest->name, 'extension_name' => (string) $manifest->name ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * On uninstalling extensions logging method * This method adds a record to #__action_logs contains (message, date, context, user) * Method is called when an extension is uninstalled * * @param JInstaller $installer Installer instance * @param integer $eid Extension id * @param integer $result Installation result * * @return void * * @since 3.9.0 */ public function onExtensionAfterUninstall($installer, $eid, $result) { $context = $this->app->input->get('option'); if (!$this->checkLoggable($context)) { return; } // If the process failed, we don't have manifest data, stop process to avoid fatal error if ($result === false) { return; } $manifest = $installer->get('manifest'); if ($manifest === null) { return; } $extensionType = $manifest->attributes()->type; // If the extension type has it own language key, use it, otherwise, use default language key if ($this->app->getLanguage()->hasKey(strtoupper('PLG_ACTIONLOG_JOOMLA_' . $extensionType . '_UNINSTALLED'))) { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_' . $extensionType . '_UNINSTALLED'; } else { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_EXTENSION_UNINSTALLED'; } $message = array( 'action' => 'install', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_' . $extensionType, 'id' => $eid, 'name' => (string) $manifest->name, 'extension_name' => (string) $manifest->name ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * On updating extensions logging method * This method adds a record to #__action_logs contains (message, date, context, user) * Method is called when an extension is updated * * @param JInstaller $installer Installer instance * @param integer $eid Extension id * * @return void * * @since 3.9.0 */ public function onExtensionAfterUpdate($installer, $eid) { $context = $this->app->input->get('option'); if (!$this->checkLoggable($context)) { return; } $manifest = $installer->get('manifest'); if ($manifest === null) { return; } $extensionType = $manifest->attributes()->type; // If the extension type has it own language key, use it, otherwise, use default language key if ($this->app->getLanguage()->hasKey('PLG_ACTIONLOG_JOOMLA_' . $extensionType . '_UPDATED')) { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_' . $extensionType . '_UPDATED'; } else { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_EXTENSION_UPDATED'; } $message = array( 'action' => 'update', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_' . $extensionType, 'id' => $eid, 'name' => (string) $manifest->name, 'extension_name' => (string) $manifest->name ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * On Saving extensions logging method * Method is called when an extension is being saved * * @param string $context The extension * @param JTable $table DataBase Table object * @param boolean $isNew If the extension is new or not * * @return void * * @since 3.9.0 */ public function onExtensionAfterSave($context, $table, $isNew) { $option = $this->app->input->getCmd('option'); if ($table->get('module') != null) { $option = 'com_modules'; } if (!$this->checkLoggable($option)) { return; } $params = ActionlogsHelper::getLogContentTypeParams($context); // Not found a valid content type, don't process further if ($params === null) { return; } list(, $contentType) = explode('.', $params->type_alias); if ($isNew) { $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_ADDED'; $defaultLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_ADDED'; } else { $messageLanguageKey = $params->text_prefix . '_' . $params->type_title . '_UPDATED'; $defaultLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_UPDATED'; } // If the extension type doesn't have it own language key, use default language key if (!$this->app->getLanguage()->hasKey($messageLanguageKey)) { $messageLanguageKey = $defaultLanguageKey; } $message = array( 'action' => $isNew ? 'add' : 'update', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_' . $params->type_title, 'id' => $table->get($params->id_holder), 'title' => $table->get($params->title_holder), 'extension_name' => $table->get($params->title_holder), 'itemlink' => ActionlogsHelper::getContentTypeLink($option, $contentType, $table->get($params->id_holder), $params->id_holder, null) ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * On Deleting extensions logging method * Method is called when an extension is being deleted * * @param string $context The extension * @param JTable $table DataBase Table object * * @return void * * @since 3.9.0 */ public function onExtensionAfterDelete($context, $table) { if (!$this->checkLoggable($this->app->input->get('option'))) { return; } $params = ActionlogsHelper::getLogContentTypeParams($context); // Not found a valid content type, don't process further if ($params === null) { return; } $messageLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_DELETED'; $message = array( 'action' => 'delete', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_' . $params->type_title, 'title' => $table->get($params->title_holder) ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * On saving user data logging method * * Method is called after user data is stored in the database. * This method logs who created/edited any user's data * * @param array $user Holds the new user data. * @param boolean $isnew True if a new user is stored. * @param boolean $success True if user was successfully stored in the database. * @param string $msg Message. * * @return void * * @since 3.9.0 */ public function onUserAfterSave($user, $isnew, $success, $msg) { $context = $this->app->input->get('option'); $task = $this->app->input->get->getCmd('task'); if (!$this->checkLoggable($context)) { return; } $jUser = Factory::getUser(); if (!$jUser->id) { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_USER_REGISTERED'; $action = 'register'; // Reset request if ($task === 'reset.request') { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_USER_RESET_REQUEST'; $action = 'resetrequest'; } // Reset complete if ($task === 'reset.complete') { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_USER_RESET_COMPLETE'; $action = 'resetcomplete'; } // Registration Activation if ($task === 'registration.activate') { $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_USER_REGISTRATION_ACTIVATE'; $action = 'activaterequest'; } } elseif ($isnew) { $messageLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_ADDED'; $action = 'add'; } else { $messageLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_UPDATED'; $action = 'update'; } $userId = $jUser->id ?: $user['id']; $username = $jUser->username ?: $user['username']; $message = array( 'action' => $action, 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER', 'id' => $user['id'], 'title' => $user['name'], 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $user['id'], 'userid' => $userId, 'username' => $username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $userId, ); $this->addLog(array($message), $messageLanguageKey, $context, $userId); } /** * On deleting user data logging method * * Method is called after user data is deleted from the database * * @param array $user Holds the user data * @param boolean $success True if user was successfully stored in the database * @param string $msg Message * * @return void * * @since 3.9.0 */ public function onUserAfterDelete($user, $success, $msg) { $context = $this->app->input->get('option'); if (!$this->checkLoggable($context)) { return; } $messageLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_DELETED'; $message = array( 'action' => 'delete', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER', 'id' => $user['id'], 'title' => $user['name'] ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * On after save user group data logging method * * Method is called after user group is stored into the database * * @param string $context The context * @param JTable $table DataBase Table object * @param boolean $isNew Is new or not * * @return void * * @since 3.9.0 */ public function onUserAfterSaveGroup($context, $table, $isNew) { // Override context (com_users.group) with the component context (com_users) to pass the checkLoggable $context = $this->app->input->get('option'); if (!$this->checkLoggable($context)) { return; } if ($isNew) { $messageLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_ADDED'; $action = 'add'; } else { $messageLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_UPDATED'; $action = 'update'; } $message = array( 'action' => $action, 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER_GROUP', 'id' => $table->id, 'title' => $table->title, 'itemlink' => 'index.php?option=com_users&task=group.edit&id=' . $table->id ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * On deleting user group data logging method * * Method is called after user group is deleted from the database * * @param array $group Holds the group data * @param boolean $success True if user was successfully stored in the database * @param string $msg Message * * @return void * * @since 3.9.0 */ public function onUserAfterDeleteGroup($group, $success, $msg) { $context = $this->app->input->get('option'); if (!$this->checkLoggable($context)) { return; } $messageLanguageKey = 'PLG_SYSTEM_ACTIONLOGS_CONTENT_DELETED'; $message = array( 'action' => 'delete', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER_GROUP', 'id' => $group['id'], 'title' => $group['title'] ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * Method to log user login success action * * @param array $options Array holding options (user, responseType) * * @return void * * @since 3.9.0 */ public function onUserAfterLogin($options) { $context = 'com_users'; if (!$this->checkLoggable($context)) { return; } $loggedInUser = $options['user']; $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_USER_LOGGED_IN'; $message = array( 'action' => 'login', 'userid' => $loggedInUser->id, 'username' => $loggedInUser->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $loggedInUser->id, 'app' => 'PLG_ACTIONLOG_JOOMLA_APPLICATION_' . $this->app->getName(), ); $this->addLog(array($message), $messageLanguageKey, $context, $loggedInUser->id); } /** * Method to log user login failed action * * @param array $response Array of response data. * * @return void * * @since 3.9.0 */ public function onUserLoginFailure($response) { $context = 'com_users'; if (!$this->checkLoggable($context)) { return; } // Get the user id for the given username $query = $this->db->getQuery(true) ->select($this->db->quoteName(array('id', 'username'))) ->from($this->db->quoteName('#__users')) ->where($this->db->quoteName('username') . ' = ' . $this->db->quote($response['username'])); $this->db->setQuery($query); try { $loggedInUser = $this->db->loadObject(); } catch (JDatabaseExceptionExecuting $e) { return; } // Not a valid user, return if (!isset($loggedInUser->id)) { return; } $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_USER_LOGIN_FAILED'; $message = array( 'action' => 'login', 'id' => $loggedInUser->id, 'userid' => $loggedInUser->id, 'username' => $loggedInUser->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $loggedInUser->id, 'app' => 'PLG_ACTIONLOG_JOOMLA_APPLICATION_' . $this->app->getName(), ); $this->addLog(array($message), $messageLanguageKey, $context, $loggedInUser->id); } /** * Method to log user's logout action * * @param array $user Holds the user data * @param array $options Array holding options (remember, autoregister, group) * * @return void * * @since 3.9.0 */ public function onUserLogout($user, $options = array()) { $context = 'com_users'; if (!$this->checkLoggable($context)) { return; } $loggedOutUser = User::getInstance($user['id']); if ($loggedOutUser->block) { return; } $messageLanguageKey = 'PLG_ACTIONLOG_JOOMLA_USER_LOGGED_OUT'; $message = array( 'action' => 'logout', 'id' => $loggedOutUser->id, 'userid' => $loggedOutUser->id, 'username' => $loggedOutUser->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $loggedOutUser->id, 'app' => 'PLG_ACTIONLOG_JOOMLA_APPLICATION_' . $this->app->getName(), ); $this->addLog(array($message), $messageLanguageKey, $context); } /** * Function to check if a component is loggable or not * * @param string $extension The extension that triggered the event * * @return boolean * * @since 3.9.0 */ protected function checkLoggable($extension) { return in_array($extension, $this->loggableExtensions); } /** * On after Remind username request * * Method is called after user request to remind their username. * * @param array $user Holds the user data. * * @return void * * @since 3.9.0 */ public function onUserAfterRemind($user) { $context = $this->app->input->get('option'); if (!$this->checkLoggable($context)) { return; } $message = array( 'action' => 'remind', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER', 'id' => $user->id, 'title' => $user->name, 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'userid' => $user->id, 'username' => $user->name, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, ); $this->addLog(array($message), 'PLG_ACTIONLOG_JOOMLA_USER_REMIND', $context, $user->id); } /** * On after Check-in request * * Method is called after user request to check-in items. * * @param array $table Holds the table name. * * @return void * * @since 3.9.3 */ public function onAfterCheckin($table) { $context = 'com_checkin'; $user = Factory::getUser(); if (!$this->checkLoggable($context)) { return; } $message = array( 'action' => 'checkin', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER', 'id' => $user->id, 'title' => $user->username, 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'userid' => $user->id, 'username' => $user->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'table' => $table, ); $this->addLog(array($message), 'PLG_ACTIONLOG_JOOMLA_USER_CHECKIN', $context, $user->id); } /** * On after log action purge * * Method is called after user request to clean action log items. * * @param array $group Holds the group name. * * @return void * * @since 3.9.4 */ public function onAfterLogPurge($group = '') { $context = $this->app->input->get('option'); $user = Factory::getUser(); $message = array( 'action' => 'actionlogs', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER', 'id' => $user->id, 'title' => $user->username, 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'userid' => $user->id, 'username' => $user->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, ); $this->addLog(array($message), 'PLG_ACTIONLOG_JOOMLA_USER_LOG', $context, $user->id); } /** * On after log export * * Method is called after user request to export action log items. * * @param array $group Holds the group name. * * @return void * * @since 3.9.4 */ public function onAfterLogExport($group = '') { $context = $this->app->input->get('option'); $user = Factory::getUser(); $message = array( 'action' => 'actionlogs', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER', 'id' => $user->id, 'title' => $user->username, 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'userid' => $user->id, 'username' => $user->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, ); $this->addLog(array($message), 'PLG_ACTIONLOG_JOOMLA_USER_LOGEXPORT', $context, $user->id); } /** * On after Cache purge * * Method is called after user request to clean cached items. * * @param string $group Holds the group name. * * @return void * * @since 3.9.4 */ public function onAfterPurge($group = 'all') { $context = $this->app->input->get('option'); $user = JFactory::getUser(); if (!$this->checkLoggable($context)) { return; } $message = array( 'action' => 'cache', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER', 'id' => $user->id, 'title' => $user->username, 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'userid' => $user->id, 'username' => $user->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'group' => $group, ); $this->addLog(array($message), 'PLG_ACTIONLOG_JOOMLA_USER_CACHE', $context, $user->id); } /** * On after CMS Update * * Method is called after user update the CMS. * * @param string $oldVersion The Joomla version before the update * * @return void * * @since 3.9.21 */ public function onJoomlaAfterUpdate($oldVersion = null) { $context = $this->app->input->get('option'); $user = JFactory::getUser(); if (empty($oldVersion)) { $oldVersion = JText::_('JLIB_UNKNOWN'); } $message = array( 'action' => 'joomlaupdate', 'type' => 'PLG_ACTIONLOG_JOOMLA_TYPE_USER', 'id' => $user->id, 'title' => $user->username, 'itemlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'userid' => $user->id, 'username' => $user->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, 'version' => JVERSION, 'oldversion' => $oldVersion, ); $this->addLog(array($message), 'PLG_ACTIONLOG_JOOMLA_USER_UPDATE', $context, $user->id); } } actionlog/joomla/joomla.xml 0000644 00000001420 14735702223 0012011 0 ustar 00 <?xml version="1.0" encoding="UTF-8"?> <extension version="3.9" type="plugin" group="actionlog" method="upgrade"> <name>PLG_ACTIONLOG_JOOMLA</name> <author>Joomla! Project</author> <creationDate>May 2018</creationDate> <copyright>(C) 2018 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.9.0</version> <description>PLG_ACTIONLOG_JOOMLA_XML_DESCRIPTION</description> <files> <filename plugin="joomla">joomla.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_actionlog_joomla.ini</language> <language tag="en-GB">en-GB.plg_actionlog_joomla.sys.ini</language> </languages> </extension> authentication/cookie/cookie.php 0000644 00000026743 14735702223 0013037 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Authentication.cookie * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Joomla Authentication plugin * * @since 3.2 * @note Code based on http://jaspan.com/improved_persistent_login_cookie_best_practice * and http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/ */ class PlgAuthenticationCookie extends JPlugin { /** * Application object * * @var JApplicationCms * @since 3.2 */ protected $app; /** * Database object * * @var JDatabaseDriver * @since 3.2 */ protected $db; /** * Reports the privacy related capabilities for this plugin to site administrators. * * @return array * * @since 3.9.0 */ public function onPrivacyCollectAdminCapabilities() { $this->loadLanguage(); return array( JText::_('PLG_AUTHENTICATION_COOKIE') => array( JText::_('PLG_AUTH_COOKIE_PRIVACY_CAPABILITY_COOKIE'), ) ); } /** * This method should handle any authentication and report back to the subject * * @param array $credentials Array holding the user credentials * @param array $options Array of extra options * @param object &$response Authentication response object * * @return boolean * * @since 3.2 */ public function onUserAuthenticate($credentials, $options, &$response) { // No remember me for admin if ($this->app->isClient('administrator')) { return false; } // Get cookie $cookieName = 'joomla_remember_me_' . JUserHelper::getShortHashedUserAgent(); $cookieValue = $this->app->input->cookie->get($cookieName); // Try with old cookieName (pre 3.6.0) if not found if (!$cookieValue) { $cookieName = JUserHelper::getShortHashedUserAgent(); $cookieValue = $this->app->input->cookie->get($cookieName); } if (!$cookieValue) { return false; } $cookieArray = explode('.', $cookieValue); // Check for valid cookie value if (count($cookieArray) !== 2) { // Destroy the cookie in the browser. $this->app->input->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); JLog::add('Invalid cookie detected.', JLog::WARNING, 'error'); return false; } $response->type = 'Cookie'; // Filter series since we're going to use it in the query $filter = new JFilterInput; $series = $filter->clean($cookieArray[1], 'ALNUM'); // Remove expired tokens $query = $this->db->getQuery(true) ->delete('#__user_keys') ->where($this->db->quoteName('time') . ' < ' . $this->db->quote(time())); try { $this->db->setQuery($query)->execute(); } catch (RuntimeException $e) { // We aren't concerned with errors from this query, carry on } // Find the matching record if it exists. $query = $this->db->getQuery(true) ->select($this->db->quoteName(array('user_id', 'token', 'series', 'time'))) ->from($this->db->quoteName('#__user_keys')) ->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series)) ->where($this->db->quoteName('uastring') . ' = ' . $this->db->quote($cookieName)) ->order($this->db->quoteName('time') . ' DESC'); try { $results = $this->db->setQuery($query)->loadObjectList(); } catch (RuntimeException $e) { $response->status = JAuthentication::STATUS_FAILURE; return false; } if (count($results) !== 1) { // Destroy the cookie in the browser. $this->app->input->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); $response->status = JAuthentication::STATUS_FAILURE; return false; } // We have a user with one cookie with a valid series and a corresponding record in the database. if (!JUserHelper::verifyPassword($cookieArray[0], $results[0]->token)) { /* * This is a real attack! * Either the series was guessed correctly or a cookie was stolen and used twice (once by attacker and once by victim). * Delete all tokens for this user! */ $query = $this->db->getQuery(true) ->delete('#__user_keys') ->where($this->db->quoteName('user_id') . ' = ' . $this->db->quote($results[0]->user_id)); try { $this->db->setQuery($query)->execute(); } catch (RuntimeException $e) { // Log an alert for the site admin JLog::add( sprintf('Failed to delete cookie token for user %s with the following error: %s', $results[0]->user_id, $e->getMessage()), JLog::WARNING, 'security' ); } // Destroy the cookie in the browser. $this->app->input->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); // Issue warning by email to user and/or admin? JLog::add(JText::sprintf('PLG_AUTH_COOKIE_ERROR_LOG_LOGIN_FAILED', $results[0]->user_id), JLog::WARNING, 'security'); $response->status = JAuthentication::STATUS_FAILURE; return false; } // Make sure there really is a user with this name and get the data for the session. $query = $this->db->getQuery(true) ->select($this->db->quoteName(array('id', 'username', 'password'))) ->from($this->db->quoteName('#__users')) ->where($this->db->quoteName('username') . ' = ' . $this->db->quote($results[0]->user_id)) ->where($this->db->quoteName('requireReset') . ' = 0'); try { $result = $this->db->setQuery($query)->loadObject(); } catch (RuntimeException $e) { $response->status = JAuthentication::STATUS_FAILURE; return false; } if ($result) { // Bring this in line with the rest of the system $user = JUser::getInstance($result->id); // Set response data. $response->username = $result->username; $response->email = $user->email; $response->fullname = $user->name; $response->password = $result->password; $response->language = $user->getParam('language'); // Set response status. $response->status = JAuthentication::STATUS_SUCCESS; $response->error_message = ''; } else { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_NO_USER'); } } /** * We set the authentication cookie only after login is successfully finished. * We set a new cookie either for a user with no cookies or one * where the user used a cookie to authenticate. * * @param array $options Array holding options * * @return boolean True on success * * @since 3.2 */ public function onUserAfterLogin($options) { // No remember me for admin if ($this->app->isClient('administrator')) { return false; } if (isset($options['responseType']) && $options['responseType'] === 'Cookie') { // Logged in using a cookie $cookieName = 'joomla_remember_me_' . JUserHelper::getShortHashedUserAgent(); // We need the old data to get the existing series $cookieValue = $this->app->input->cookie->get($cookieName); // Try with old cookieName (pre 3.6.0) if not found if (!$cookieValue) { $oldCookieName = JUserHelper::getShortHashedUserAgent(); $cookieValue = $this->app->input->cookie->get($oldCookieName); // Destroy the old cookie in the browser $this->app->input->cookie->set($oldCookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); } $cookieArray = explode('.', $cookieValue); // Filter series since we're going to use it in the query $filter = new JFilterInput; $series = $filter->clean($cookieArray[1], 'ALNUM'); } elseif (!empty($options['remember'])) { // Remember checkbox is set $cookieName = 'joomla_remember_me_' . JUserHelper::getShortHashedUserAgent(); // Create a unique series which will be used over the lifespan of the cookie $unique = false; $errorCount = 0; do { $series = JUserHelper::genRandomPassword(20); $query = $this->db->getQuery(true) ->select($this->db->quoteName('series')) ->from($this->db->quoteName('#__user_keys')) ->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series)); try { $results = $this->db->setQuery($query)->loadResult(); if ($results === null) { $unique = true; } } catch (RuntimeException $e) { $errorCount++; // We'll let this query fail up to 5 times before giving up, there's probably a bigger issue at this point if ($errorCount === 5) { return false; } } } while ($unique === false); } else { return false; } // Get the parameter values $lifetime = $this->params->get('cookie_lifetime', 60) * 24 * 60 * 60; $length = $this->params->get('key_length', 16); // Generate new cookie $token = JUserHelper::genRandomPassword($length); $cookieValue = $token . '.' . $series; // Overwrite existing cookie with new value $this->app->input->cookie->set( $cookieName, $cookieValue, time() + $lifetime, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', ''), $this->app->isHttpsForced(), true ); $query = $this->db->getQuery(true); if (!empty($options['remember'])) { // Create new record $query ->insert($this->db->quoteName('#__user_keys')) ->set($this->db->quoteName('user_id') . ' = ' . $this->db->quote($options['user']->username)) ->set($this->db->quoteName('series') . ' = ' . $this->db->quote($series)) ->set($this->db->quoteName('uastring') . ' = ' . $this->db->quote($cookieName)) ->set($this->db->quoteName('time') . ' = ' . (time() + $lifetime)); } else { // Update existing record with new token $query ->update($this->db->quoteName('#__user_keys')) ->where($this->db->quoteName('user_id') . ' = ' . $this->db->quote($options['user']->username)) ->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series)) ->where($this->db->quoteName('uastring') . ' = ' . $this->db->quote($cookieName)); } $hashedToken = JUserHelper::hashPassword($token); $query->set($this->db->quoteName('token') . ' = ' . $this->db->quote($hashedToken)); try { $this->db->setQuery($query)->execute(); } catch (RuntimeException $e) { return false; } return true; } /** * This is where we delete any authentication cookie when a user logs out * * @param array $options Array holding options (length, timeToExpiration) * * @return boolean True on success * * @since 3.2 */ public function onUserAfterLogout($options) { // No remember me for admin if ($this->app->isClient('administrator')) { return false; } $cookieName = 'joomla_remember_me_' . JUserHelper::getShortHashedUserAgent(); $cookieValue = $this->app->input->cookie->get($cookieName); // There are no cookies to delete. if (!$cookieValue) { return true; } $cookieArray = explode('.', $cookieValue); // Filter series since we're going to use it in the query $filter = new JFilterInput; $series = $filter->clean($cookieArray[1], 'ALNUM'); // Remove the record from the database $query = $this->db->getQuery(true) ->delete('#__user_keys') ->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series)); try { $this->db->setQuery($query)->execute(); } catch (RuntimeException $e) { // We aren't concerned with errors from this query, carry on } // Destroy the cookie $this->app->input->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); return true; } } authentication/cookie/cookie.xml 0000644 00000002772 14735702223 0013044 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.2" type="plugin" group="authentication" method="upgrade"> <name>plg_authentication_cookie</name> <author>Joomla! Project</author> <creationDate>July 2013</creationDate> <copyright>(C) 2013 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_AUTH_COOKIE_XML_DESCRIPTION</description> <files> <filename plugin="cookie">cookie.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_authentication_cookie.ini</language> <language tag="en-GB">en-GB.plg_authentication_cookie.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="cookie_lifetime" type="number" label="PLG_AUTH_COOKIE_FIELD_COOKIE_LIFETIME_LABEL" description="PLG_AUTH_COOKIE_FIELD_COOKIE_LIFETIME_DESC" default="60" filter="integer" required="true" /> <field name="key_length" type="list" label="PLG_AUTH_COOKIE_FIELD_KEY_LENGTH_LABEL" description="PLG_AUTH_COOKIE_FIELD_KEY_LENGTH_DESC" default="16" filter="integer" required="true" > <option value="8">8</option> <option value="16">16</option> <option value="32">32</option> <option value="64">64</option> </field> </fieldset> </fields> </config> </extension> authentication/gmail/gmail.php 0000644 00000014535 14735702223 0012473 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Authentication.gmail * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\CMS\Authentication\AuthenticationResponse; use Joomla\Registry\Registry; /** * GMail Authentication Plugin * * @since 1.5 */ class PlgAuthenticationGMail extends JPlugin { /** * This method should handle any authentication and report back to the subject * * @param array $credentials Array holding the user credentials * @param array $options Array of extra options * @param AuthenticationResponse &$response Authentication response object * * @return void * * @since 1.5 */ public function onUserAuthenticate($credentials, $options, &$response) { // Load plugin language $this->loadLanguage(); // No backend authentication if (JFactory::getApplication()->isClient('administrator') && !$this->params->get('backendLogin', 0)) { return; } $success = false; $curlParams = array( 'follow_location' => true, 'transport.curl' => array( CURLOPT_SSL_VERIFYPEER => $this->params->get('verifypeer', 1) ), ); $transportParams = new Registry($curlParams); try { $http = JHttpFactory::getHttp($transportParams, 'curl'); } catch (RuntimeException $e) { $response->status = JAuthentication::STATUS_FAILURE; $response->type = 'GMail'; $response->error_message = JText::sprintf('JGLOBAL_AUTH_FAILED', JText::_('JGLOBAL_AUTH_CURL_NOT_INSTALLED')); return; } // Check if we have a username and password if ($credentials['username'] === '' || $credentials['password'] === '') { $response->type = 'GMail'; $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::sprintf('JGLOBAL_AUTH_FAILED', JText::_('JGLOBAL_AUTH_USER_BLACKLISTED')); return; } $blacklist = explode(',', $this->params->get('user_blacklist', '')); // Check if the username isn't blacklisted if (in_array($credentials['username'], $blacklist)) { $response->type = 'GMail'; $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::sprintf('JGLOBAL_AUTH_FAILED', JText::_('JGLOBAL_AUTH_USER_BLACKLISTED')); return; } $suffix = $this->params->get('suffix', ''); $applysuffix = $this->params->get('applysuffix', 0); $offset = strpos($credentials['username'], '@'); // Check if we want to do suffix stuff, typically for Google Apps for Your Domain if ($suffix && $applysuffix) { if ($applysuffix == 1 && $offset === false) { // Apply suffix if missing $credentials['username'] .= '@' . $suffix; } elseif ($applysuffix == 2) { // Always use suffix if ($offset) { // If we already have an @, get rid of it and replace it $credentials['username'] = substr($credentials['username'], 0, $offset); } $credentials['username'] .= '@' . $suffix; } } $headers = array( 'Authorization' => 'Basic ' . base64_encode($credentials['username'] . ':' . $credentials['password']) ); try { $result = $http->get('https://mail.google.com/mail/feed/atom', $headers); } catch (Exception $e) { $response->status = JAuthentication::STATUS_FAILURE; $response->type = 'GMail'; $response->error_message = JText::sprintf('JGLOBAL_AUTH_FAILED', JText::_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED')); return; } $code = $result->code; switch ($code) { case 200 : $message = JText::_('JGLOBAL_AUTH_ACCESS_GRANTED'); $success = true; break; case 401 : $message = JText::_('JGLOBAL_AUTH_ACCESS_DENIED'); break; default : $message = JText::_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED'); break; } $response->type = 'GMail'; if (!$success) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::sprintf('JGLOBAL_AUTH_FAILED', $message); return; } if (strpos($credentials['username'], '@') === false) { if ($suffix) { // If there is a suffix then we want to apply it $email = $credentials['username'] . '@' . $suffix; } else { // If there isn't a suffix just use the default gmail one $email = $credentials['username'] . '@gmail.com'; } } else { // The username looks like an email address (probably is) so use that $email = $credentials['username']; } // Extra security checks with existing local accounts $db = JFactory::getDbo(); $localUsernameChecks = array(strstr($email, '@', true), $email); $query = $db->getQuery(true) ->select('id, activation, username, email, block') ->from('#__users') ->where('username IN(' . implode(',', array_map(array($db, 'quote'), $localUsernameChecks)) . ')' . ' OR email = ' . $db->quote($email) ); $db->setQuery($query); if ($localUsers = $db->loadObjectList()) { foreach ($localUsers as $localUser) { // Local user exists with same username but different email address if ($email !== $localUser->email) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::sprintf('JGLOBAL_AUTH_FAILED', JText::_('PLG_GMAIL_ERROR_LOCAL_USERNAME_CONFLICT')); return; } else { // Existing user disabled locally if ($localUser->block || !empty($localUser->activation)) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_ACCESS_DENIED'); return; } // We will always keep the local username for existing accounts $credentials['username'] = $localUser->username; break; } } } elseif (JFactory::getApplication()->isClient('administrator')) { // We wont' allow backend access without local account $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JERROR_LOGIN_DENIED'); return; } $response->status = JAuthentication::STATUS_SUCCESS; $response->error_message = ''; $response->email = $email; // Reset the username to what we ended up using $response->username = $credentials['username']; $response->fullname = $credentials['username']; } } authentication/gmail/gmail.xml 0000644 00000004454 14735702223 0012503 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="authentication" method="upgrade"> <name>plg_authentication_gmail</name> <author>Joomla! Project</author> <creationDate>February 2006</creationDate> <copyright>(C) 2006 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_GMAIL_XML_DESCRIPTION</description> <files> <filename plugin="gmail">gmail.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_authentication_gmail.ini</language> <language tag="en-GB">en-GB.plg_authentication_gmail.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="applysuffix" type="list" label="PLG_GMAIL_FIELD_APPLYSUFFIX_LABEL" description="PLG_GMAIL_FIELD_APPLYSUFFIX_DESC" default="0" filter="integer" > <option value="0">PLG_GMAIL_FIELD_VALUE_NOAPPLYSUFFIX</option> <option value="1">PLG_GMAIL_FIELD_VALUE_APPLYSUFFIXMISSING</option> <option value="2">PLG_GMAIL_FIELD_VALUE_APPLYSUFFIXALWAYS</option> </field> <field name="suffix" type="text" label="PLG_GMAIL_FIELD_SUFFIX_LABEL" description="PLG_GMAIL_FIELD_SUFFIX_DESC" size="20" showon="applysuffix:1,2" /> <field name="verifypeer" type="radio" label="PLG_GMAIL_FIELD_VERIFYPEER_LABEL" description="PLG_GMAIL_FIELD_VERIFYPEER_DESC" default="1" filter="integer" class="btn-group btn-group-yesno" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="user_blacklist" type="text" label="PLG_GMAIL_FIELD_USER_BLACKLIST_LABEL" description="PLG_GMAIL_FIELD_USER_BLACKLIST_DESC" size="20" /> <field name="backendLogin" type="radio" label="PLG_GMAIL_FIELD_BACKEND_LOGIN_LABEL" description="PLG_GMAIL_FIELD_BACKEND_LOGIN_DESC" default="0" filter="integer" class="btn-group btn-group-yesno" > <option value="1">JENABLED</option> <option value="0">JDISABLED</option> </field> </fieldset> </fields> </config> </extension> authentication/joomla/joomla.php 0000644 00000013766 14735702223 0013060 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Authentication.joomla * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Joomla Authentication plugin * * @since 1.5 */ class PlgAuthenticationJoomla extends JPlugin { /** * This method should handle any authentication and report back to the subject * * @param array $credentials Array holding the user credentials * @param array $options Array of extra options * @param object &$response Authentication response object * * @return void * * @since 1.5 */ public function onUserAuthenticate($credentials, $options, &$response) { $response->type = 'Joomla'; // Joomla does not like blank passwords if (empty($credentials['password'])) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED'); return; } // Get a database object $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('id, password') ->from('#__users') ->where('username=' . $db->quote($credentials['username'])); $db->setQuery($query); $result = $db->loadObject(); if ($result) { $match = JUserHelper::verifyPassword($credentials['password'], $result->password, $result->id); if ($match === true) { // Bring this in line with the rest of the system $user = JUser::getInstance($result->id); $response->email = $user->email; $response->fullname = $user->name; if (JFactory::getApplication()->isClient('administrator')) { $response->language = $user->getParam('admin_language'); } else { $response->language = $user->getParam('language'); } $response->status = JAuthentication::STATUS_SUCCESS; $response->error_message = ''; } else { // Invalid password $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_INVALID_PASS'); } } else { // Let's hash the entered password even if we don't have a matching user for some extra response time // By doing so, we mitigate side channel user enumeration attacks JUserHelper::hashPassword($credentials['password']); // Invalid user $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_NO_USER'); } // Check the two factor authentication if ($response->status === JAuthentication::STATUS_SUCCESS) { $methods = JAuthenticationHelper::getTwoFactorMethods(); if (count($methods) <= 1) { // No two factor authentication method is enabled return; } JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_users/models', 'UsersModel'); /** @var UsersModelUser $model */ $model = JModelLegacy::getInstance('User', 'UsersModel', array('ignore_request' => true)); // Load the user's OTP (one time password, a.k.a. two factor auth) configuration if (!array_key_exists('otp_config', $options)) { $otpConfig = $model->getOtpConfig($result->id); $options['otp_config'] = $otpConfig; } else { $otpConfig = $options['otp_config']; } // Check if the user has enabled two factor authentication if (empty($otpConfig->method) || ($otpConfig->method === 'none')) { // Warn the user if they are using a secret code but they have not // enabled two factor auth in their account. if (!empty($credentials['secretkey'])) { try { $app = JFactory::getApplication(); $this->loadLanguage(); $app->enqueueMessage(JText::_('PLG_AUTH_JOOMLA_ERR_SECRET_CODE_WITHOUT_TFA'), 'warning'); } catch (Exception $exc) { // This happens when we are in CLI mode. In this case // no warning is issued return; } } return; } // Try to validate the OTP FOFPlatform::getInstance()->importPlugin('twofactorauth'); $otpAuthReplies = FOFPlatform::getInstance()->runPlugins('onUserTwofactorAuthenticate', array($credentials, $options)); $check = false; /* * This looks like noob code but DO NOT TOUCH IT and do not convert * to in_array(). During testing in_array() inexplicably returned * null when the OTEP begins with a zero! o_O */ if (!empty($otpAuthReplies)) { foreach ($otpAuthReplies as $authReply) { $check = $check || $authReply; } } // Fall back to one time emergency passwords if (!$check) { // Did the user use an OTEP instead? if (empty($otpConfig->otep)) { if (empty($otpConfig->method) || ($otpConfig->method === 'none')) { // Two factor authentication is not enabled on this account. // Any string is assumed to be a valid OTEP. return; } else { /* * Two factor authentication enabled and no OTEPs defined. The * user has used them all up. Therefore anything they enter is * an invalid OTEP. */ $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_INVALID_SECRETKEY'); return; } } // Clean up the OTEP (remove dashes, spaces and other funny stuff // our beloved users may have unwittingly stuffed in it) $otep = $credentials['secretkey']; $otep = filter_var($otep, FILTER_SANITIZE_NUMBER_INT); $otep = str_replace('-', '', $otep); $check = false; // Did we find a valid OTEP? if (in_array($otep, $otpConfig->otep)) { // Remove the OTEP from the array $otpConfig->otep = array_diff($otpConfig->otep, array($otep)); $model->setOtpConfig($result->id, $otpConfig); // Return true; the OTEP was a valid one $check = true; } } if (!$check) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_INVALID_SECRETKEY'); } } } } authentication/joomla/joomla.xml 0000644 00000001444 14735702223 0013057 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="authentication" method="upgrade"> <name>plg_authentication_joomla</name> <author>Joomla! Project</author> <creationDate>November 2005</creationDate> <copyright>(C) 2005 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_AUTH_JOOMLA_XML_DESCRIPTION</description> <files> <filename plugin="joomla">joomla.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_authentication_joomla.ini</language> <language tag="en-GB">en-GB.plg_authentication_joomla.sys.ini</language> </languages> </extension> authentication/ldap/ldap.php 0000644 00000011767 14735702223 0012155 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Authentication.ldap * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\Ldap\LdapClient; /** * LDAP Authentication Plugin * * @since 1.5 */ class PlgAuthenticationLdap extends JPlugin { /** * This method should handle any authentication and report back to the subject * * @param array $credentials Array holding the user credentials * @param array $options Array of extra options * @param object &$response Authentication response object * * @return boolean * * @since 1.5 */ public function onUserAuthenticate($credentials, $options, &$response) { $userdetails = null; $success = 0; $userdetails = array(); // For JLog $response->type = 'LDAP'; // Strip null bytes from the password $credentials['password'] = str_replace(chr(0), '', $credentials['password']); // LDAP does not like Blank passwords (tries to Anon Bind which is bad) if (empty($credentials['password'])) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED'); return false; } // Load plugin params info $ldap_email = $this->params->get('ldap_email'); $ldap_fullname = $this->params->get('ldap_fullname'); $ldap_uid = $this->params->get('ldap_uid'); $auth_method = $this->params->get('auth_method'); $ldap = new LdapClient($this->params); if (!$ldap->connect()) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_NOT_CONNECT'); return; } switch ($auth_method) { case 'search': { // Bind using Connect Username/password // Force anon bind to mitigate misconfiguration like [#7119] if ($this->params->get('username', '') !== '') { $bindtest = $ldap->bind(); } else { $bindtest = $ldap->anonymous_bind(); } if ($bindtest) { // Search for users DN $binddata = $this->searchByString( str_replace( '[search]', str_replace(';', '\3b', $ldap->escape($credentials['username'], null, LDAP_ESCAPE_FILTER)), $this->params->get('search_string') ), $ldap ); if (isset($binddata[0], $binddata[0]['dn'])) { // Verify Users Credentials $success = $ldap->bind($binddata[0]['dn'], $credentials['password'], 1); // Get users details $userdetails = $binddata; } else { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_NO_USER'); } } else { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_NOT_CONNECT'); } } break; case 'bind': { // We just accept the result here $success = $ldap->bind($ldap->escape($credentials['username'], null, LDAP_ESCAPE_DN), $credentials['password']); if ($success) { $userdetails = $this->searchByString( str_replace( '[search]', str_replace(';', '\3b', $ldap->escape($credentials['username'], null, LDAP_ESCAPE_FILTER)), $this->params->get('search_string') ), $ldap ); } else { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_INVALID_PASS'); } } break; } if (!$success) { $response->status = JAuthentication::STATUS_FAILURE; if ($response->error_message === '') { $response->error_message = JText::_('JGLOBAL_AUTH_INVALID_PASS'); } } else { // Grab some details from LDAP and return them if (isset($userdetails[0][$ldap_uid][0])) { $response->username = $userdetails[0][$ldap_uid][0]; } if (isset($userdetails[0][$ldap_email][0])) { $response->email = $userdetails[0][$ldap_email][0]; } if (isset($userdetails[0][$ldap_fullname][0])) { $response->fullname = $userdetails[0][$ldap_fullname][0]; } else { $response->fullname = $credentials['username']; } // Were good - So say so. $response->status = JAuthentication::STATUS_SUCCESS; $response->error_message = ''; } $ldap->close(); } /** * Shortcut method to build a LDAP search based on a semicolon separated string * * Note that this method requires that semicolons which should be part of the search term to be escaped * to correctly split the search string into separate lookups * * @param string $search search string of search values * @param LdapClient $ldap The LDAP client * * @return array Search results * * @since 3.8.2 */ private static function searchByString($search, LdapClient $ldap) { $results = explode(';', $search); foreach ($results as $key => $result) { $results[$key] = '(' . str_replace('\3b', ';', $result) . ')'; } return $ldap->search($results); } } authentication/ldap/ldap.xml 0000644 00000010756 14735702223 0012163 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="authentication" method="upgrade"> <name>plg_authentication_ldap</name> <author>Joomla! Project</author> <creationDate>November 2005</creationDate> <copyright>(C) 2005 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_LDAP_XML_DESCRIPTION</description> <files> <filename plugin="ldap">ldap.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_authentication_ldap.ini</language> <language tag="en-GB">en-GB.plg_authentication_ldap.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="host" type="text" label="PLG_LDAP_FIELD_HOST_LABEL" description="PLG_LDAP_FIELD_HOST_DESC" size="20" /> <field name="port" type="number" label="PLG_LDAP_FIELD_PORT_LABEL" description="PLG_LDAP_FIELD_PORT_DESC" min="1" max="65535" default="389" hint="389" validate="number" filter="integer" size="5" /> <field name="use_ldapV3" type="radio" label="PLG_LDAP_FIELD_V3_LABEL" description="PLG_LDAP_FIELD_V3_DESC" default="0" filter="integer" class="btn-group btn-group-yesno" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="negotiate_tls" type="radio" label="PLG_LDAP_FIELD_NEGOCIATE_LABEL" description="PLG_LDAP_FIELD_NEGOCIATE_DESC" default="0" filter="integer" class="btn-group btn-group-yesno" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="ignore_reqcert_tls" type="radio" label="PLG_LDAP_FIELD_IGNORE_REQCERT_TLS_LABEL" description="PLG_LDAP_FIELD_IGNORE_REQCERT_TLS_DESC" default="0" filter="integer" class="btn-group btn-group-yesno" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="no_referrals" type="radio" label="PLG_LDAP_FIELD_REFERRALS_LABEL" description="PLG_LDAP_FIELD_REFERRALS_DESC" default="0" filter="integer" class="btn-group btn-group-yesno" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="auth_method" type="list" label="PLG_LDAP_FIELD_AUTHMETHOD_LABEL" description="PLG_LDAP_FIELD_AUTHMETHOD_DESC" default="bind" > <option value="search">PLG_LDAP_FIELD_VALUE_BINDSEARCH</option> <option value="bind">PLG_LDAP_FIELD_VALUE_BINDUSER</option> </field> <field name="base_dn" type="text" label="PLG_LDAP_FIELD_BASEDN_LABEL" description="PLG_LDAP_FIELD_BASEDN_DESC" size="20" /> <field name="search_string" type="text" label="PLG_LDAP_FIELD_SEARCHSTRING_LABEL" description="PLG_LDAP_FIELD_SEARCHSTRING_DESC" size="20" /> <field name="users_dn" type="text" label="PLG_LDAP_FIELD_USERSDN_LABEL" description="PLG_LDAP_FIELD_USERSDN_DESC" size="20" /> <field name="username" type="text" label="PLG_LDAP_FIELD_USERNAME_LABEL" description="PLG_LDAP_FIELD_USERNAME_DESC" size="20" /> <field name="password" type="password" label="PLG_LDAP_FIELD_PASSWORD_LABEL" description="PLG_LDAP_FIELD_PASSWORD_DESC" size="20" /> <field name="ldap_fullname" type="text" label="PLG_LDAP_FIELD_FULLNAME_LABEL" description="PLG_LDAP_FIELD_FULLNAME_DESC" default="fullName" size="20" /> <field name="ldap_email" type="text" label="PLG_LDAP_FIELD_EMAIL_LABEL" description="PLG_LDAP_FIELD_EMAIL_DESC" default="mail" size="20" /> <field name="ldap_uid" type="text" label="PLG_LDAP_FIELD_UID_LABEL" description="PLG_LDAP_FIELD_UID_DESC" default="uid" size="20" /> <field name="ldap_debug" type="radio" label="PLG_LDAP_FIELD_LDAPDEBUG_LABEL" description="PLG_LDAP_FIELD_LDAPDEBUG_DESC" default="0" filter="integer" class="btn-group btn-group-yesno" > <option value="1">JYES</option> <option value="0">JNO</option> </field> </fieldset> </fields> </config> </extension> captcha/recaptcha/postinstall/actions.php 0000644 00000003026 14735702223 0014634 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Captcha * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt * * This file contains the functions used by the com_postinstall code to deliver * the necessary post-installation messages for the end of life of reCAPTCHA V1. */ /** * Checks if the plugin is enabled and reCAPTCHA V1 is being used. If true then the * message about reCAPTCHA v1 EOL should be displayed. * * @return boolean * * @since 3.8.6 */ function recaptcha_postinstall_condition() { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('1') ->from($db->qn('#__extensions')) ->where($db->qn('name') . ' = ' . $db->q('plg_captcha_recaptcha')) ->where($db->qn('enabled') . ' = 1') ->where($db->qn('params') . ' LIKE ' . $db->q('%1.0%')); $db->setQuery($query); $enabled_plugins = $db->loadObjectList(); return count($enabled_plugins) === 1; } /** * Open the reCAPTCHA plugin so that they can update the settings to V2 and new keys. * * @return void * * @since 3.8.6 */ function recaptcha_postinstall_action() { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('extension_id') ->from($db->qn('#__extensions')) ->where($db->qn('name') . ' = ' . $db->q('plg_captcha_recaptcha')); $db->setQuery($query); $e_id = $db->loadResult(); $url = 'index.php?option=com_plugins&task=plugin.edit&extension_id=' . $e_id; JFactory::getApplication()->redirect($url); } captcha/recaptcha/recaptcha.php 0000644 00000023126 14735702223 0012555 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Captcha * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\CMS\Captcha\Google\HttpBridgePostRequestMethod; use Joomla\Utilities\IpHelper; /** * Recaptcha Plugin * Based on the official recaptcha library( https://packagist.org/packages/google/recaptcha ) * * @since 2.5 */ class PlgCaptchaRecaptcha extends JPlugin { /** * Load the language file on instantiation. * * @var boolean * @since 3.1 */ protected $autoloadLanguage = true; /** * Reports the privacy related capabilities for this plugin to site administrators. * * @return array * * @since 3.9.0 */ public function onPrivacyCollectAdminCapabilities() { $this->loadLanguage(); return array( JText::_('PLG_CAPTCHA_RECAPTCHA') => array( JText::_('PLG_RECAPTCHA_PRIVACY_CAPABILITY_IP_ADDRESS'), ) ); } /** * Initialise the captcha * * @param string $id The id of the field. * * @return Boolean True on success, false otherwise * * @since 2.5 * @throws \RuntimeException */ public function onInit($id = 'dynamic_recaptcha_1') { $pubkey = $this->params->get('public_key', ''); if ($pubkey === '') { throw new \RuntimeException(JText::_('PLG_RECAPTCHA_ERROR_NO_PUBLIC_KEY')); } if ($this->params->get('version', '1.0') === '1.0') { JHtml::_('jquery.framework'); $theme = $this->params->get('theme', 'clean'); $file = 'https://www.google.com/recaptcha/api/js/recaptcha_ajax.js'; JHtml::_('script', $file); JFactory::getDocument()->addScriptDeclaration('jQuery( document ).ready(function() {Recaptcha.create("' . $pubkey . '", "' . $id . '", {theme: "' . $theme . '",' . $this->_getLanguage() . 'tabindex: 0});});'); } else { // Load callback first for browser compatibility JHtml::_('script', 'plg_captcha_recaptcha/recaptcha.min.js', array('version' => 'auto', 'relative' => true)); $file = 'https://www.google.com/recaptcha/api.js?onload=JoomlaInitReCaptcha2&render=explicit&hl=' . JFactory::getLanguage()->getTag(); JHtml::_('script', $file); } return true; } /** * Gets the challenge HTML * * @param string $name The name of the field. Not Used. * @param string $id The id of the field. * @param string $class The class of the field. * * @return string The HTML to be embedded in the form. * * @since 2.5 */ public function onDisplay($name = null, $id = 'dynamic_recaptcha_1', $class = '') { $dom = new \DOMDocument('1.0', 'UTF-8'); $ele = $dom->createElement('div'); $ele->setAttribute('id', $id); if ($this->params->get('version', '1.0') === '1.0') { $ele->setAttribute('class', $class); } else { $ele->setAttribute('class', ((trim($class) == '') ? 'g-recaptcha' : ($class . ' g-recaptcha'))); $ele->setAttribute('data-sitekey', $this->params->get('public_key', '')); $ele->setAttribute('data-theme', $this->params->get('theme2', 'light')); $ele->setAttribute('data-size', $this->params->get('size', 'normal')); $ele->setAttribute('data-tabindex', $this->params->get('tabindex', '0')); $ele->setAttribute('data-callback', $this->params->get('callback', '')); $ele->setAttribute('data-expired-callback', $this->params->get('expired_callback', '')); $ele->setAttribute('data-error-callback', $this->params->get('error_callback', '')); } $dom->appendChild($ele); return $dom->saveHTML($ele); } /** * Calls an HTTP POST function to verify if the user's guess was correct * * @param string $code Answer provided by user. Not needed for the Recaptcha implementation * * @return True if the answer is correct, false otherwise * * @since 2.5 * @throws \RuntimeException */ public function onCheckAnswer($code = null) { $input = \JFactory::getApplication()->input; $privatekey = $this->params->get('private_key'); $version = $this->params->get('version', '1.0'); $remoteip = IpHelper::getIp(); switch ($version) { case '1.0': $challenge = $input->get('recaptcha_challenge_field', '', 'string'); $response = $code ? $code : $input->get('recaptcha_response_field', '', 'string'); $spam = ($challenge === '' || $response === ''); break; case '2.0': // Challenge Not needed in 2.0 but needed for getResponse call $challenge = null; $response = $code ? $code : $input->get('g-recaptcha-response', '', 'string'); $spam = ($response === ''); break; } // Check for Private Key if (empty($privatekey)) { throw new \RuntimeException(JText::_('PLG_RECAPTCHA_ERROR_NO_PRIVATE_KEY')); } // Check for IP if (empty($remoteip)) { throw new \RuntimeException(JText::_('PLG_RECAPTCHA_ERROR_NO_IP')); } // Discard spam submissions if ($spam) { throw new \RuntimeException(JText::_('PLG_RECAPTCHA_ERROR_EMPTY_SOLUTION')); } return $this->getResponse($privatekey, $remoteip, $response, $challenge); } /** * Get the reCaptcha response. * * @param string $privatekey The private key for authentication. * @param string $remoteip The remote IP of the visitor. * @param string $response The response received from Google. * @param string $challenge The challenge field from the reCaptcha. Only for 1.0 * * @return bool True if response is good | False if response is bad. * * @since 3.4 * @throws \RuntimeException */ private function getResponse($privatekey, $remoteip, $response, $challenge = null) { $version = $this->params->get('version', '1.0'); switch ($version) { case '1.0': $response = $this->_recaptcha_http_post( 'www.google.com', '/recaptcha/api/verify', array( 'privatekey' => $privatekey, 'remoteip' => $remoteip, 'challenge' => $challenge, 'response' => $response ) ); $answers = explode("\n", $response[1]); if (trim($answers[0]) !== 'true') { // @todo use exceptions here $this->_subject->setError(JText::_('PLG_RECAPTCHA_ERROR_' . strtoupper(str_replace('-', '_', $answers[1])))); return false; } break; case '2.0': $reCaptcha = new \ReCaptcha\ReCaptcha($privatekey, new HttpBridgePostRequestMethod); $response = $reCaptcha->verify($response, $remoteip); if (!$response->isSuccess()) { foreach ($response->getErrorCodes() as $error) { throw new \RuntimeException($error); } return false; } break; } return true; } /** * Encodes the given data into a query string format. * * @param array $data Array of string elements to be encoded * * @return string Encoded request * * @since 2.5 */ private function _recaptcha_qsencode($data) { $req = ''; foreach ($data as $key => $value) { $req .= $key . '=' . urlencode(stripslashes($value)) . '&'; } // Cut the last '&' $req = rtrim($req, '&'); return $req; } /** * Submits an HTTP POST to a reCAPTCHA server. * * @param string $host Host name to POST to. * @param string $path Path on host to POST to. * @param array $data Data to be POSTed. * @param int $port Optional port number on host. * * @return array Response * * @since 2.5 */ private function _recaptcha_http_post($host, $path, $data, $port = 80) { $req = $this->_recaptcha_qsencode($data); $http_request = "POST $path HTTP/1.0\r\n"; $http_request .= "Host: $host\r\n"; $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; $http_request .= "Content-Length: " . strlen($req) . "\r\n"; $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; $http_request .= "\r\n"; $http_request .= $req; $response = ''; if (($fs = @fsockopen($host, $port, $errno, $errstr, 10)) === false) { die('Could not open socket'); } fwrite($fs, $http_request); while (!feof($fs)) { // One TCP-IP packet $response .= fgets($fs, 1160); } fclose($fs); $response = explode("\r\n\r\n", $response, 2); return $response; } /** * Get the language tag or a custom translation * * @return string * * @since 2.5 */ private function _getLanguage() { $language = JFactory::getLanguage(); $tag = explode('-', $language->getTag()); $tag = $tag[0]; $available = array('en', 'pt', 'fr', 'de', 'nl', 'ru', 'es', 'tr'); if (in_array($tag, $available)) { return "lang : '" . $tag . "',"; } // If the default language is not available, let's search for a custom translation if ($language->hasKey('PLG_RECAPTCHA_CUSTOM_LANG')) { $custom[] = 'custom_translations : {'; $custom[] = "\t" . 'instructions_visual : "' . JText::_('PLG_RECAPTCHA_INSTRUCTIONS_VISUAL') . '",'; $custom[] = "\t" . 'instructions_audio : "' . JText::_('PLG_RECAPTCHA_INSTRUCTIONS_AUDIO') . '",'; $custom[] = "\t" . 'play_again : "' . JText::_('PLG_RECAPTCHA_PLAY_AGAIN') . '",'; $custom[] = "\t" . 'cant_hear_this : "' . JText::_('PLG_RECAPTCHA_CANT_HEAR_THIS') . '",'; $custom[] = "\t" . 'visual_challenge : "' . JText::_('PLG_RECAPTCHA_VISUAL_CHALLENGE') . '",'; $custom[] = "\t" . 'audio_challenge : "' . JText::_('PLG_RECAPTCHA_AUDIO_CHALLENGE') . '",'; $custom[] = "\t" . 'refresh_btn : "' . JText::_('PLG_RECAPTCHA_REFRESH_BTN') . '",'; $custom[] = "\t" . 'help_btn : "' . JText::_('PLG_RECAPTCHA_HELP_BTN') . '",'; $custom[] = "\t" . 'incorrect_try_again : "' . JText::_('PLG_RECAPTCHA_INCORRECT_TRY_AGAIN') . '",'; $custom[] = '},'; $custom[] = "lang : '" . $tag . "',"; return implode("\n", $custom); } // If nothing helps fall back to english return ''; } } captcha/recaptcha/recaptcha.xml 0000644 00000007423 14735702223 0012570 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.4" type="plugin" group="captcha" method="upgrade"> <name>plg_captcha_recaptcha</name> <version>3.4.0</version> <creationDate>December 2011</creationDate> <author>Joomla! Project</author> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <copyright>(C) 2011 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <description>PLG_CAPTCHA_RECAPTCHA_XML_DESCRIPTION</description> <files> <filename plugin="recaptcha">recaptcha.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_captcha_recaptcha.ini</language> <language tag="en-GB">en-GB.plg_captcha_recaptcha.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="message" type="note" label="PLG_RECAPTCHA_VERSION_1_WARNING_LABEL" showon="version:1.0" /> <field name="version" type="list" label="PLG_RECAPTCHA_VERSION_LABEL" description="PLG_RECAPTCHA_VERSION_DESC" default="2.0" size="1" > <option value="1.0">PLG_RECAPTCHA_VERSION_V1</option> <option value="2.0">PLG_RECAPTCHA_VERSION_V2</option> </field> <field name="public_key" type="text" label="PLG_RECAPTCHA_PUBLIC_KEY_LABEL" description="PLG_RECAPTCHA_PUBLIC_KEY_DESC" default="" required="true" filter="string" size="100" class="input-xxlarge" /> <field name="private_key" type="text" label="PLG_RECAPTCHA_PRIVATE_KEY_LABEL" description="PLG_RECAPTCHA_PRIVATE_KEY_DESC" default="" required="true" filter="string" size="100" class="input-xxlarge" /> <field name="theme" type="list" label="PLG_RECAPTCHA_THEME_LABEL" description="PLG_RECAPTCHA_THEME_DESC" default="clean" showon="version:1.0" filter="" > <option value="clean">PLG_RECAPTCHA_THEME_CLEAN</option> <option value="white">PLG_RECAPTCHA_THEME_WHITE</option> <option value="blackglass">PLG_RECAPTCHA_THEME_BLACKGLASS</option> <option value="red">PLG_RECAPTCHA_THEME_RED</option> </field> <field name="theme2" type="list" label="PLG_RECAPTCHA_THEME_LABEL" description="PLG_RECAPTCHA_THEME_DESC" default="light" showon="version:2.0" filter="" > <option value="light">PLG_RECAPTCHA_THEME_LIGHT</option> <option value="dark">PLG_RECAPTCHA_THEME_DARK</option> </field> <field name="size" type="list" label="PLG_RECAPTCHA_SIZE_LABEL" description="PLG_RECAPTCHA_SIZE_DESC" default="normal" showon="version:2.0" filter="" > <option value="normal">PLG_RECAPTCHA_THEME_NORMAL</option> <option value="compact">PLG_RECAPTCHA_THEME_COMPACT</option> </field> <field name="tabindex" type="number" label="PLG_RECAPTCHA_TABINDEX_LABEL" description="PLG_RECAPTCHA_TABINDEX_DESC" default="0" showon="version:2.0" min="0" /> <field name="callback" type="text" label="PLG_RECAPTCHA_CALLBACK_LABEL" description="PLG_RECAPTCHA_CALLBACK_DESC" default="" showon="version:2.0" filter="string" /> <field name="expired_callback" type="text" label="PLG_RECAPTCHA_EXPIRED_CALLBACK_LABEL" description="PLG_RECAPTCHA_EXPIRED_CALLBACK_DESC" default="" showon="version:2.0" filter="string" /> <field name="error_callback" type="text" label="PLG_RECAPTCHA_ERROR_CALLBACK_LABEL" description="PLG_RECAPTCHA_ERROR_CALLBACK_DESC" default="" showon="version:2.0" filter="string" /> </fieldset> </fields> </config> </extension> captcha/recaptcha_invisible/recaptcha_invisible.php 0000644 00000012704 14735702223 0016665 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Captcha * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\CMS\Captcha\Google\HttpBridgePostRequestMethod; use Joomla\Utilities\IpHelper; /** * Invisible reCAPTCHA Plugin. * * @since 3.9.0 */ class PlgCaptchaRecaptcha_Invisible extends \JPlugin { /** * Load the language file on instantiation. * * @var boolean * @since 3.9.0 */ protected $autoloadLanguage = true; /** * Reports the privacy related capabilities for this plugin to site administrators. * * @return array * * @since 3.9.0 */ public function onPrivacyCollectAdminCapabilities() { $this->loadLanguage(); return array( JText::_('PLG_CAPTCHA_RECAPTCHA_INVISIBLE') => array( JText::_('PLG_RECAPTCHA_INVISIBLE_PRIVACY_CAPABILITY_IP_ADDRESS'), ) ); } /** * Initialise the captcha * * @param string $id The id of the field. * * @return boolean True on success, false otherwise * * @since 3.9.0 * @throws \RuntimeException */ public function onInit($id = 'dynamic_recaptcha_invisible_1') { $pubkey = $this->params->get('public_key', ''); if ($pubkey === '') { throw new \RuntimeException(JText::_('PLG_RECAPTCHA_INVISIBLE_ERROR_NO_PUBLIC_KEY')); } // Load callback first for browser compatibility \JHtml::_( 'script', 'plg_captcha_recaptcha_invisible/recaptcha.min.js', array('version' => 'auto', 'relative' => true), array('async' => 'async', 'defer' => 'defer') ); // Load Google reCAPTCHA api js $file = 'https://www.google.com/recaptcha/api.js' . '?onload=JoomlaInitReCaptchaInvisible' . '&render=explicit' . '&hl=' . \JFactory::getLanguage()->getTag(); \JHtml::_( 'script', $file, array(), array('async' => 'async', 'defer' => 'defer') ); return true; } /** * Gets the challenge HTML * * @param string $name The name of the field. Not Used. * @param string $id The id of the field. * @param string $class The class of the field. * * @return string The HTML to be embedded in the form. * * @since 3.9.0 */ public function onDisplay($name = null, $id = 'dynamic_recaptcha_invisible_1', $class = '') { $dom = new \DOMDocument('1.0', 'UTF-8'); $ele = $dom->createElement('div'); $ele->setAttribute('id', $id); $ele->setAttribute('class', ((trim($class) == '') ? 'g-recaptcha' : ($class . ' g-recaptcha'))); $ele->setAttribute('data-sitekey', $this->params->get('public_key', '')); $ele->setAttribute('data-badge', $this->params->get('badge', 'bottomright')); $ele->setAttribute('data-size', 'invisible'); $ele->setAttribute('data-tabindex', $this->params->get('tabindex', '0')); $ele->setAttribute('data-callback', $this->params->get('callback', '')); $ele->setAttribute('data-expired-callback', $this->params->get('expired_callback', '')); $ele->setAttribute('data-error-callback', $this->params->get('error_callback', '')); $dom->appendChild($ele); return $dom->saveHTML($ele); } /** * Calls an HTTP POST function to verify if the user's guess was correct * * @param string $code Answer provided by user. Not needed for the Recaptcha implementation * * @return boolean True if the answer is correct, false otherwise * * @since 3.9.0 * @throws \RuntimeException */ public function onCheckAnswer($code = null) { $input = \JFactory::getApplication()->input; $privatekey = $this->params->get('private_key'); $remoteip = IpHelper::getIp(); $response = $input->get('g-recaptcha-response', '', 'string'); // Check for Private Key if (empty($privatekey)) { throw new \RuntimeException(JText::_('PLG_RECAPTCHA_INVISIBLE_ERROR_NO_PRIVATE_KEY')); } // Check for IP if (empty($remoteip)) { throw new \RuntimeException(JText::_('PLG_RECAPTCHA_INVISIBLE_ERROR_NO_IP')); } // Discard spam submissions if (trim($response) == '') { throw new \RuntimeException(JText::_('PLG_RECAPTCHA_INVISIBLE_ERROR_EMPTY_SOLUTION')); } return $this->getResponse($privatekey, $remoteip, $response); } /** * Method to react on the setup of a captcha field. Gives the possibility * to change the field and/or the XML element for the field. * * @param \Joomla\CMS\Form\Field\CaptchaField $field Captcha field instance * @param \SimpleXMLElement $element XML form definition * * @return void * * @since 3.9.0 */ public function onSetupField(\Joomla\CMS\Form\Field\CaptchaField $field, \SimpleXMLElement $element) { // Hide the label for the invisible recaptcha type $element['hiddenLabel'] = true; } /** * Get the reCaptcha response. * * @param string $privatekey The private key for authentication. * @param string $remoteip The remote IP of the visitor. * @param string $response The response received from Google. * * @return boolean True if response is good | False if response is bad. * * @since 3.9.0 * @throws \RuntimeException */ private function getResponse($privatekey, $remoteip, $response) { $reCaptcha = new \ReCaptcha\ReCaptcha($privatekey, new HttpBridgePostRequestMethod); $response = $reCaptcha->verify($response, $remoteip); if (!$response->isSuccess()) { foreach ($response->getErrorCodes() as $error) { throw new \RuntimeException($error); } return false; } return true; } } captcha/recaptcha_invisible/recaptcha_invisible.xml 0000644 00000005374 14735702223 0016703 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.8" type="plugin" group="captcha" method="upgrade"> <name>plg_captcha_recaptcha_invisible</name> <version>3.8</version> <creationDate>November 2017</creationDate> <author>Joomla! Project</author> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <copyright>(C) 2017 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <description>PLG_CAPTCHA_RECAPTCHA_INVISIBLE_XML_DESCRIPTION</description> <files> <filename plugin="recaptcha_invisible">recaptcha_invisible.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_captcha_recaptcha_invisible.ini</language> <language tag="en-GB">en-GB.plg_captcha_recaptcha_invisible.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="public_key" type="text" label="PLG_RECAPTCHA_INVISIBLE_PUBLIC_KEY_LABEL" description="PLG_RECAPTCHA_INVISIBLE_PUBLIC_KEY_DESC" default="" required="true" filter="string" size="100" class="input-xxlarge" /> <field name="private_key" type="text" label="PLG_RECAPTCHA_INVISIBLE_PRIVATE_KEY_LABEL" description="PLG_RECAPTCHA_INVISIBLE_PRIVATE_KEY_DESC" default="" required="true" filter="string" size="100" class="input-xxlarge" /> <field name="badge" type="list" label="PLG_RECAPTCHA_INVISIBLE_BADGE_LABEL" description="PLG_RECAPTCHA_INVISIBLE_BADGE_DESC" default="bottomright" > <option value="bottomright">PLG_RECAPTCHA_INVISIBLE_BADGE_BOTTOMRIGHT</option> <option value="bottomleft">PLG_RECAPTCHA_INVISIBLE_BADGE_BOTTOMLEFT</option> <option value="inline">PLG_RECAPTCHA_INVISIBLE_BADGE_INLINE</option> </field> <field name="tabindex" type="number" label="PLG_RECAPTCHA_INVISIBLE_TABINDEX_LABEL" description="PLG_RECAPTCHA_INVISIBLE_TABINDEX_DESC" default="0" min="0" filter="integer" /> <field name="callback" type="text" label="PLG_RECAPTCHA_INVISIBLE_CALLBACK_LABEL" description="PLG_RECAPTCHA_INVISIBLE_CALLBACK_DESC" default="" filter="string" /> <field name="expired_callback" type="text" label="PLG_RECAPTCHA_INVISIBLE_EXPIRED_CALLBACK_LABEL" description="PLG_RECAPTCHA_INVISIBLE_EXPIRED_CALLBACK_DESC" default="" filter="string" /> <field name="error_callback" type="text" label="PLG_RECAPTCHA_INVISIBLE_ERROR_CALLBACK_LABEL" description="PLG_RECAPTCHA_INVISIBLE_ERROR_CALLBACK_DESC" default="" filter="string" /> </fieldset> </fields> </config> </extension> content/confirmconsent/confirmconsent.php 0000644 00000003746 14735702223 0015024 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.confirmconsent * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\CMSPlugin; /** * The Joomla Core confirm consent plugin * * @since 3.9.0 */ class PlgContentConfirmConsent extends CMSPlugin { /** * The Application object * * @var JApplicationSite * @since 3.9.0 */ protected $app; /** * Load the language file on instantiation. * * @var boolean * @since 3.9.0 */ protected $autoloadLanguage = true; /** * The supported form contexts * * @var array * @since 3.9.0 */ protected $supportedContext = array( 'com_contact.contact', 'com_mailto.mailto', 'com_privacy.request', ); /** * Add additional fields to the supported forms * * @param JForm $form The form to be altered. * @param mixed $data The associated data for the form. * * @return boolean * * @since 3.9.0 */ public function onContentPrepareForm(JForm $form, $data) { if ($this->app->isClient('administrator') || !in_array($form->getName(), $this->supportedContext)) { return true; } // Get the consent box Text & the selected privacyarticle $consentboxText = (string) $this->params->get('consentbox_text', Text::_('PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DEFAULT')); $privacyArticle = $this->params->get('privacy_article', false); $form->load(' <form> <fieldset name="default" addfieldpath="/plugins/content/confirmconsent/fields"> <field name="consentbox" type="consentbox" articleid="' . $privacyArticle . '" label="PLG_CONTENT_CONFIRMCONSENT_CONSENTBOX_LABEL" required="true" > <option value="0">' . htmlspecialchars($consentboxText, ENT_COMPAT, 'UTF-8') . '</option> </field> </fieldset> </form>' ); return true; } } content/confirmconsent/confirmconsent.xml 0000644 00000003067 14735702223 0015031 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.9" type="plugin" group="content" method="upgrade"> <name>plg_content_confirmconsent</name> <author>Joomla! Project</author> <creationDate>May 2018</creationDate> <copyright>(C) 2018 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.9.0</version> <description>PLG_CONTENT_CONFIRMCONSENT_XML_DESCRIPTION</description> <files> <filename plugin="confirmconsent">confirmconsent.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_content_confirmconsent.ini</language> <language tag="en-GB">en-GB.plg_content_confirmconsent.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic" addfieldpath="/administrator/components/com_content/models/fields"> <field name="consentbox_text" type="textarea" label="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_LABEL" description="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DESC" hint="PLG_CONTENT_CONFIRMCONSENT_FIELD_NOTE_DEFAULT" class="span12" rows="7" cols="20" filter="html" /> <field name="privacy_article" type="modal_article" label="PLG_CONTENT_CONFIRMCONSENT_FIELD_ARTICLE_LABEL" description="PLG_CONTENT_CONFIRMCONSENT_FIELD_ARTICLE_DESC" select="true" new="true" edit="true" clear="true" filter="integer" /> </fieldset> </fields> </config> </extension> content/confirmconsent/fields/consentbox.php 0000644 00000015164 14735702223 0015422 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.confirmconsent * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Associations; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; JFormHelper::loadFieldClass('Checkboxes'); /** * Consentbox Field class for the Confirm Consent Plugin. * * @since 3.9.1 */ class JFormFieldConsentBox extends JFormFieldCheckboxes { /** * The form field type. * * @var string * @since 3.9.1 */ protected $type = 'ConsentBox'; /** * Flag to tell the field to always be in multiple values mode. * * @var boolean * @since 3.9.1 */ protected $forceMultiple = false; /** * The article ID. * * @var integer * @since 3.9.1 */ protected $articleid; /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.9.1 */ public function __set($name, $value) { switch ($name) { case 'articleid': $this->articleid = (int) $value; break; default: parent::__set($name, $value); } } /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.9.1 */ public function __get($name) { switch ($name) { case 'articleid': return $this->$name; } return parent::__get($name); } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.9.1 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->articleid = (int) $this->element['articleid']; } return $return; } /** * Method to get the field label markup. * * @return string The field label markup. * * @since 3.9.1 */ protected function getLabel() { if ($this->hidden) { return ''; } $data = $this->getLayoutData(); // Forcing the Alias field to display the tip below $position = $this->element['name'] == 'alias' ? ' data-placement="bottom" ' : ''; // When we have an article let's add the modal and make the title clickable if ($data['articleid']) { $attribs['data-toggle'] = 'modal'; $data['label'] = HTMLHelper::_( 'link', '#modal-' . $this->id, $data['label'], $attribs ); } // Here mainly for B/C with old layouts. This can be done in the layouts directly $extraData = array( 'text' => $data['label'], 'for' => $this->id, 'classes' => explode(' ', $data['labelclass']), 'position' => $position, ); return $this->getRenderer($this->renderLabelLayout)->render(array_merge($data, $extraData)); } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.9.2 */ protected function getInput() { $modalHtml = ''; $layoutData = $this->getLayoutData(); if ($this->articleid) { $modalParams['title'] = $layoutData['label']; $modalParams['url'] = $this->getAssignedArticleUrl(); $modalParams['height'] = 800; $modalParams['width'] = '100%'; $modalHtml = HTMLHelper::_('bootstrap.renderModal', 'modal-' . $this->id, $modalParams); } return $modalHtml . parent::getInput(); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.9.1 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = array( 'articleid' => (integer) $this->articleid, ); return array_merge($data, $extraData); } /** * Return the url of the assigned article based on the current user language * * @return string Returns the link to the article * * @since 3.9.1 */ private function getAssignedArticleUrl() { $db = Factory::getDbo(); // Get the info from the article $query = $db->getQuery(true) ->select($db->quoteName(array('id', 'catid', 'language'))) ->from($db->quoteName('#__content')) ->where($db->quoteName('id') . ' = ' . (int) $this->articleid); $db->setQuery($query); try { $article = $db->loadObject(); } catch (JDatabaseExceptionExecuting $e) { // Something at the database layer went wrong return Route::_( 'index.php?option=com_content&view=article&id=' . $this->articleid . '&tmpl=component' ); } if (!is_object($article)) { // We have not found the article object lets show a 404 to the user return Route::_( 'index.php?option=com_content&view=article&id=' . $this->articleid . '&tmpl=component' ); } // Register ContentHelperRoute JLoader::register('ContentHelperRoute', JPATH_BASE . '/components/com_content/helpers/route.php'); if (!Associations::isEnabled()) { return Route::_( ContentHelperRoute::getArticleRoute( $article->id, $article->catid, $article->language ) . '&tmpl=component' ); } $associatedArticles = Associations::getAssociations('com_content', '#__content', 'com_content.item', $article->id); $currentLang = Factory::getLanguage()->getTag(); if (isset($associatedArticles) && $currentLang !== $article->language && array_key_exists($currentLang, $associatedArticles)) { return Route::_( ContentHelperRoute::getArticleRoute( $associatedArticles[$currentLang]->id, $associatedArticles[$currentLang]->catid, $associatedArticles[$currentLang]->language ) . '&tmpl=component' ); } // Association is enabled but this article is not associated return Route::_( 'index.php?option=com_content&view=article&id=' . $article->id . '&catid=' . $article->catid . '&tmpl=component&lang=' . $article->language ); } } content/contact/contact.php 0000644 00000006370 14735702223 0012030 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.Contact * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\Registry\Registry; /** * Contact Plugin * * @since 3.2 */ class PlgContentContact extends JPlugin { /** * Database object * * @var JDatabaseDriver * @since 3.3 */ protected $db; /** * Plugin that retrieves contact information for contact * * @param string $context The context of the content being passed to the plugin. * @param mixed &$row An object with a "text" property * @param mixed $params Additional parameters. See {@see PlgContentContent()}. * @param integer $page Optional page number. Unused. Defaults to zero. * * @return boolean True on success. */ public function onContentPrepare($context, &$row, $params, $page = 0) { $allowed_contexts = array('com_content.category', 'com_content.article', 'com_content.featured'); if (!in_array($context, $allowed_contexts)) { return true; } // Return if we don't have valid params or don't link the author if (!($params instanceof Registry) || !$params->get('link_author')) { return true; } // Return if an alias is used if ((int) $this->params->get('link_to_alias', 0) === 0 && $row->created_by_alias != '') { return true; } // Return if we don't have a valid article id if (!isset($row->id) || !(int) $row->id) { return true; } $contact = $this->getContactData($row->created_by); $row->contactid = $contact->contactid; $row->webpage = $contact->webpage; $row->email = $contact->email_to; $url = $this->params->get('url', 'url'); if ($row->contactid && $url === 'url') { JLoader::register('ContactHelperRoute', JPATH_SITE . '/components/com_contact/helpers/route.php'); $row->contact_link = JRoute::_(ContactHelperRoute::getContactRoute($contact->contactid . ':' . $contact->alias, $contact->catid)); } elseif ($row->webpage && $url === 'webpage') { $row->contact_link = $row->webpage; } elseif ($row->email && $url === 'email') { $row->contact_link = 'mailto:' . $row->email; } else { $row->contact_link = ''; } return true; } /** * Retrieve Contact * * @param int $userId Id of the user who created the article * * @return mixed|null|integer */ protected function getContactData($userId) { static $contacts = array(); if (isset($contacts[$userId])) { return $contacts[$userId]; } $query = $this->db->getQuery(true); $query->select('MAX(contact.id) AS contactid, contact.alias, contact.catid, contact.webpage, contact.email_to'); $query->from($this->db->quoteName('#__contact_details', 'contact')); $query->where('contact.published = 1'); $query->where('contact.user_id = ' . (int) $userId); if (JLanguageMultilang::isEnabled() === true) { $query->where('(contact.language in ' . '(' . $this->db->quote(JFactory::getLanguage()->getTag()) . ',' . $this->db->quote('*') . ') ' . ' OR contact.language IS NULL)'); } $this->db->setQuery($query); $contacts[$userId] = $this->db->loadObject(); return $contacts[$userId]; } } content/contact/contact.xml 0000644 00000003142 14735702223 0012033 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.2" type="plugin" group="content" method="upgrade"> <name>plg_content_contact</name> <author>Joomla! Project</author> <creationDate>January 2014</creationDate> <copyright>(C) 2014 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.2.2</version> <description>PLG_CONTENT_CONTACT_XML_DESCRIPTION</description> <files> <filename plugin="contact">contact.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_content_contact.ini</language> <language tag="en-GB">en-GB.plg_content_contact.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="url" type="list" label="PLG_CONTENT_CONTACT_PARAM_URL_LABEL" description="PLG_CONTENT_CONTACT_PARAM_URL_DESCRIPTION" default="url" > <option value="url">PLG_CONTENT_CONTACT_PARAM_URL_URL</option> <option value="webpage">PLG_CONTENT_CONTACT_PARAM_URL_WEBPAGE</option> <option value="email">PLG_CONTENT_CONTACT_PARAM_URL_EMAIL</option> </field> <field name="link_to_alias" type="radio" label="PLG_CONTENT_CONTACT_PARAM_ALIAS_LABEL" description="PLG_CONTENT_CONTACT_PARAM_ALIAS_DESCRIPTION" default="0" class="btn-group btn-group-yesno" filter="integer" > <option value="0">JNO</option> <option value="1">JYES</option> </field> </fieldset> </fields> </config> </extension> content/emailcloak/emailcloak.php 0000644 00000042375 14735702223 0013151 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.emailcloak * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\String\StringHelper; /** * Email cloack plugin class. * * @since 1.5 */ class PlgContentEmailcloak extends JPlugin { /** * Plugin that cloaks all emails in content from spambots via Javascript. * * @param string $context The context of the content being passed to the plugin. * @param mixed &$row An object with a "text" property or the string to be cloaked. * @param mixed &$params Additional parameters. See {@see PlgContentEmailcloak()}. * @param integer $page Optional page number. Unused. Defaults to zero. * * @return boolean True on success. */ public function onContentPrepare($context, &$row, &$params, $page = 0) { // Don't run this plugin when the content is being indexed if ($context === 'com_finder.indexer') { return true; } if (is_object($row)) { return $this->_cloak($row->text, $params); } return $this->_cloak($row, $params); } /** * Generate a search pattern based on link and text. * * @param string $link The target of an email link. * @param string $text The text enclosed by the link. * * @return string A regular expression that matches a link containing the parameters. */ protected function _getPattern ($link, $text) { $pattern = '~(?:<a ([^>]*)href\s*=\s*"mailto:' . $link . '"([^>]*))>' . $text . '</a>~i'; return $pattern; } /** * Adds an attributes to the js cloaked email. * * @param string $jsEmail Js cloaked email. * @param string $before Attributes before email. * @param string $after Attributes after email. * * @return string Js cloaked email with attributes. */ protected function _addAttributesToEmail($jsEmail, $before, $after) { if ($before !== '') { $before = str_replace("'", "\'", $before); $jsEmail = str_replace(".innerHTML += '<a '", ".innerHTML += '<a {$before}'", $jsEmail); } if ($after !== '') { $after = str_replace("'", "\'", $after); $jsEmail = str_replace("'\'>'", "'\'{$after}>'", $jsEmail); } return $jsEmail; } /** * Cloak all emails in text from spambots via Javascript. * * @param string &$text The string to be cloaked. * @param mixed &$params Additional parameters. Parameter "mode" (integer, default 1) * replaces addresses with "mailto:" links if nonzero. * * @return boolean True on success. */ protected function _cloak(&$text, &$params) { /* * Check for presence of {emailcloak=off} which is explicits disables this * bot for the item. */ if (StringHelper::strpos($text, '{emailcloak=off}') !== false) { $text = StringHelper::str_ireplace('{emailcloak=off}', '', $text); return true; } // Simple performance check to determine whether bot should process further. if (StringHelper::strpos($text, '@') === false) { return true; } $mode = $this->params->def('mode', 1); // Example: any@example.org $searchEmail = '([\w\.\'\-\+]+\@(?:[a-z0-9\.\-]+\.)+(?:[a-zA-Z0-9\-]{2,24}))'; // Example: any@example.org?subject=anyText $searchEmailLink = $searchEmail . '([?&][\x20-\x7f][^"<>]+)'; // Any Text $searchText = '((?:[\x20-\x7f]|[\xA1-\xFF]|[\xC2-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF4][\x80-\xBF]{3})[^<>]+)'; // Any Image link $searchImage = '(<img[^>]+>)'; // Any Text with <span or <strong $searchTextSpan = '(<span[^>]+>|<span>|<strong>|<strong><span[^>]+>|<strong><span>)' . $searchText . '(</span>|</strong>|</span></strong>)'; // Any address with <span or <strong $searchEmailSpan = '(<span[^>]+>|<span>|<strong>|<strong><span[^>]+>|<strong><span>)' . $searchEmail . '(</span>|</strong>|</span></strong>)'; /* * Search and fix derivatives of link code <a href="http://mce_host/ourdirectory/email@example.org" * >email@example.org</a>. This happens when inserting an email in TinyMCE, cancelling its suggestion to add * the mailto: prefix... */ $pattern = $this->_getPattern($searchEmail, $searchEmail); $pattern = str_replace('"mailto:', '"http://mce_host([\x20-\x7f][^<>]+/)', $pattern); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[3][0]; $mailText = $regs[5][0]; // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText); // Ensure that attributes is not stripped out by email cloaking $replacement = $this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0]); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search and fix derivatives of link code <a href="http://mce_host/ourdirectory/email@example.org" * >anytext</a>. This happens when inserting an email in TinyMCE, cancelling its suggestion to add * the mailto: prefix... */ $pattern = $this->_getPattern($searchEmail, $searchText); $pattern = str_replace('"mailto:', '"http://mce_host([\x20-\x7f][^<>]+/)', $pattern); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[3][0]; $mailText = $regs[5][0]; // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = $this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0]); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@example.org" * >email@example.org</a> */ $pattern = $this->_getPattern($searchEmail, $searchEmail); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0]; $mailText = $regs[4][0]; // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText); // Ensure that attributes is not stripped out by email cloaking $replacement = $this->_addAttributesToEmail($replacement, $regs[1][0], $regs[3][0]); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@amail.com" * ><anyspan >email@amail.com</anyspan></a> */ $pattern = $this->_getPattern($searchEmail, $searchEmailSpan); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0]; $mailText = $regs[4][0] . $regs[5][0] . $regs[6][0]; // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[3][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@amail.com"> * <anyspan >anytext</anyspan></a> */ $pattern = $this->_getPattern($searchEmail, $searchTextSpan); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0]; $mailText = $regs[4][0] . addslashes($regs[5][0]) . $regs[6][0]; $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[3][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@example.org"> * anytext</a> */ $pattern = $this->_getPattern($searchEmail, $searchText); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0]; $mailText = addslashes($regs[4][0]); $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = $this->_addAttributesToEmail($replacement, $regs[1][0], $regs[3][0]); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@example.org"> * <img anything></a> */ $pattern = $this->_getPattern($searchEmail, $searchImage); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0]; $mailText = $regs[4][0]; $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[3][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@example.org"> * <img anything>email@example.org</a> */ $pattern = $this->_getPattern($searchEmail, $searchImage . $searchEmail); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0]; $mailText = $regs[4][0] . $regs[5][0]; $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[3][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@example.org"> * <img anything>any text</a> */ $pattern = $this->_getPattern($searchEmail, $searchImage . $searchText); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0]; $mailText = $regs[4][0] . addslashes($regs[5][0]); $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[3][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@example.org? * subject=Text">email@example.org</a> */ $pattern = $this->_getPattern($searchEmailLink, $searchEmail); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0] . $regs[3][0]; $mailText = $regs[5][0]; // Needed for handling of Body parameter $mail = str_replace('&', '&', $mail); // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText); // Ensure that attributes is not stripped out by email cloaking $replacement = $this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0]); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@example.org? * subject=Text">anytext</a> */ $pattern = $this->_getPattern($searchEmailLink, $searchText); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0] . $regs[3][0]; $mailText = addslashes($regs[5][0]); // Needed for handling of Body parameter $mail = str_replace('&', '&', $mail); $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = $this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0]); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@amail.com?subject= Text" * ><anyspan >email@amail.com</anyspan></a> */ $pattern = $this->_getPattern($searchEmailLink, $searchEmailSpan); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0] . $regs[3][0]; $mailText = $regs[5][0] . $regs[6][0] . $regs[7][0]; // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code <a href="mailto:email@amail.com?subject= Text"> * <anyspan >anytext</anyspan></a> */ $pattern = $this->_getPattern($searchEmailLink, $searchTextSpan); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[2][0] . $regs[3][0]; $mailText = $regs[5][0] . addslashes($regs[6][0]) . $regs[7][0]; $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code * <a href="mailto:email@amail.com?subject=Text"><img anything></a> */ $pattern = $this->_getPattern($searchEmailLink, $searchImage); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[1][0] . $regs[2][0] . $regs[3][0]; $mailText = $regs[5][0]; // Needed for handling of Body parameter $mail = str_replace('&', '&', $mail); // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code * <a href="mailto:email@amail.com?subject=Text"><img anything>email@amail.com</a> */ $pattern = $this->_getPattern($searchEmailLink, $searchImage . $searchEmail); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[1][0] . $regs[2][0] . $regs[3][0]; $mailText = $regs[4][0] . $regs[5][0] . $regs[6][0]; // Needed for handling of Body parameter $mail = str_replace('&', '&', $mail); // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for derivatives of link code * <a href="mailto:email@amail.com?subject=Text"><img anything>any text</a> */ $pattern = $this->_getPattern($searchEmailLink, $searchImage . $searchText); while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[1][0] . $regs[2][0] . $regs[3][0]; $mailText = $regs[4][0] . $regs[5][0] . addslashes($regs[6][0]); // Needed for handling of Body parameter $mail = str_replace('&', '&', $mail); // Check to see if mail text is different from mail addy $replacement = JHtml::_('email.cloak', $mail, $mode, $mailText, 0); // Ensure that attributes is not stripped out by email cloaking $replacement = html_entity_decode($this->_addAttributesToEmail($replacement, $regs[1][0], $regs[4][0])); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[0][1], strlen($regs[0][0])); } /* * Search for plain text email addresses, such as email@example.org but not within HTML tags: * <img src="..." title="email@example.org"> or <input type="text" placeholder="email@example.org"> * The '<[^<]*>(*SKIP)(*F)|' trick is used to exclude this kind of occurrences */ $pattern = '~<[^<]*>(*SKIP)(*F)|' . $searchEmail . '~i'; while (preg_match($pattern, $text, $regs, PREG_OFFSET_CAPTURE)) { $mail = $regs[1][0]; $replacement = JHtml::_('email.cloak', $mail, $mode); // Replace the found address with the js cloaked email $text = substr_replace($text, $replacement, $regs[1][1], strlen($mail)); } return true; } } content/emailcloak/emailcloak.xml 0000644 00000002325 14735702223 0013151 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="content" method="upgrade"> <name>plg_content_emailcloak</name> <author>Joomla! Project</author> <creationDate>November 2005</creationDate> <copyright>(C) 2005 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_CONTENT_EMAILCLOAK_XML_DESCRIPTION</description> <files> <filename plugin="emailcloak">emailcloak.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_content_emailcloak.ini</language> <language tag="en-GB">en-GB.plg_content_emailcloak.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="mode" type="list" label="PLG_CONTENT_EMAILCLOAK_MODE_LABEL" description="PLG_CONTENT_EMAILCLOAK_MODE_DESC" default="1" filter="integer" > <option value="0">PLG_CONTENT_EMAILCLOAK_NONLINKABLE</option> <option value="1">PLG_CONTENT_EMAILCLOAK_LINKABLE</option> </field> </fieldset> </fields> </config> </extension> content/fields/fields.php 0000644 00000010140 14735702224 0011445 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.Fields * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die(); /** * Plug-in to show a custom field in eg an article * This uses the {fields ID} syntax * * @since 3.7.0 */ class PlgContentFields extends JPlugin { /** * Plugin that shows a custom field * * @param string $context The context of the content being passed to the plugin. * @param object &$item The item object. Note $article->text is also available * @param object &$params The article params * @param int $page The 'page' number * * @return void * * @since 3.7.0 */ public function onContentPrepare($context, &$item, &$params, $page = 0) { // If the item has a context, overwrite the existing one if ($context == 'com_finder.indexer' && !empty($item->context)) { $context = $item->context; } elseif ($context == 'com_finder.indexer') { // Don't run this plugin when the content is being indexed and we have no real context return; } // Don't run if there is no text property (in case of bad calls) or it is empty if (empty($item->text)) { return; } // Simple performance check to determine whether bot should process further if (strpos($item->text, 'field') === false) { return; } // Register FieldsHelper JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); // Prepare the text if (isset($item->text)) { $item->text = $this->prepare($item->text, $context, $item); } // Prepare the intro text if (isset($item->introtext)) { $item->introtext = $this->prepare($item->introtext, $context, $item); } } /** * Prepares the given string by parsing {field} and {fieldgroup} groups and replacing them. * * @param string $string The text to prepare * @param string $context The context of the content * @param object $item The item object * * @return string * * @since 3.8.1 */ private function prepare($string, $context, $item) { // Search for {field ID} or {fieldgroup ID} tags and put the results into $matches. $regex = '/{(field|fieldgroup)\s+(.*?)}/i'; preg_match_all($regex, $string, $matches, PREG_SET_ORDER); if (!$matches) { return $string; } $parts = FieldsHelper::extract($context); if (count($parts) < 2) { return $string; } $context = $parts[0] . '.' . $parts[1]; $fields = FieldsHelper::getFields($context, $item, true); $fieldsById = array(); $groups = array(); // Rearranging fields in arrays for easier lookup later. foreach ($fields as $field) { $fieldsById[$field->id] = $field; $groups[$field->group_id][] = $field; } foreach ($matches as $i => $match) { // $match[0] is the full pattern match, $match[1] is the type (field or fieldgroup) and $match[2] the ID and optional the layout $explode = explode(',', $match[2]); $id = (int) $explode[0]; $output = ''; if ($match[1] == 'field' && $id) { if (isset($fieldsById[$id])) { $layout = !empty($explode[1]) ? trim($explode[1]) : $fieldsById[$id]->params->get('layout', 'render'); $output = FieldsHelper::render( $context, 'field.' . $layout, array( 'item' => $item, 'context' => $context, 'field' => $fieldsById[$id] ) ); } } else { if ($match[2] === '*') { $match[0] = str_replace('*', '\*', $match[0]); $renderFields = $fields; } else { $renderFields = isset($groups[$id]) ? $groups[$id] : ''; } if ($renderFields) { $layout = !empty($explode[1]) ? trim($explode[1]) : 'render'; $output = FieldsHelper::render( $context, 'fields.' . $layout, array( 'item' => $item, 'context' => $context, 'fields' => $renderFields ) ); } } $string = preg_replace("|$match[0]|", addcslashes($output, '\\$'), $string, 1); } return $string; } } content/fields/fields.xml 0000644 00000001561 14735702224 0011465 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.7.0" type="plugin" group="content" method="upgrade"> <name>plg_content_fields</name> <author>Joomla! Project</author> <creationDate>February 2017</creationDate> <copyright>(C) 2017 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.7.0</version> <description>PLG_CONTENT_FIELDS_XML_DESCRIPTION</description> <files> <filename plugin="fields">fields.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_content_fields.ini</language> <language tag="en-GB">en-GB.plg_content_fields.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> </fieldset> </fields> </config> </extension> content/finder/finder.php 0000644 00000007715 14735702224 0011465 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Smart Search Content Plugin * * @since 2.5 */ class PlgContentFinder extends JPlugin { /** * Smart Search after save content method. * Content is passed by reference, but after the save, so no changes will be saved. * Method is called right after the content is saved. * * @param string $context The context of the content passed to the plugin (added in 1.6) * @param object $article A JTableContent object * @param bool $isNew If the content has just been created * * @return void * * @since 2.5 */ public function onContentAfterSave($context, $article, $isNew) { $dispatcher = JEventDispatcher::getInstance(); JPluginHelper::importPlugin('finder'); // Trigger the onFinderAfterSave event. $dispatcher->trigger('onFinderAfterSave', array($context, $article, $isNew)); } /** * Smart Search before save content method. * Content is passed by reference. Method is called before the content is saved. * * @param string $context The context of the content passed to the plugin (added in 1.6). * @param object $article A JTableContent object. * @param bool $isNew If the content is just about to be created. * * @return void * * @since 2.5 */ public function onContentBeforeSave($context, $article, $isNew) { $dispatcher = JEventDispatcher::getInstance(); JPluginHelper::importPlugin('finder'); // Trigger the onFinderBeforeSave event. $dispatcher->trigger('onFinderBeforeSave', array($context, $article, $isNew)); } /** * Smart Search after delete content method. * Content is passed by reference, but after the deletion. * * @param string $context The context of the content passed to the plugin (added in 1.6). * @param object $article A JTableContent object. * * @return void * * @since 2.5 */ public function onContentAfterDelete($context, $article) { $dispatcher = JEventDispatcher::getInstance(); JPluginHelper::importPlugin('finder'); // Trigger the onFinderAfterDelete event. $dispatcher->trigger('onFinderAfterDelete', array($context, $article)); } /** * Smart Search content state change method. * Method to update the link information for items that have been changed * from outside the edit screen. This is fired when the item is published, * unpublished, archived, or unarchived from the list view. * * @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 state. * @param integer $value The value of the state that the content has been changed to. * * @return void * * @since 2.5 */ public function onContentChangeState($context, $pks, $value) { $dispatcher = JEventDispatcher::getInstance(); JPluginHelper::importPlugin('finder'); // Trigger the onFinderChangeState event. $dispatcher->trigger('onFinderChangeState', array($context, $pks, $value)); } /** * Smart Search change category state content method. * Method is called when the state of the category to which the * content item belongs is changed. * * @param string $extension The extension whose category has been updated. * @param array $pks A list of primary key ids of the content that has changed state. * @param integer $value The value of the state that the content has been changed to. * * @return void * * @since 2.5 */ public function onCategoryChangeState($extension, $pks, $value) { $dispatcher = JEventDispatcher::getInstance(); JPluginHelper::importPlugin('finder'); // Trigger the onFinderCategoryChangeState event. $dispatcher->trigger('onFinderCategoryChangeState', array($extension, $pks, $value)); } } content/finder/finder.xml 0000644 00000001506 14735702224 0011466 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="content" method="upgrade"> <name>plg_content_finder</name> <author>Joomla! Project</author> <creationDate>December 2011</creationDate> <copyright>(C) 2011 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_CONTENT_FINDER_XML_DESCRIPTION</description> <files> <filename plugin="finder">finder.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_content_finder.ini</language> <language tag="en-GB">en-GB.plg_content_finder.sys.ini</language> </languages> <config> <fields name="params"> </fields> </config> </extension> content/joomla/joomla.php 0000644 00000021365 14735702224 0011506 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.joomla * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Example Content Plugin * * @since 1.6 */ class PlgContentJoomla extends JPlugin { /** * Example after save content method * Article is passed by reference, but after the save, so no changes will be saved. * Method is called right after the content is saved * * @param string $context The context of the content passed to the plugin (added in 1.6) * @param object $article A JTableContent object * @param boolean $isNew If the content is just about to be created * * @return boolean true if function not enabled, is in frontend or is new. Else true or * false depending on success of save function. * * @since 1.6 */ public function onContentAfterSave($context, $article, $isNew) { // Check we are handling the frontend edit form. if ($context !== 'com_content.form') { return true; } // Check if this function is enabled. if (!$this->params->def('email_new_fe', 1)) { return true; } // Check this is a new article. if (!$isNew) { return true; } $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__users')) ->where($db->quoteName('sendEmail') . ' = 1') ->where($db->quoteName('block') . ' = 0'); $db->setQuery($query); $users = (array) $db->loadColumn(); if (empty($users)) { return true; } $user = JFactory::getUser(); // Messaging for new items JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_messages/models', 'MessagesModel'); JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_messages/tables'); $default_language = JComponentHelper::getParams('com_languages')->get('administrator'); $debug = JFactory::getConfig()->get('debug_lang'); $result = true; foreach ($users as $user_id) { if ($user_id != $user->id) { // Load language for messaging $receiver = JUser::getInstance($user_id); $lang = JLanguage::getInstance($receiver->getParam('admin_language', $default_language), $debug); $lang->load('com_content'); $message = array( 'user_id_to' => $user_id, 'subject' => $lang->_('COM_CONTENT_NEW_ARTICLE'), 'message' => sprintf($lang->_('COM_CONTENT_ON_NEW_CONTENT'), $user->get('name'), $article->title) ); $model_message = JModelLegacy::getInstance('Message', 'MessagesModel'); $result = $model_message->save($message); } } return $result; } /** * Don't allow categories to be deleted if they contain items or subcategories with items * * @param string $context The context for the content passed to the plugin. * @param object $data The data relating to the content that was deleted. * * @return boolean * * @since 1.6 */ public function onContentBeforeDelete($context, $data) { // Skip plugin if we are deleting something other than categories if ($context !== 'com_categories.category') { return true; } // Check if this function is enabled. if (!$this->params->def('check_categories', 1)) { return true; } $extension = JFactory::getApplication()->input->getString('extension'); // Default to true if not a core extension $result = true; $tableInfo = array( 'com_banners' => array('table_name' => '#__banners'), 'com_contact' => array('table_name' => '#__contact_details'), 'com_content' => array('table_name' => '#__content'), 'com_newsfeeds' => array('table_name' => '#__newsfeeds'), 'com_weblinks' => array('table_name' => '#__weblinks') ); // Now check to see if this is a known core extension if (isset($tableInfo[$extension])) { // Get table name for known core extensions $table = $tableInfo[$extension]['table_name']; // See if this category has any content items $count = $this->_countItemsInCategory($table, $data->get('id')); // Return false if db error if ($count === false) { $result = false; } else { // Show error if items are found in the category if ($count > 0) { $msg = JText::sprintf('COM_CATEGORIES_DELETE_NOT_ALLOWED', $data->get('title')) . JText::plural('COM_CATEGORIES_N_ITEMS_ASSIGNED', $count); JError::raiseWarning(403, $msg); $result = false; } // Check for items in any child categories (if it is a leaf, there are no child categories) if (!$data->isLeaf()) { $count = $this->_countItemsInChildren($table, $data->get('id'), $data); if ($count === false) { $result = false; } elseif ($count > 0) { $msg = JText::sprintf('COM_CATEGORIES_DELETE_NOT_ALLOWED', $data->get('title')) . JText::plural('COM_CATEGORIES_HAS_SUBCATEGORY_ITEMS', $count); JError::raiseWarning(403, $msg); $result = false; } } } return $result; } } /** * Get count of items in a category * * @param string $table table name of component table (column is catid) * @param integer $catid id of the category to check * * @return mixed count of items found or false if db error * * @since 1.6 */ private function _countItemsInCategory($table, $catid) { $db = JFactory::getDbo(); $query = $db->getQuery(true); // Count the items in this category $query->select('COUNT(id)') ->from($table) ->where('catid = ' . $catid); $db->setQuery($query); try { $count = $db->loadResult(); } catch (RuntimeException $e) { JError::raiseWarning(500, $e->getMessage()); return false; } return $count; } /** * Get count of items in a category's child categories * * @param string $table table name of component table (column is catid) * @param integer $catid id of the category to check * @param object $data The data relating to the content that was deleted. * * @return mixed count of items found or false if db error * * @since 1.6 */ private function _countItemsInChildren($table, $catid, $data) { $db = JFactory::getDbo(); // Create subquery for list of child categories $childCategoryTree = $data->getTree(); // First element in tree is the current category, so we can skip that one unset($childCategoryTree[0]); $childCategoryIds = array(); foreach ($childCategoryTree as $node) { $childCategoryIds[] = $node->id; } // Make sure we only do the query if we have some categories to look in if (count($childCategoryIds)) { // Count the items in this category $query = $db->getQuery(true) ->select('COUNT(id)') ->from($table) ->where('catid IN (' . implode(',', $childCategoryIds) . ')'); $db->setQuery($query); try { $count = $db->loadResult(); } catch (RuntimeException $e) { JError::raiseWarning(500, $e->getMessage()); return false; } return $count; } else // If we didn't have any categories to check, return 0 { return 0; } } /** * Change the state in core_content if the state in a table is changed * * @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 state. * @param integer $value The value of the state that the content has been changed to. * * @return boolean * * @since 3.1 */ public function onContentChangeState($context, $pks, $value) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('core_content_id')) ->from($db->quoteName('#__ucm_content')) ->where($db->quoteName('core_type_alias') . ' = ' . $db->quote($context)) ->where($db->quoteName('core_content_item_id') . ' IN (' . $pksImploded = implode(',', $pks) . ')'); $db->setQuery($query); $ccIds = $db->loadColumn(); $cctable = new JTableCorecontent($db); $cctable->publish($ccIds, $value); return true; } /** * The save event. * * @param string $context The context * @param object $table The item * @param boolean $isNew Is new item * * @return void * * @since 3.9.12 */ public function onContentBeforeSave($context, $table, $isNew) { // Check we are handling the frontend edit form. if ($context !== 'com_menus.item') { return true; } // Special case for Create article menu item if ($table->link !== 'index.php?option=com_content&view=form&layout=edit') { return true; } // Display error if catid is not set when enable_category is enabled $params = json_decode($table->params, true); if ($params['enable_category'] == 1 && empty($params['catid'])) { $table->setError(JText::_('COM_CONTENT_CREATE_ARTICLE_ERROR')); return false; } } } content/joomla/joomla.xml 0000644 00000003056 14735702224 0011514 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="content" method="upgrade"> <name>plg_content_joomla</name> <author>Joomla! Project</author> <creationDate>November 2010</creationDate> <copyright>(C) 2010 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_CONTENT_JOOMLA_XML_DESCRIPTION</description> <files> <filename plugin="joomla">joomla.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_content_joomla.ini</language> <language tag="en-GB">en-GB.plg_content_joomla.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="check_categories" type="radio" label="PLG_CONTENT_JOOMLA_FIELD_CHECK_CATEGORIES_LABEL" description="PLG_CONTENT_JOOMLA_FIELD_CHECK_CATEGORIES_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="email_new_fe" type="radio" label="PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_FE_LABEL" description="PLG_CONTENT_JOOMLA_FIELD_EMAIL_NEW_FE_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JYES</option> <option value="0">JNO</option> </field> </fieldset> </fields> </config> </extension> content/loadmodule/loadmodule.php 0000644 00000015306 14735702224 0013214 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.loadmodule * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Plugin to enable loading modules into content (e.g. articles) * This uses the {loadmodule} syntax * * @since 1.5 */ class PlgContentLoadmodule extends JPlugin { protected static $modules = array(); protected static $mods = array(); /** * Plugin that loads module positions within content * * @param string $context The context of the content being passed to the plugin. * @param object &$article The article object. Note $article->text is also available * @param mixed &$params The article params * @param integer $page The 'page' number * * @return mixed true if there is an error. Void otherwise. * * @since 1.6 */ public function onContentPrepare($context, &$article, &$params, $page = 0) { // Don't run this plugin when the content is being indexed if ($context === 'com_finder.indexer') { return true; } // Simple performance check to determine whether bot should process further if (strpos($article->text, 'loadposition') === false && strpos($article->text, 'loadmodule') === false) { return true; } // Expression to search for (positions) $regex = '/{loadposition\s(.*?)}/i'; $style = $this->params->def('style', 'none'); // Expression to search for(modules) $regexmod = '/{loadmodule\s(.*?)}/i'; $stylemod = $this->params->def('style', 'none'); // Expression to search for(id) $regexmodid = '/{loadmoduleid\s([1-9][0-9]*)}/i'; // Find all instances of plugin and put in $matches for loadposition // $matches[0] is full pattern match, $matches[1] is the position preg_match_all($regex, $article->text, $matches, PREG_SET_ORDER); // No matches, skip this if ($matches) { foreach ($matches as $match) { $matcheslist = explode(',', $match[1]); // We may not have a module style so fall back to the plugin default. if (!array_key_exists(1, $matcheslist)) { $matcheslist[1] = $style; } $position = trim($matcheslist[0]); $style = trim($matcheslist[1]); $output = $this->_load($position, $style); // We should replace only first occurrence in order to allow positions with the same name to regenerate their content: if (($start = strpos($article->text, $match[0])) !== false) { $article->text = substr_replace($article->text, $output, $start, strlen($match[0])); } $style = $this->params->def('style', 'none'); } } // Find all instances of plugin and put in $matchesmod for loadmodule preg_match_all($regexmod, $article->text, $matchesmod, PREG_SET_ORDER); // If no matches, skip this if ($matchesmod) { foreach ($matchesmod as $matchmod) { $matchesmodlist = explode(',', $matchmod[1]); // We may not have a specific module so set to null if (!array_key_exists(1, $matchesmodlist)) { $matchesmodlist[1] = null; } // We may not have a module style so fall back to the plugin default. if (!array_key_exists(2, $matchesmodlist)) { $matchesmodlist[2] = $stylemod; } $module = trim($matchesmodlist[0]); $name = htmlspecialchars_decode(trim($matchesmodlist[1])); $stylemod = trim($matchesmodlist[2]); // $match[0] is full pattern match, $match[1] is the module,$match[2] is the title $output = $this->_loadmod($module, $name, $stylemod); // We should replace only first occurrence in order to allow positions with the same name to regenerate their content: if (($start = strpos($article->text, $matchmod[0])) !== false) { $article->text = substr_replace($article->text, $output, $start, strlen($matchmod[0])); } $stylemod = $this->params->def('style', 'none'); } } // Find all instances of plugin and put in $matchesmodid for loadmoduleid preg_match_all($regexmodid, $article->text, $matchesmodid, PREG_SET_ORDER); // If no matches, skip this if ($matchesmodid) { foreach ($matchesmodid as $match) { $id = trim($match[1]); $output = $this->_loadid($id); // We should replace only first occurrence in order to allow positions with the same name to regenerate their content: if (($start = strpos($article->text, $match[0])) !== false) { $article->text = substr_replace($article->text, $output, $start, strlen($match[0])); } $style = $this->params->def('style', 'none'); } } } /** * Loads and renders the module * * @param string $position The position assigned to the module * @param string $style The style assigned to the module * * @return mixed * * @since 1.6 */ protected function _load($position, $style = 'none') { self::$modules[$position] = ''; $document = JFactory::getDocument(); $renderer = $document->loadRenderer('module'); $modules = JModuleHelper::getModules($position); $params = array('style' => $style); ob_start(); foreach ($modules as $module) { echo $renderer->render($module, $params); } self::$modules[$position] = ob_get_clean(); return self::$modules[$position]; } /** * This is always going to get the first instance of the module type unless * there is a title. * * @param string $module The module title * @param string $title The title of the module * @param string $style The style of the module * * @return mixed * * @since 1.6 */ protected function _loadmod($module, $title, $style = 'none') { self::$mods[$module] = ''; $document = JFactory::getDocument(); $renderer = $document->loadRenderer('module'); $mod = JModuleHelper::getModule($module, $title); // If the module without the mod_ isn't found, try it with mod_. // This allows people to enter it either way in the content if (!isset($mod)) { $name = 'mod_' . $module; $mod = JModuleHelper::getModule($name, $title); } $params = array('style' => $style); ob_start(); if ($mod->id) { echo $renderer->render($mod, $params); } self::$mods[$module] = ob_get_clean(); return self::$mods[$module]; } /** * Loads and renders the module * * @param string $id The id of the module * * @return mixed * * @since 3.9.0 */ protected function _loadid($id) { self::$modules[$id] = ''; $document = JFactory::getDocument(); $renderer = $document->loadRenderer('module'); $modules = JModuleHelper::getModuleById($id); $params = array('style' => 'none'); ob_start(); if ($modules->id > 0) { echo $renderer->render($modules, $params); } self::$modules[$id] = ob_get_clean(); return self::$modules[$id]; } } content/loadmodule/loadmodule.xml 0000644 00000002631 14735702224 0013222 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="content" method="upgrade"> <name>plg_content_loadmodule</name> <author>Joomla! Project</author> <creationDate>November 2005</creationDate> <copyright>(C) 2005 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_LOADMODULE_XML_DESCRIPTION</description> <files> <filename plugin="loadmodule">loadmodule.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_content_loadmodule.ini</language> <language tag="en-GB">en-GB.plg_content_loadmodule.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="style" type="list" label="PLG_LOADMODULE_FIELD_STYLE_LABEL" description="PLG_LOADMODULE_FIELD_STYLE_DESC" default="table" > <option value="table">PLG_LOADMODULE_FIELD_VALUE_TABLE</option> <option value="horz">PLG_LOADMODULE_FIELD_VALUE_HORIZONTAL</option> <option value="xhtml">PLG_LOADMODULE_FIELD_VALUE_DIVS</option> <option value="rounded">PLG_LOADMODULE_FIELD_VALUE_MULTIPLEDIVS</option> <option value="none">PLG_LOADMODULE_FIELD_VALUE_RAW</option> </field> </fieldset> </fields> </config> </extension> content/pagebreak/pagebreak.php 0000644 00000022765 14735702224 0012613 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.pagebreak * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\String\StringHelper; jimport('joomla.utilities.utility'); JLoader::register('ContentHelperRoute', JPATH_SITE . '/components/com_content/helpers/route.php'); /** * Page break plugin * * <b>Usage:</b> * <code><hr class="system-pagebreak" /></code> * <code><hr class="system-pagebreak" title="The page title" /></code> * or * <code><hr class="system-pagebreak" alt="The first page" /></code> * or * <code><hr class="system-pagebreak" title="The page title" alt="The first page" /></code> * or * <code><hr class="system-pagebreak" alt="The first page" title="The page title" /></code> * * @since 1.6 */ class PlgContentPagebreak extends JPlugin { /** * The navigation list with all page objects if parameter 'multipage_toc' is active. * * @var array * @since 3.9.2 */ protected $list = array(); /** * Plugin that adds a pagebreak into the text and truncates text at that point * * @param string $context The context of the content being passed to the plugin. * @param object &$row The article object. Note $article->text is also available * @param mixed &$params The article params * @param integer $page The 'page' number * * @return mixed Always returns void or true * * @since 1.6 */ public function onContentPrepare($context, &$row, &$params, $page = 0) { $canProceed = $context === 'com_content.article'; if (!$canProceed) { return; } $style = $this->params->get('style', 'pages'); // Expression to search for. $regex = '#<hr(.*)class="system-pagebreak"(.*)\/>#iU'; $input = JFactory::getApplication()->input; $print = $input->getBool('print'); $showall = $input->getBool('showall'); if (!$this->params->get('enabled', 1)) { $print = true; } if ($print) { $row->text = preg_replace($regex, '<br />', $row->text); return true; } // Simple performance check to determine whether bot should process further. if (StringHelper::strpos($row->text, 'class="system-pagebreak') === false) { if ($page > 0) { throw new Exception(JText::_('JERROR_PAGE_NOT_FOUND'), 404); } return true; } $view = $input->getString('view'); $full = $input->getBool('fullview'); if (!$page) { $page = 0; } if ($full || $view !== 'article' || $params->get('intro_only') || $params->get('popup')) { $row->text = preg_replace($regex, '', $row->text); return; } // Load plugin language files only when needed (ex: not needed if no system-pagebreak class exists). $this->loadLanguage(); // Find all instances of plugin and put in $matches. $matches = array(); preg_match_all($regex, $row->text, $matches, PREG_SET_ORDER); if ($showall && $this->params->get('showall', 1)) { $hasToc = $this->params->get('multipage_toc', 1); if ($hasToc) { // Display TOC. $page = 1; $this->_createToc($row, $matches, $page); } else { $row->toc = ''; } $row->text = preg_replace($regex, '<br />', $row->text); return true; } // Split the text around the plugin. $text = preg_split($regex, $row->text); if (!isset($text[$page])) { throw new Exception(JText::_('JERROR_PAGE_NOT_FOUND'), 404); } // Count the number of pages. $n = count($text); // We have found at least one plugin, therefore at least 2 pages. if ($n > 1) { $title = $this->params->get('title', 1); $hasToc = $this->params->get('multipage_toc', 1); // Adds heading or title to <site> Title. if ($title && $page && isset($matches[$page - 1][0])) { $attrs = JUtility::parseAttributes($matches[$page - 1][0]); if (isset($attrs['title'])) { $row->page_title = $attrs['title']; } } // Reset the text, we already hold it in the $text array. $row->text = ''; if ($style === 'pages') { // Display TOC. if ($hasToc) { $this->_createToc($row, $matches, $page); } else { $row->toc = ''; } // Traditional mos page navigation $pageNav = new JPagination($n, $page, 1); // Flag indicates to not add limitstart=0 to URL $pageNav->hideEmptyLimitstart = true; // Page counter. $row->text .= '<div class="pagenavcounter">'; $row->text .= $pageNav->getPagesCounter(); $row->text .= '</div>'; // Page text. $text[$page] = str_replace('<hr id="system-readmore" />', '', $text[$page]); $row->text .= $text[$page]; // $row->text .= '<br />'; $row->text .= '<div class="pager">'; // Adds navigation between pages to bottom of text. if ($hasToc) { $this->_createNavigation($row, $page, $n); } // Page links shown at bottom of page if TOC disabled. if (!$hasToc) { $row->text .= $pageNav->getPagesLinks(); } $row->text .= '</div>'; } else { $t[] = $text[0]; $t[] = (string) JHtml::_($style . '.start', 'article' . $row->id . '-' . $style); foreach ($text as $key => $subtext) { if ($key >= 1) { $match = $matches[$key - 1]; $match = (array) JUtility::parseAttributes($match[0]); if (isset($match['alt'])) { $title = stripslashes($match['alt']); } elseif (isset($match['title'])) { $title = stripslashes($match['title']); } else { $title = JText::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $key + 1); } $t[] = (string) JHtml::_($style . '.panel', $title, 'article' . $row->id . '-' . $style . $key); } $t[] = (string) $subtext; } $t[] = (string) JHtml::_($style . '.end'); $row->text = implode(' ', $t); } } return true; } /** * Creates a Table of Contents for the pagebreak * * @param object &$row The article object. Note $article->text is also available * @param array &$matches Array of matches of a regex in onContentPrepare * @param integer &$page The 'page' number * * @return void * * @since 1.6 */ protected function _createToc(&$row, &$matches, &$page) { $heading = isset($row->title) ? $row->title : JText::_('PLG_CONTENT_PAGEBREAK_NO_TITLE'); $input = JFactory::getApplication()->input; $limitstart = $input->getUInt('limitstart', 0); $showall = $input->getInt('showall', 0); $headingtext = ''; if ($this->params->get('article_index', 1) == 1) { $headingtext = JText::_('PLG_CONTENT_PAGEBREAK_ARTICLE_INDEX'); if ($this->params->get('article_index_text')) { $headingtext = htmlspecialchars($this->params->get('article_index_text'), ENT_QUOTES, 'UTF-8'); } } // TOC first Page link. $this->list[1] = new stdClass; $this->list[1]->liClass = ($limitstart === 0 && $showall === 0) ? 'toclink active' : 'toclink'; $this->list[1]->class = $this->list[1]->liClass; $this->list[1]->link = JRoute::_(ContentHelperRoute::getArticleRoute($row->slug, $row->catid, $row->language)); $this->list[1]->title = $heading; $i = 2; foreach ($matches as $bot) { if (@$bot[0]) { $attrs2 = JUtility::parseAttributes($bot[0]); if (@$attrs2['alt']) { $title = stripslashes($attrs2['alt']); } elseif (@$attrs2['title']) { $title = stripslashes($attrs2['title']); } else { $title = JText::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $i); } } else { $title = JText::sprintf('PLG_CONTENT_PAGEBREAK_PAGE_NUM', $i); } $this->list[$i] = new stdClass; $this->list[$i]->link = JRoute::_(ContentHelperRoute::getArticleRoute($row->slug, $row->catid, $row->language) . '&limitstart=' . ($i - 1)); $this->list[$i]->title = $title; $this->list[$i]->liClass = ($limitstart === $i - 1) ? 'active' : ''; $this->list[$i]->class = ($limitstart === $i - 1) ? 'toclink active' : 'toclink'; $i++; } if ($this->params->get('showall')) { $this->list[$i] = new stdClass; $this->list[$i]->link = JRoute::_(ContentHelperRoute::getArticleRoute($row->slug, $row->catid, $row->language) . '&showall=1'); $this->list[$i]->liClass = ($showall === 1) ? 'active' : ''; $this->list[$i]->class = ($showall === 1) ? 'toclink active' : 'toclink'; $this->list[$i]->title = JText::_('PLG_CONTENT_PAGEBREAK_ALL_PAGES'); } $list = $this->list; $path = JPluginHelper::getLayoutPath('content', 'pagebreak', 'toc'); ob_start(); include $path; $row->toc = ob_get_clean(); } /** * Creates the navigation for the item * * @param object &$row The article object. Note $article->text is also available * @param int $page The page number * @param int $n The total number of pages * * @return void * * @since 1.6 */ protected function _createNavigation(&$row, $page, $n) { $links = array( 'next' => '', 'previous' => '' ); if ($page < $n - 1) { $links['next'] = JRoute::_(ContentHelperRoute::getArticleRoute($row->slug, $row->catid, $row->language) . '&limitstart=' . ($page + 1)); } if ($page > 0) { $links['previous'] = ContentHelperRoute::getArticleRoute($row->slug, $row->catid, $row->language); if ($page > 1) { $links['previous'] .= '&limitstart=' . ($page - 1); } $links['previous'] = JRoute::_($links['previous']); } $path = JPluginHelper::getLayoutPath('content', 'pagebreak', 'navigation'); ob_start(); include $path; $row->text .= ob_get_clean(); } } content/pagebreak/pagebreak.xml 0000644 00000005427 14735702224 0012620 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="content" method="upgrade"> <name>plg_content_pagebreak</name> <author>Joomla! Project</author> <creationDate>November 2005</creationDate> <copyright>(C) 2005 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_CONTENT_PAGEBREAK_XML_DESCRIPTION</description> <files> <filename plugin="pagebreak">pagebreak.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_content_pagebreak.ini</language> <language tag="en-GB">en-GB.plg_content_pagebreak.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="title" type="radio" label="PLG_CONTENT_PAGEBREAK_SITE_TITLE_LABEL" description="PLG_CONTENT_PAGEBREAK_SITE_TITLE_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field name="article_index" type="radio" label="PLG_CONTENT_PAGEBREAK_SITE_ARTICLEINDEX_LABEL" description="PLG_CONTENT_PAGEBREAK_SITE_ARTICLEINDEX_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field name="article_index_text" type="text" label="PLG_CONTENT_PAGEBREAK_SITE_ARTICLEINDEXTEXT" description="PLG_CONTENT_PAGEBREAK_SITE_ARTICLEINDEXTEXT_DESC" showon="article_index:1" /> <field name="multipage_toc" type="radio" label="PLG_CONTENT_PAGEBREAK_TOC_LABEL" description="PLG_CONTENT_PAGEBREAK_TOC_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field name="showall" type="radio" label="PLG_CONTENT_PAGEBREAK_SHOW_ALL_LABEL" description="PLG_CONTENT_PAGEBREAK_SHOW_ALL_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field name="style" type="list" label="PLG_CONTENT_PAGEBREAK_STYLE_LABEL" description="PLG_CONTENT_PAGEBREAK_STYLE_DESC" default="pages" > <option value="pages">PLG_CONTENT_PAGEBREAK_PAGES</option> <option value="sliders">PLG_CONTENT_PAGEBREAK_SLIDERS</option> <option value="tabs">PLG_CONTENT_PAGEBREAK_TABS</option> </field> </fieldset> </fields> </config> </extension> content/pagebreak/tmpl/navigation.php 0000644 00000002663 14735702224 0014000 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.pagebreak * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; $lang = JFactory::getLanguage(); ?> <ul> <li> <?php if ($links['previous']) : $direction = $lang->isRtl() ? 'right' : 'left'; $title = htmlspecialchars($this->list[$page]->title, ENT_QUOTES, 'UTF-8'); $ariaLabel = JText::_('JPREVIOUS') . ': ' . $title . ' (' . JText::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', $page, $n) . ')'; ?> <a href="<?php echo $links['previous']; ?>" title="<?php echo $title; ?>" aria-label="<?php echo $ariaLabel; ?>" rel="prev"> <?php echo '<span class="icon-chevron-' . $direction . '" aria-hidden="true"></span> ' . JText::_('JPREV'); ?> </a> <?php endif; ?> </li> <li> <?php if ($links['next']) : $direction = $lang->isRtl() ? 'left' : 'right'; $title = htmlspecialchars($this->list[$page + 2]->title, ENT_QUOTES, 'UTF-8'); $ariaLabel = JText::_('JNEXT') . ': ' . $title . ' (' . JText::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', ($page + 2), $n) . ')'; ?> <a href="<?php echo $links['next']; ?>" title="<?php echo $title; ?>" aria-label="<?php echo $ariaLabel; ?>" rel="next"> <?php echo JText::_('JNEXT') . ' <span class="icon-chevron-' . $direction . '" aria-hidden="true"></span>'; ?> </a> <?php endif; ?> </li> </ul> content/pagebreak/tmpl/toc.php 0000644 00000001366 14735702224 0012425 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.pagebreak * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; ?> <div class="pull-right article-index"> <?php if ($headingtext) : ?> <h3><?php echo $headingtext; ?></h3> <?php endif; ?> <ul class="nav nav-tabs nav-stacked"> <?php foreach ($list as $listItem) : ?> <?php $class = $listItem->liClass ? ' class="' . $listItem->liClass . '"' : ''; ?> <li<?php echo $class; ?>> <a href="<?php echo $listItem->link; ?>" class="<?php echo $listItem->class; ?>"> <?php echo $listItem->title; ?> </a> </li> <?php endforeach; ?> </ul> </div> content/pagenavigation/pagenavigation.php 0000644 00000016421 14735702224 0014731 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.pagenavigation * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; JLoader::register('ContentHelperRoute', JPATH_SITE . '/components/com_content/helpers/route.php'); /** * Pagenavigation plugin class. * * @since 1.5 */ class PlgContentPagenavigation extends JPlugin { /** * If in the article view and the parameter is enabled shows the page navigation * * @param string $context The context of the content being passed to the plugin * @param object &$row The article object * @param mixed &$params The article params * @param integer $page The 'page' number * * @return mixed void or true * * @since 1.6 */ public function onContentBeforeDisplay($context, &$row, &$params, $page = 0) { $app = JFactory::getApplication(); $view = $app->input->get('view'); $print = $app->input->getBool('print'); if ($print) { return false; } if ($context === 'com_content.article' && $view === 'article' && $params->get('show_item_navigation')) { $db = JFactory::getDbo(); $user = JFactory::getUser(); $lang = JFactory::getLanguage(); $nullDate = $db->getNullDate(); $date = JFactory::getDate(); $now = $date->toSql(); $uid = $row->id; $option = 'com_content'; $canPublish = $user->authorise('core.edit.state', $option . '.article.' . $row->id); /** * The following is needed as different menu items types utilise a different param to control ordering. * For Blogs the `orderby_sec` param is the order controlling param. * For Table and List views it is the `orderby` param. **/ $params_list = $params->toArray(); if (array_key_exists('orderby_sec', $params_list)) { $order_method = $params->get('orderby_sec', ''); } else { $order_method = $params->get('orderby', ''); } // Additional check for invalid sort ordering. if ($order_method === 'front') { $order_method = ''; } // Get the order code $orderDate = $params->get('order_date'); $queryDate = $this->getQueryDate($orderDate); // Determine sort order. switch ($order_method) { case 'date' : $orderby = $queryDate; break; case 'rdate' : $orderby = $queryDate . ' DESC '; break; case 'alpha' : $orderby = 'a.title'; break; case 'ralpha' : $orderby = 'a.title DESC'; break; case 'hits' : $orderby = 'a.hits'; break; case 'rhits' : $orderby = 'a.hits DESC'; break; case 'order' : $orderby = 'a.ordering'; break; case 'author' : $orderby = 'a.created_by_alias, u.name'; break; case 'rauthor' : $orderby = 'a.created_by_alias DESC, u.name DESC'; break; case 'front' : $orderby = 'f.ordering'; break; default : $orderby = 'a.ordering'; break; } $xwhere = ' AND (a.state = 1 OR a.state = -1)' . ' AND (publish_up = ' . $db->quote($nullDate) . ' OR publish_up <= ' . $db->quote($now) . ')' . ' AND (publish_down = ' . $db->quote($nullDate) . ' OR publish_down >= ' . $db->quote($now) . ')'; // Array of articles in same category correctly ordered. $query = $db->getQuery(true); // Sqlsrv changes $case_when = ' CASE WHEN ' . $query->charLength('a.alias', '!=', '0'); $a_id = $query->castAsChar('a.id'); $case_when .= ' THEN ' . $query->concatenate(array($a_id, 'a.alias'), ':'); $case_when .= ' ELSE ' . $a_id . ' END as slug'; $case_when1 = ' CASE WHEN ' . $query->charLength('cc.alias', '!=', '0'); $c_id = $query->castAsChar('cc.id'); $case_when1 .= ' THEN ' . $query->concatenate(array($c_id, 'cc.alias'), ':'); $case_when1 .= ' ELSE ' . $c_id . ' END as catslug'; $query->select('a.id, a.title, a.catid, a.language,' . $case_when . ',' . $case_when1) ->from('#__content AS a') ->join('LEFT', '#__categories AS cc ON cc.id = a.catid'); if ($order_method === 'author' || $order_method === 'rauthor') { $query->select('a.created_by, u.name'); $query->join('LEFT', '#__users AS u ON u.id = a.created_by'); } $query->where( 'a.catid = ' . (int) $row->catid . ' AND a.state = ' . (int) $row->state . ($canPublish ? '' : ' AND a.access IN (' . implode(',', JAccess::getAuthorisedViewLevels($user->id)) . ') ') . $xwhere ); $query->order($orderby); if ($app->isClient('site') && $app->getLanguageFilter()) { $query->where('a.language in (' . $db->quote($lang->getTag()) . ',' . $db->quote('*') . ')'); } $db->setQuery($query); $list = $db->loadObjectList('id'); // This check needed if incorrect Itemid is given resulting in an incorrect result. if (!is_array($list)) { $list = array(); } reset($list); // Location of current content item in array list. $location = array_search($uid, array_keys($list)); $rows = array_values($list); $row->prev = null; $row->next = null; if ($location - 1 >= 0) { // The previous content item cannot be in the array position -1. $row->prev = $rows[$location - 1]; } if (($location + 1) < count($rows)) { // The next content item cannot be in an array position greater than the number of array postions. $row->next = $rows[$location + 1]; } if ($row->prev) { $row->prev_label = ($this->params->get('display', 0) == 0) ? JText::_('JPREV') : $row->prev->title; $row->prev = JRoute::_(ContentHelperRoute::getArticleRoute($row->prev->slug, $row->prev->catid, $row->prev->language)); } else { $row->prev_label = ''; $row->prev = ''; } if ($row->next) { $row->next_label = ($this->params->get('display', 0) == 0) ? JText::_('JNEXT') : $row->next->title; $row->next = JRoute::_(ContentHelperRoute::getArticleRoute($row->next->slug, $row->next->catid, $row->next->language)); } else { $row->next_label = ''; $row->next = ''; } // Output. if ($row->prev || $row->next) { // Get the path for the layout file $path = JPluginHelper::getLayoutPath('content', 'pagenavigation'); // Render the pagenav ob_start(); include $path; $row->pagination = ob_get_clean(); $row->paginationposition = $this->params->get('position', 1); // This will default to the 1.5 and 1.6-1.7 behavior. $row->paginationrelative = $this->params->get('relative', 0); } } } /** * Translate an order code to a field for primary ordering. * * @param string $orderDate The ordering code. * * @return string The SQL field(s) to order by. * * @since 3.3 */ private static function getQueryDate($orderDate) { $db = JFactory::getDbo(); switch ($orderDate) { // Use created if modified is not set case 'modified' : $queryDate = ' CASE WHEN a.modified = ' . $db->quote($db->getNullDate()) . ' THEN a.created ELSE a.modified END'; break; // Use created if publish_up is not set case 'published' : $queryDate = ' CASE WHEN a.publish_up = ' . $db->quote($db->getNullDate()) . ' THEN a.created ELSE a.publish_up END '; break; // Use created as default case 'created' : default : $queryDate = ' a.created '; break; } return $queryDate; } } content/pagenavigation/pagenavigation.xml 0000644 00000003742 14735702224 0014744 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="content" method="upgrade"> <name>plg_content_pagenavigation</name> <author>Joomla! Project</author> <creationDate>January 2006</creationDate> <copyright>(C) 2006 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_PAGENAVIGATION_XML_DESCRIPTION</description> <files> <filename plugin="pagenavigation">pagenavigation.php</filename> <folder>tmpl</folder> </files> <languages> <language tag="en-GB">en-GB.plg_content_pagenavigation.ini</language> <language tag="en-GB">en-GB.plg_content_pagenavigation.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="position" type="list" label="PLG_PAGENAVIGATION_FIELD_POSITION_LABEL" description="PLG_PAGENAVIGATION_FIELD_POSITION_DESC" default="1" filter="integer" > <option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_BELOW</option> <option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_ABOVE</option> </field> <field name="relative" type="list" label="PLG_PAGENAVIGATION_FIELD_RELATIVE_LABEL" description="PLG_PAGENAVIGATION_FIELD_RELATIVE_DESC" default="1" filter="integer" > <option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_ARTICLE</option> <option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_TEXT</option> </field> <field name="display" type="list" label="PLG_PAGENAVIGATION_FIELD_DISPLAY_LABEL" description="PLG_PAGENAVIGATION_FIELD_DISPLAY_DESC" default="0" filter="integer" > <option value="0">PLG_PAGENAVIGATION_FIELD_VALUE_NEXTPREV</option> <option value="1">PLG_PAGENAVIGATION_FIELD_VALUE_TITLE</option> </field> </fieldset> </fields> </config> </extension> content/pagenavigation/tmpl/default.php 0000644 00000002577 14735702224 0014344 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.pagenavigation * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; JHtml::_('bootstrap.tooltip'); $lang = JFactory::getLanguage(); ?> <ul class="pager pagenav"> <?php if ($row->prev) : $direction = $lang->isRtl() ? 'right' : 'left'; ?> <li class="previous"> <a class="hasTooltip" title="<?php echo htmlspecialchars($rows[$location-1]->title); ?>" aria-label="<?php echo JText::sprintf('JPREVIOUS_TITLE', htmlspecialchars($rows[$location-1]->title)); ?>" href="<?php echo $row->prev; ?>" rel="prev"> <?php echo '<span class="icon-chevron-' . $direction . '" aria-hidden="true"></span> <span aria-hidden="true">' . $row->prev_label . '</span>'; ?> </a> </li> <?php endif; ?> <?php if ($row->next) : $direction = $lang->isRtl() ? 'left' : 'right'; ?> <li class="next"> <a class="hasTooltip" title="<?php echo htmlspecialchars($rows[$location+1]->title); ?>" aria-label="<?php echo JText::sprintf('JNEXT_TITLE', htmlspecialchars($rows[$location+1]->title)); ?>" href="<?php echo $row->next; ?>" rel="next"> <?php echo '<span aria-hidden="true">' . $row->next_label . '</span> <span class="icon-chevron-' . $direction . '" aria-hidden="true"></span>'; ?> </a> </li> <?php endif; ?> </ul> content/vote/tmpl/rating.php 0000644 00000003167 14735702224 0012161 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.vote * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Layout variables * ----------------- * @var string $context The context of the content being passed to the plugin * @var object &$row The article object * @var object &$params The article params * @var integer $page The 'page' number * @var array $parts The context segments * @var string $path Path to this file */ if ($context == 'com_content.categories') { return; } $rating = (int) $row->rating; $rcount = (int) $row->rating_count; // Look for images in template if available $starImageOn = JHtml::_('image', 'system/rating_star.png', JText::_('PLG_VOTE_STAR_ACTIVE'), null, true); $starImageOff = JHtml::_('image', 'system/rating_star_blank.png', JText::_('PLG_VOTE_STAR_INACTIVE'), null, true); $img = ''; for ($i = 0; $i < $rating; $i++) { $img .= $starImageOn; } for ($i = $rating; $i < 5; $i++) { $img .= $starImageOff; } ?> <div class="content_rating"> <?php if ($rcount) : ?> <p class="unseen element-invisible" itemprop="aggregateRating" itemscope itemtype="https://schema.org/AggregateRating"> <?php echo JText::sprintf('PLG_VOTE_USER_RATING', '<span itemprop="ratingValue">' . $rating . '</span>', '<span itemprop="bestRating">5</span>'); ?> <meta itemprop="ratingCount" content="<?php echo $rcount; ?>" /> <meta itemprop="worstRating" content="1" /> </p> <?php endif; ?> <?php echo $img; ?> </div> content/vote/tmpl/vote.php 0000644 00000003257 14735702224 0011652 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.vote * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Layout variables * ----------------- * @var string $context The context of the content being passed to the plugin * @var object &$row The article object * @var object &$params The article params * @var integer $page The 'page' number * @var array $parts The context segments * @var string $path Path to this file */ $uri = clone JUri::getInstance(); $uri->setVar('hitcount', '0'); // Create option list for voting select box $options = array(); for ($i = 1; $i < 6; $i++) { $options[] = JHtml::_('select.option', $i, JText::sprintf('PLG_VOTE_VOTE', $i)); } ?> <form method="post" action="<?php echo htmlspecialchars($uri->toString(), ENT_COMPAT, 'UTF-8'); ?>" class="form-inline"> <span class="content_vote"> <label class="unseen element-invisible" for="content_vote_<?php echo (int) $row->id; ?>"><?php echo JText::_('PLG_VOTE_LABEL'); ?></label> <?php echo JHtml::_('select.genericlist', $options, 'user_rating', null, 'value', 'text', '5', 'content_vote_' . (int) $row->id); ?>  <input class="btn btn-mini" type="submit" name="submit_vote" value="<?php echo JText::_('PLG_VOTE_RATE'); ?>" /> <input type="hidden" name="task" value="article.vote" /> <input type="hidden" name="hitcount" value="0" /> <input type="hidden" name="url" value="<?php echo htmlspecialchars($uri->toString(), ENT_COMPAT, 'UTF-8'); ?>" /> <?php echo JHtml::_('form.token'); ?> </span> </form> content/vote/vote.php 0000644 00000007152 14735702224 0010674 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Content.vote * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Vote plugin. * * @since 1.5 */ class PlgContentVote extends JPlugin { /** * Application object * * @var JApplicationCms * @since 3.7.0 */ protected $app; /** * The position the voting data is displayed in relative to the article. * * @var string * @since 3.7.0 */ protected $votingPosition; /** * Constructor. * * @param object &$subject The object to observe * @param array $config An optional associative array of configuration settings. * * @since 3.7.0 */ public function __construct(&$subject, $config) { parent::__construct($subject, $config); $this->votingPosition = $this->params->get('position', 'top'); } /** * Displays the voting area when viewing an article and the voting section is displayed before the article * * @param string $context The context of the content being passed to the plugin * @param object &$row The article object * @param object &$params The article params * @param integer $page The 'page' number * * @return string|boolean HTML string containing code for the votes if in com_content else boolean false * * @since 1.6 */ public function onContentBeforeDisplay($context, &$row, &$params, $page = 0) { if ($this->votingPosition !== 'top') { return ''; } return $this->displayVotingData($context, $row, $params, $page); } /** * Displays the voting area when viewing an article and the voting section is displayed after the article * * @param string $context The context of the content being passed to the plugin * @param object &$row The article object * @param object &$params The article params * @param integer $page The 'page' number * * @return string|boolean HTML string containing code for the votes if in com_content else boolean false * * @since 3.7.0 */ public function onContentAfterDisplay($context, &$row, &$params, $page = 0) { if ($this->votingPosition !== 'bottom') { return ''; } return $this->displayVotingData($context, $row, $params, $page); } /** * Displays the voting area * * @param string $context The context of the content being passed to the plugin * @param object &$row The article object * @param object &$params The article params * @param integer $page The 'page' number * * @return string|boolean HTML string containing code for the votes if in com_content else boolean false * * @since 3.7.0 */ private function displayVotingData($context, &$row, &$params, $page) { $parts = explode('.', $context); if ($parts[0] !== 'com_content') { return false; } if (empty($params) || !$params->get('show_vote', null)) { return ''; } // Load plugin language files only when needed (ex: they are not needed if show_vote is not active). $this->loadLanguage(); // Get the path for the rating summary layout file $path = JPluginHelper::getLayoutPath('content', 'vote', 'rating'); // Render the layout ob_start(); include $path; $html = ob_get_clean(); if ($this->app->input->getString('view', '') === 'article' && $row->state == 1) { // Get the path for the voting form layout file $path = JPluginHelper::getLayoutPath('content', 'vote', 'vote'); // Render the layout ob_start(); include $path; $html .= ob_get_clean(); } return $html; } } content/vote/vote.xml 0000644 00000002176 14735702224 0010706 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="content" method="upgrade"> <name>plg_content_vote</name> <author>Joomla! Project</author> <creationDate>November 2005</creationDate> <copyright>(C) 2005 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <version>3.0.0</version> <description>PLG_VOTE_XML_DESCRIPTION</description> <files> <filename plugin="vote">vote.php</filename> <folder>tmpl</folder> </files> <languages> <language tag="en-GB">en-GB.plg_content_vote.ini</language> <language tag="en-GB">en-GB.plg_content_vote.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="position" type="list" label="PLG_VOTE_POSITION_LABEL" description="PLG_VOTE_POSITION_DESC" default="top" > <option value="top">PLG_VOTE_TOP</option> <option value="bottom">PLG_VOTE_BOTTOM</option> </field> </fieldset> </fields> </config> </extension> editors/codemirror/codemirror.php 0000644 00000024473 14735702224 0013260 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.codemirror * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access defined('_JEXEC') or die; /** * CodeMirror Editor Plugin. * * @since 1.6 */ class PlgEditorCodemirror extends JPlugin { /** * Affects constructor behavior. If true, language files will be loaded automatically. * * @var boolean * @since 3.1.4 */ protected $autoloadLanguage = true; /** * Mapping of syntax to CodeMirror modes. * * @var array */ protected $modeAlias = array(); /** * Initialises the Editor. * * @return void */ public function onInit() { static $done = false; // Do this only once. if ($done) { return; } $done = true; // Most likely need this later $doc = JFactory::getDocument(); // Codemirror shall have its own group of plugins to modify and extend its behavior JPluginHelper::importPlugin('editors_codemirror'); $dispatcher = JEventDispatcher::getInstance(); // At this point, params can be modified by a plugin before going to the layout renderer. $dispatcher->trigger('onCodeMirrorBeforeInit', array(&$this->params)); $displayData = (object) array('params' => $this->params); // We need to do output buffering here because layouts may actually 'echo' things which we do not want. ob_start(); JLayoutHelper::render('editors.codemirror.init', $displayData, __DIR__ . '/layouts'); ob_end_clean(); $font = $this->params->get('fontFamily', '0'); $fontInfo = $this->getFontInfo($font); if (isset($fontInfo)) { if (isset($fontInfo->url)) { $doc->addStyleSheet($fontInfo->url); } if (isset($fontInfo->css)) { $displayData->fontFamily = $fontInfo->css . '!important'; } } // We need to do output buffering here because layouts may actually 'echo' things which we do not want. ob_start(); JLayoutHelper::render('editors.codemirror.styles', $displayData, __DIR__ . '/layouts'); ob_end_clean(); $dispatcher->trigger('onCodeMirrorAfterInit', array(&$this->params)); } /** * Copy editor content to form field. * * @param string $id The id of the editor field. * * @return string Javascript * * @deprecated 4.0 Code executes directly on submit */ public function onSave($id) { return sprintf('document.getElementById(%1$s).value = Joomla.editors.instances[%1$s].getValue();', json_encode((string) $id)); } /** * Get the editor content. * * @param string $id The id of the editor field. * * @return string Javascript * * @deprecated 4.0 Use directly the returned code */ public function onGetContent($id) { return sprintf('Joomla.editors.instances[%1$s].getValue();', json_encode((string) $id)); } /** * Set the editor content. * * @param string $id The id of the editor field. * @param string $content The content to set. * * @return string Javascript * * @deprecated 4.0 Use directly the returned code */ public function onSetContent($id, $content) { return sprintf('Joomla.editors.instances[%1$s].setValue(%2$s);', json_encode((string) $id), json_encode((string) $content)); } /** * Adds the editor specific insert method. * * @return void * * @deprecated 4.0 Code is loaded in the init script */ public function onGetInsertMethod() { static $done = false; // Do this only once. if ($done) { return true; } $done = true; JFactory::getDocument()->addScriptDeclaration(" ;function jInsertEditorText(text, editor) { Joomla.editors.instances[editor].replaceSelection(text); } "); return true; } /** * Display the editor area. * * @param string $name The control name. * @param string $content The contents of the text area. * @param string $width The width of the text area (px or %). * @param string $height The height of the text area (px or %). * @param int $col The number of columns for the textarea. * @param int $row The number of rows for the textarea. * @param boolean $buttons True and the editor buttons will be displayed. * @param string $id An optional ID for the textarea (note: since 1.6). If not supplied the name is used. * @param string $asset Not used. * @param object $author Not used. * @param array $params Associative array of editor parameters. * * @return string HTML */ public function onDisplay( $name, $content, $width, $height, $col, $row, $buttons = true, $id = null, $asset = null, $author = null, $params = array()) { // True if a CodeMirror already has autofocus. Prevent multiple autofocuses. static $autofocused; $id = empty($id) ? $name : $id; // Must pass the field id to the buttons in this editor. $buttons = $this->displayButtons($id, $buttons, $asset, $author); // Only add "px" to width and height if they are not given as a percentage. $width .= is_numeric($width) ? 'px' : ''; $height .= is_numeric($height) ? 'px' : ''; // Options for the CodeMirror constructor. $options = new stdClass; // Is field readonly? if (!empty($params['readonly'])) { $options->readOnly = 'nocursor'; } // Should we focus on the editor on load? if (!$autofocused) { $options->autofocus = isset($params['autofocus']) ? (bool) $params['autofocus'] : false; $autofocused = $options->autofocus; } $options->lineWrapping = (boolean) $this->params->get('lineWrapping', 1); // Add styling to the active line. $options->styleActiveLine = (boolean) $this->params->get('activeLine', 1); // Do we highlight selection matches? if ($this->params->get('selectionMatches', 1)) { $options->highlightSelectionMatches = array( 'showToken' => true, 'annotateScrollbar' => true, ); } // Do we use line numbering? if ($options->lineNumbers = (boolean) $this->params->get('lineNumbers', 1)) { $options->gutters[] = 'CodeMirror-linenumbers'; } // Do we use code folding? if ($options->foldGutter = (boolean) $this->params->get('codeFolding', 1)) { $options->gutters[] = 'CodeMirror-foldgutter'; } // Do we use a marker gutter? if ($options->markerGutter = (boolean) $this->params->get('markerGutter', $this->params->get('marker-gutter', 1))) { $options->gutters[] = 'CodeMirror-markergutter'; } // Load the syntax mode. $syntax = !empty($params['syntax']) ? $params['syntax'] : $this->params->get('syntax', 'html'); $options->mode = isset($this->modeAlias[$syntax]) ? $this->modeAlias[$syntax] : $syntax; // Load the theme if specified. if ($theme = $this->params->get('theme')) { $options->theme = $theme; JHtml::_('stylesheet', $this->params->get('basePath', 'media/editors/codemirror/') . 'theme/' . $theme . '.css', array('version' => 'auto')); } // Special options for tagged modes (xml/html). if (in_array($options->mode, array('xml', 'html', 'php'))) { // Autogenerate closing tags (html/xml only). $options->autoCloseTags = (boolean) $this->params->get('autoCloseTags', 1); // Highlight the matching tag when the cursor is in a tag (html/xml only). $options->matchTags = (boolean) $this->params->get('matchTags', 1); } // Special options for non-tagged modes. if (!in_array($options->mode, array('xml', 'html'))) { // Autogenerate closing brackets. $options->autoCloseBrackets = (boolean) $this->params->get('autoCloseBrackets', 1); // Highlight the matching bracket. $options->matchBrackets = (boolean) $this->params->get('matchBrackets', 1); } $options->scrollbarStyle = $this->params->get('scrollbarStyle', 'native'); // KeyMap settings. $options->keyMap = $this->params->get('keyMap', false); // Support for older settings. if ($options->keyMap === false) { $options->keyMap = $this->params->get('vimKeyBinding', 0) ? 'vim' : 'default'; } if ($options->keyMap && $options->keyMap != 'default') { $this->loadKeyMap($options->keyMap); } $displayData = (object) array( 'options' => $options, 'params' => $this->params, 'name' => $name, 'id' => $id, 'cols' => $col, 'rows' => $row, 'content' => $content, 'buttons' => $buttons ); $dispatcher = JEventDispatcher::getInstance(); // At this point, displayData can be modified by a plugin before going to the layout renderer. $results = $dispatcher->trigger('onCodeMirrorBeforeDisplay', array(&$displayData)); $results[] = JLayoutHelper::render('editors.codemirror.element', $displayData, __DIR__ . '/layouts', array('debug' => JDEBUG)); foreach ($dispatcher->trigger('onCodeMirrorAfterDisplay', array(&$displayData)) as $result) { $results[] = $result; } return implode("\n", $results); } /** * Displays the editor buttons. * * @param string $name Button name. * @param mixed $buttons [array with button objects | boolean true to display buttons] * @param mixed $asset Unused. * @param mixed $author Unused. * * @return string HTML */ protected function displayButtons($name, $buttons, $asset, $author) { $return = ''; $args = array( 'name' => $name, 'event' => 'onGetInsertMethod' ); $results = (array) $this->update($args); if ($results) { foreach ($results as $result) { if (is_string($result) && trim($result)) { $return .= $result; } } } if (is_array($buttons) || (is_bool($buttons) && $buttons)) { $buttons = $this->_subject->getButtons($name, $buttons, $asset, $author); $return .= JLayoutHelper::render('joomla.editors.buttons', $buttons); } return $return; } /** * Gets font info from the json data file * * @param string $font A key from the $fonts array. * * @return object */ protected function getFontInfo($font) { static $fonts; if (!$fonts) { $fonts = json_decode(file_get_contents(__DIR__ . '/fonts.json'), true); } return isset($fonts[$font]) ? (object) $fonts[$font] : null; } /** * Loads a keyMap file * * @param string $keyMap The name of a keyMap file to load. * * @return void */ protected function loadKeyMap($keyMap) { $basePath = $this->params->get('basePath', 'media/editors/codemirror/'); $ext = JDEBUG ? '.js' : '.min.js'; JHtml::_('script', $basePath . 'keymap/' . $keyMap . $ext, array('version' => 'auto')); } } editors/codemirror/codemirror.xml 0000644 00000024674 14735702224 0013274 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.2" type="plugin" group="editors" method="upgrade"> <name>plg_editors_codemirror</name> <version>5.60.0</version> <creationDate>28 March 2011</creationDate> <author>Marijn Haverbeke</author> <authorEmail>marijnh@gmail.com</authorEmail> <authorUrl>https://codemirror.net/</authorUrl> <copyright>Copyright (C) 2014 - 2021 by Marijn Haverbeke <marijnh@gmail.com> and others</copyright> <license>MIT license: https://codemirror.net/LICENSE</license> <description>PLG_CODEMIRROR_XML_DESCRIPTION</description> <files> <filename plugin="codemirror">codemirror.php</filename> <filename>styles.css</filename> <filename>styles.min.css</filename> <filename>fonts.json</filename> <filename>fonts.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_editors_codemirror.ini</language> <language tag="en-GB">en-GB.plg_editors_codemirror.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field name="lineNumbers" type="radio" label="PLG_CODEMIRROR_FIELD_LINENUMBERS_LABEL" description="PLG_CODEMIRROR_FIELD_LINENUMBERS_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="codeFolding" type="radio" label="PLG_CODEMIRROR_FIELD_CODEFOLDING_LABEL" description="PLG_CODEMIRROR_FIELD_CODEFOLDING_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="markerGutter" type="radio" label="PLG_CODEMIRROR_FIELD_MARKERGUTTER_LABEL" description="PLG_CODEMIRROR_FIELD_MARKERGUTTER_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="lineWrapping" type="radio" label="PLG_CODEMIRROR_FIELD_LINEWRAPPING_LABEL" description="PLG_CODEMIRROR_FIELD_LINEWRAPPING_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="activeLine" type="radio" label="PLG_CODEMIRROR_FIELD_ACTIVELINE_LABEL" description="PLG_CODEMIRROR_FIELD_ACTIVELINE_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="selectionMatches" type="radio" label="PLG_CODEMIRROR_FIELD_SELECTIONMATCHES_LABEL" description="PLG_CODEMIRROR_FIELD_SELECTIONMATCHES_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="matchTags" type="radio" label="PLG_CODEMIRROR_FIELD_MATCHTAGS_LABEL" description="PLG_CODEMIRROR_FIELD_MATCHTAGS_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="matchBrackets" type="radio" label="PLG_CODEMIRROR_FIELD_MATCHBRACKETS_LABEL" description="PLG_CODEMIRROR_FIELD_MATCHBRACKETS_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="autoCloseTags" type="radio" label="PLG_CODEMIRROR_FIELD_AUTOCLOSETAGS_LABEL" description="PLG_CODEMIRROR_FIELD_AUTOCLOSETAGS_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="autoCloseBrackets" type="radio" label="PLG_CODEMIRROR_FIELD_AUTOCLOSEBRACKET_LABEL" description="PLG_CODEMIRROR_FIELD_AUTOCLOSEBRACKET_DESC" class="btn-group btn-group-yesno" default="1" filter="integer" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="keyMap" type="list" label="PLG_CODEMIRROR_FIELD_KEYMAP_LABEL" description="PLG_CODEMIRROR_FIELD_KEYMAP_DESC" default="" > <option value="">JDEFAULT</option> <option value="emacs">PLG_CODEMIRROR_FIELD_KEYMAP_EMACS</option> <option value="sublime">PLG_CODEMIRROR_FIELD_KEYMAP_SUBLIME</option> <option value="vim">PLG_CODEMIRROR_FIELD_KEYMAP_VIM</option> </field> <field name="fullScreen" type="list" label="PLG_CODEMIRROR_FIELD_FULLSCREEN_LABEL" description="PLG_CODEMIRROR_FIELD_FULLSCREEN_DESC" default="F10" > <option value="F1">F1</option> <option value="F2">F2</option> <option value="F3">F3</option> <option value="F4">F4</option> <option value="F5">F5</option> <option value="F6">F6</option> <option value="F7">F7</option> <option value="F8">F8</option> <option value="F9">F9</option> <option value="F10">F10</option> <option value="F11">F11</option> <option value="F12">F12</option> </field> <field name="fullScreenMod" type="checkboxes" label="PLG_CODEMIRROR_FIELD_FULLSCREEN_MOD_LABEL" description="PLG_CODEMIRROR_FIELD_FULLSCREEN_MOD_DESC" > <option value="Shift">PLG_CODEMIRROR_FIELD_VALUE_FULLSCREEN_MOD_SHIFT</option> <option value="Cmd">PLG_CODEMIRROR_FIELD_VALUE_FULLSCREEN_MOD_CMD</option> <option value="Ctrl">PLG_CODEMIRROR_FIELD_VALUE_FULLSCREEN_MOD_CTRL</option> <option value="Alt">PLG_CODEMIRROR_FIELD_VALUE_FULLSCREEN_MOD_ALT</option> </field> <field name="basePath" type="hidden" default="media/editors/codemirror/" /> <field name="modePath" type="hidden" default="media/editors/codemirror/mode/%N/%N" /> </fieldset> <fieldset name="appearance" label="PLG_CODEMIRROR_FIELDSET_APPEARANCE_OPTIONS_LABEL" addfieldpath="plugins/editors/codemirror"> <field name="theme" type="filelist" label="PLG_CODEMIRROR_FIELD_THEME_LABEL" description="PLG_CODEMIRROR_FIELD_THEME_DESC" default="" filter="\.css$" stripext="true" hide_none="true" hide_default="false" directory="media/editors/codemirror/theme" /> <field name="activeLineColor" type="color" label="PLG_CODEMIRROR_FIELD_ACTIVELINE_COLOR_LABEL" description="PLG_CODEMIRROR_FIELD_ACTIVELINE_COLOR_DESC" default="#a4c2eb" filter="color" /> <field name="highlightMatchColor" type="color" label="PLG_CODEMIRROR_FIELD_HIGHLIGHT_MATCH_COLOR_LABEL" description="PLG_CODEMIRROR_FIELD_HIGHLIGHT_MATCH_COLOR_DESC" default="#fa542f" filter="color" /> <field name="fontFamily" type="fonts" label="PLG_CODEMIRROR_FIELD_FONT_FAMILY_LABEL" description="PLG_CODEMIRROR_FIELD_FONT_FAMILY_DESC" default="0" > <option value="0">PLG_CODEMIRROR_FIELD_VALUE_FONT_FAMILY_DEFAULT</option> </field> <field name="fontSize" type="integer" label="PLG_CODEMIRROR_FIELD_FONT_SIZE_LABEL" description="PLG_CODEMIRROR_FIELD_FONT_SIZE_DESC" first="6" last="16" step="1" default="13" filter="integer" /> <field name="lineHeight" type="list" label="PLG_CODEMIRROR_FIELD_LINE_HEIGHT_LABEL" description="PLG_CODEMIRROR_FIELD_LINE_HEIGHT_DESC" default="1.2" filter="float" > <option value="1">1</option> <option value="1.1">1.1</option> <option value="1.2">1.2</option> <option value="1.3">1.3</option> <option value="1.4">1.4</option> <option value="1.5">1.5</option> <option value="1.6">1.6</option> <option value="1.7">1.7</option> <option value="1.8">1.8</option> <option value="1.9">1.9</option> <option value="2">2</option> </field> <field name="scrollbarStyle" type="radio" label="PLG_CODEMIRROR_FIELD_VALUE_SCROLLBARSTYLE_LABEL" description="PLG_CODEMIRROR_FIELD_VALUE_SCROLLBARSTYLE_DESC" class="btn-group btn-group-yesno" default="native" > <option value="native">PLG_CODEMIRROR_FIELD_VALUE_SCROLLBARSTYLE_DEFAULT</option> <option value="simple">PLG_CODEMIRROR_FIELD_VALUE_SCROLLBARSTYLE_SIMPLE</option> <option value="overlay">PLG_CODEMIRROR_FIELD_VALUE_SCROLLBARSTYLE_OVERLAY</option> </field> <field name="preview" type="editor" label="PLG_CODEMIRROR_FIELD_PREVIEW_LABEL" description="PLG_CODEMIRROR_FIELD_PREVIEW_DESC" editor="codemirror" filter="unset" buttons="false" > <default> <![CDATA[ <script type="text/javascript"> jQuery(function ($) { $('.hello').html('Hello World'); }); </script> <style type="text/css"> h1 { background-clip: border-box; background-color: #cacaff; background-image: linear-gradient(45deg, transparent 0px, transparent 30px, #ababff 30px, #ababff 60px, transparent 60px); background-repeat: repeat-x; background-size: 90px 100%; border: 1px solid #8989ff; border-radius: 10px; color: #333; padding: 0 15px; } </style> <div> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam a ornare lectus, quis semper urna. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus interdum metus id elit rutrum sollicitudin. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam in fermentum risus, id facilisis nulla. Phasellus gravida erat sed ullamcorper accumsan. Donec blandit sem eget sem congue, a varius sapien semper.</p> <p>Integer euismod tempor convallis. Nullam porttitor et ex ac fringilla. Quisque facilisis est ac erat condimentum malesuada. Aenean commodo quam odio, tincidunt ultricies mauris suscipit et.</p> <ul> <li>Vivamus ultrices ligula a odio lacinia pellentesque.</li> <li>Curabitur iaculis arcu pharetra, mollis turpis id, commodo erat.</li> <li>Etiam consequat enim quis faucibus interdum.</li> <li>Morbi in ipsum pulvinar, eleifend lorem sit amet, euismod magna.</li> <li>Donec consectetur lacus vitae eros euismod porta.</li> </ul> </div> ]]> </default> </field> </fieldset> </fields> </config> </extension> editors/codemirror/fonts.json 0000644 00000006055 14735702224 0012422 0 ustar 00 { "anonymous_pro": { "name": "Anonymous Pro", "url": "https://fonts.googleapis.com/css?family=Anonymous+Pro", "css": "'Anonymous Pro', monospace" }, "cousine": { "name": "Cousine", "url": "https://fonts.googleapis.com/css?family=Cousine", "css": "Cousine, monospace" }, "cutive_mono": { "name": "Cutive Mono", "url": "https://fonts.googleapis.com/css?family=Cutive+Mono", "css": "'Cutive Mono', monospace" }, "droid_sans_mono": { "name": "Droid Sans Mono", "url": "https://fonts.googleapis.com/css?family=Droid+Sans+Mono", "css": "'Droid Sans Mono', monospace" }, "fira_mono": { "name": "Fira Mono", "url": "https://fonts.googleapis.com/css?family=Fira+Mono", "css": "'Fira Mono', monospace" }, "ibm_plex_mono": { "name": "IBM Plex Mono", "url": "https://fonts.googleapis.com/css?family=IBM+Plex+Mono", "css": "'IBM Plex Mono', monospace;" }, "inconsolata": { "name": "Inconsolata", "url": "https://fonts.googleapis.com/css?family=Inconsolata", "css": "Inconsolata, monospace" }, "lekton": { "name": "Lekton", "url": "https://fonts.googleapis.com/css?family=Lekton", "css": "Lekton, monospace" }, "nanum_gothic_coding": { "name": "Nanum Gothic Coding", "url": "https://fonts.googleapis.com/css?family=Nanum+Gothic+Coding", "css": "'Nanum Gothic Coding', monospace" }, "nova_mono": { "name": "Nova Mono", "url": "https://fonts.googleapis.com/css?family=Nova+Mono", "css": "'Nova Mono', monospace" }, "overpass_mono": { "name": "Overpass Mono", "url": "https://fonts.googleapis.com/css?family=Overpass+Mono", "css": "'Overpass Mono', monospace" }, "oxygen_mono": { "name": "Oxygen Mono", "url": "https://fonts.googleapis.com/css?family=Oxygen+Mono", "css": "'Oxygen Mono', monospace" }, "press_start_2p": { "name": "Press Start 2P", "url": "https://fonts.googleapis.com/css?family=Press+Start+2P", "css": "'Press Start 2P', monospace" }, "pt_mono": { "name": "PT Mono", "url": "https://fonts.googleapis.com/css?family=PT+Mono", "css": "'PT Mono', monospace" }, "roboto_mono": { "name": "Roboto Mono", "url": "https://fonts.googleapis.com/css?family=Roboto+Mono", "css": "'Roboto Mono', monospace" }, "rubik_mono_one": { "name": "Rubik Mono One", "url": "https://fonts.googleapis.com/css?family=Rubik+Mono+One", "css": "'Rubik Mono One', monospace" }, "share_tech_mono": { "name": "Share Tech Mono", "url": "https://fonts.googleapis.com/css?family=Share+Tech+Mono", "css": "'Share Tech Mono', monospace" }, "source_code_pro": { "name": "Source Code Pro", "url": "https://fonts.googleapis.com/css?family=Source+Code+Pro", "css": "'Source Code Pro', monospace" }, "space_mono": { "name": "Space Mono", "url": "https://fonts.googleapis.com/css?family=Space+Mono", "css": "'Space Mono', monospace" }, "ubuntu_mono": { "name": "Ubuntu Mono", "url": "https://fonts.googleapis.com/css?family=Ubuntu+Mono", "css": "'Ubuntu Mono', monospace" }, "vt323": { "name": "VT323", "url": "https://fonts.googleapis.com/css?family=VT323", "css": "'VT323', monospace" } } editors/codemirror/fonts.php 0000644 00000002104 14735702224 0012227 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.codemirror * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access defined('_JEXEC') or die; JFormHelper::loadFieldClass('list'); /** * Supports an HTML select list of fonts * * @package Joomla.Plugin * @subpackage Editors.codemirror * @since 3.4 */ class JFormFieldFonts extends JFormFieldList { /** * The form field type. * * @var string * @since 3.4 */ protected $type = 'Fonts'; /** * Method to get the list of fonts field options. * * @return array The field option objects. * * @since 3.4 */ protected function getOptions() { $fonts = json_decode(file_get_contents(__DIR__ . '/fonts.json')); $options = array(); foreach ($fonts as $key => $info) { $options[] = JHtml::_('select.option', $key, $info->name); } // Merge any additional options in the XML definition. return array_merge(parent::getOptions(), $options); } } editors/codemirror/layouts/editors/codemirror/element.php 0000644 00000002052 14735702224 0020047 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.codemirror * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access defined('_JEXEC') or die; $options = $displayData->options; $params = $displayData->params; $name = $displayData->name; $id = $displayData->id; $cols = $displayData->cols; $rows = $displayData->rows; $content = $displayData->content; $buttons = $displayData->buttons; $modifier = $params->get('fullScreenMod', array()) ? implode(' + ', $params->get('fullScreenMod', array())) . ' + ' : ''; ?> <p class="label"> <?php echo JText::sprintf('PLG_CODEMIRROR_TOGGLE_FULL_SCREEN', $modifier, $params->get('fullScreen', 'F10')); ?> </p> <?php echo '<textarea class="codemirror-source" name="', $name, '" id="', $id, '" cols="', $cols, '" rows="', $rows, '" data-options="', htmlspecialchars(json_encode($options)), '">', $content, '</textarea>'; ?> <?php echo $buttons; ?> editors/codemirror/layouts/editors/codemirror/init.php 0000644 00000007243 14735702224 0017370 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.codemirror * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access defined('_JEXEC') or die; $params = $displayData->params; $basePath = $params->get('basePath', 'media/editors/codemirror/'); $modePath = $params->get('modePath', 'media/editors/codemirror/mode/%N/%N'); $extJS = JDEBUG ? '.js' : '.min.js'; $extCSS = JDEBUG ? '.css' : '.min.css'; JHtml::_('script', $basePath . 'lib/codemirror' . $extJS, array('version' => 'auto')); JHtml::_('script', $basePath . 'lib/addons' . $extJS, array('version' => 'auto')); JHtml::_('stylesheet', $basePath . 'lib/codemirror' . $extCSS, array('version' => 'auto')); JHtml::_('stylesheet', $basePath . 'lib/addons' . $extCSS, array('version' => 'auto')); $fskeys = $params->get('fullScreenMod', array()); $fskeys[] = $params->get('fullScreen', 'F10'); $fullScreenCombo = implode('-', $fskeys); $fsCombo = json_encode($fullScreenCombo); $modPath = json_encode(JUri::root(true) . '/' . $modePath . $extJS); JFactory::getDocument()->addScriptDeclaration( <<<JS ;(function (cm, $) { cm.commands.toggleFullScreen = function (cm) { cm.setOption('fullScreen', !cm.getOption('fullScreen')); }; cm.commands.closeFullScreen = function (cm) { cm.getOption('fullScreen') && cm.setOption('fullScreen', false); }; cm.keyMap.default['Ctrl-Q'] = 'toggleFullScreen'; cm.keyMap.default[$fsCombo] = 'toggleFullScreen'; cm.keyMap.default['Esc'] = 'closeFullScreen'; // For mode autoloading. cm.modeURL = $modPath; // Fire this function any time an editor is created. cm.defineInitHook(function (editor) { // Try to set up the mode var mode = cm.findModeByMIME(editor.options.mode || '') || cm.findModeByName(editor.options.mode || '') || cm.findModeByExtension(editor.options.mode || ''); cm.autoLoadMode(editor, mode ? mode.mode : editor.options.mode); if (mode && mode.mime) { editor.setOption('mode', mode.mime); } // Handle gutter clicks (place or remove a marker). editor.on('gutterClick', function (ed, n, gutter) { if (gutter != 'CodeMirror-markergutter') { return; } var info = ed.lineInfo(n), hasMarker = !!info.gutterMarkers && !!info.gutterMarkers['CodeMirror-markergutter']; ed.setGutterMarker(n, 'CodeMirror-markergutter', hasMarker ? null : makeMarker()); }); // jQuery's ready function. $(function () { // Some browsers do something weird with the fieldset which doesn't work well with CodeMirror. Fix it. $(editor.getWrapperElement()).parent('fieldset').css('min-width', 0); // Listen for Bootstrap's 'shown' event. If this editor was in a hidden element when created, it may need to be refreshed. $(document.body).on('shown shown.bs.tab shown.bs.modal', function () { editor.refresh(); }); }); }); function makeMarker() { var marker = document.createElement('div'); marker.className = 'CodeMirror-markergutter-mark'; return marker; } // Initialize any CodeMirrors on page load and when a subform is added $(function ($) { initCodeMirror(); $('body').on('subform-row-add', initCodeMirror); }); function initCodeMirror(event, container) { container = container || document; $(container).find('textarea.codemirror-source').each(function () { var input = $(this).removeClass('codemirror-source'); var id = input.prop('id'); Joomla.editors.instances[id] = cm.fromTextArea(this, input.data('options')); }); } }(CodeMirror, jQuery)); JS ); editors/codemirror/layouts/editors/codemirror/styles.php 0000644 00000004502 14735702224 0017743 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.codemirror * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access defined('_JEXEC') or die; $params = $displayData->params; $fontFamily = isset($displayData->fontFamily) ? $displayData->fontFamily : 'monospace'; $fontSize = $params->get('fontSize', 13) . 'px;'; $lineHeight = $params->get('lineHeight', 1.2) . 'em;'; // Set the active line color. $color = $params->get('activeLineColor', '#a4c2eb'); $r = hexdec($color[1] . $color[2]); $g = hexdec($color[3] . $color[4]); $b = hexdec($color[5] . $color[6]); $activeLineColor = 'rgba(' . $r . ', ' . $g . ', ' . $b . ', .5)'; // Set the color for matched tags. $color = $params->get('highlightMatchColor', '#fa542f'); $r = hexdec($color[1] . $color[2]); $g = hexdec($color[3] . $color[4]); $b = hexdec($color[5] . $color[6]); $highlightMatchColor = 'rgba(' . $r . ', ' . $g . ', ' . $b . ', .5)'; JFactory::getDocument()->addStyleDeclaration( <<<CSS .CodeMirror { font-family: $fontFamily; font-size: $fontSize; line-height: $lineHeight; border: 1px solid #ccc; } /* In order to hid the Joomla menu */ .CodeMirror-fullscreen { z-index: 1040; } /* Make the fold marker a little more visible/nice */ .CodeMirror-foldmarker { background: rgb(255, 128, 0); background: rgba(255, 128, 0, .5); box-shadow: inset 0 0 2px rgba(255, 255, 255, .5); font-family: serif; font-size: 90%; border-radius: 1em; padding: 0 1em; vertical-align: middle; color: white; text-shadow: none; } .CodeMirror-foldgutter, .CodeMirror-markergutter { width: 1.2em; text-align: center; } .CodeMirror-markergutter { cursor: pointer; } .CodeMirror-markergutter-mark { cursor: pointer; text-align: center; } .CodeMirror-markergutter-mark:after { content: "\25CF"; } .CodeMirror-activeline-background { background: $activeLineColor; } .CodeMirror-matchingtag { background: $highlightMatchColor; } .cm-matchhighlight {background-color: $highlightMatchColor; } .CodeMirror-selection-highlight-scrollbar {background-color: $highlightMatchColor; } CSS ); editors/none/none.php 0000644 00000007565 14735702224 0010647 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.none * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Plain Textarea Editor Plugin * * @since 1.5 */ class PlgEditorNone extends JPlugin { /** * Method to handle the onInitEditor event. * - Initialises the Editor * * @return void * * @since 1.5 */ public function onInit() { JHtml::_('script', 'editors/none/none.min.js', array('version' => 'auto', 'relative' => true)); } /** * Copy editor content to form field. * * Not applicable in this editor. * * @param string $editor the editor id * * @return void * * @deprecated 4.0 Use directly the returned code */ public function onSave($editor) { } /** * Get the editor content. * * @param string $id The id of the editor field. * * @return string * * @deprecated 4.0 Use directly the returned code */ public function onGetContent($id) { return 'Joomla.editors.instances[' . json_encode($id) . '].getValue();'; } /** * Set the editor content. * * @param string $id The id of the editor field. * @param string $html The content to set. * * @return string * * @deprecated 4.0 Use directly the returned code */ public function onSetContent($id, $html) { return 'Joomla.editors.instances[' . json_encode($id) . '].setValue(' . json_encode($html) . ');'; } /** * Inserts html code into the editor * * @param string $id The id of the editor field * * @return void * * @deprecated 4.0 */ public function onGetInsertMethod($id) { } /** * Display the editor area. * * @param string $name The control name. * @param string $content The contents of the text area. * @param string $width The width of the text area (px or %). * @param string $height The height of the text area (px or %). * @param integer $col The number of columns for the textarea. * @param integer $row The number of rows for the textarea. * @param boolean $buttons True and the editor buttons will be displayed. * @param string $id An optional ID for the textarea (note: since 1.6). If not supplied the name is used. * @param string $asset The object asset * @param object $author The author. * @param array $params Associative array of editor parameters. * * @return string */ public function onDisplay($name, $content, $width, $height, $col, $row, $buttons = true, $id = null, $asset = null, $author = null, $params = array()) { if (empty($id)) { $id = $name; } // Only add "px" to width and height if they are not given as a percentage if (is_numeric($width)) { $width .= 'px'; } if (is_numeric($height)) { $height .= 'px'; } $readonly = !empty($params['readonly']) ? ' readonly disabled' : ''; $editor = '<div class="js-editor-none">' . '<textarea name="' . $name . '" id="' . $id . '" cols="' . $col . '" rows="' . $row . '" style="width: ' . $width . '; height: ' . $height . ';"' . $readonly . '>' . $content . '</textarea>' . $this->_displayButtons($id, $buttons, $asset, $author) . '</div>'; return $editor; } /** * Displays the editor buttons. * * @param string $name The control name. * @param mixed $buttons [array with button objects | boolean true to display buttons] * @param string $asset The object asset * @param object $author The author. * * @return void|string HTML */ public function _displayButtons($name, $buttons, $asset, $author) { if (is_array($buttons) || (is_bool($buttons) && $buttons)) { $buttons = $this->_subject->getButtons($name, $buttons, $asset, $author); return JLayoutHelper::render('joomla.editors.buttons', $buttons); } } } editors/none/none.xml 0000644 00000001370 14735702224 0010644 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <extension version="3.1" type="plugin" group="editors" method="upgrade"> <name>plg_editors_none</name> <version>3.0.0</version> <creationDate>September 2005</creationDate> <author>Joomla! Project</author> <authorEmail>admin@joomla.org</authorEmail> <authorUrl>www.joomla.org</authorUrl> <copyright>(C) 2005 Open Source Matters, Inc.</copyright> <license>GNU General Public License version 2 or later; see LICENSE.txt</license> <description>PLG_NONE_XML_DESCRIPTION</description> <files> <filename plugin="none">none.php</filename> </files> <languages> <language tag="en-GB">en-GB.plg_editors_none.ini</language> <language tag="en-GB">en-GB.plg_editors_none.sys.ini</language> </languages> </extension> editors/tinymce/field/skins.php 0000644 00000002740 14735702224 0012621 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.tinymce * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; jimport('joomla.form.helper'); JFormHelper::loadFieldClass('list'); /** * Generates the list of options for available skins. * * @package Joomla.Plugin * @subpackage Editors.tinymce * @since 3.4 */ class JFormFieldSkins extends JFormFieldList { protected $type = 'skins'; /** * Method to get the skins options. * * @return array The skins option objects. * * @since 3.4 */ public function getOptions() { $options = array(); $directories = glob(JPATH_ROOT . '/media/editors/tinymce/skins' . '/*', GLOB_ONLYDIR); for ($i = 0, $iMax = count($directories); $i < $iMax; ++$i) { $dir = basename($directories[$i]); $options[] = JHtml::_('select.option', $i, $dir); } $options = array_merge(parent::getOptions(), $options); return $options; } /** * Method to get the field input markup for the list of skins. * * @return string The field input markup. * * @since 3.4 */ protected function getInput() { $html = array(); // Get the field options. $options = (array) $this->getOptions(); // Create a regular list. $html[] = JHtml::_('select.genericlist', $options, $this->name, '', 'value', 'text', $this->value, $this->id); return implode($html); } } editors/tinymce/field/tinymcebuilder.php 0000644 00000017317 14735702224 0014517 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.tinymce * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; extract($displayData); /** * Layout variables * ----------------- * @var string $autocomplete Autocomplete attribute for the field. * @var boolean $autofocus Is autofocus enabled? * @var string $class Classes for the input. * @var string $description Description of the field. * @var boolean $disabled Is this field disabled? * @var string $group Group the field belongs to. <fields> section in form XML. * @var boolean $hidden Is this field hidden in the form? * @var string $hint Placeholder for the field. * @var string $id DOM id of the field. * @var string $label Label of the field. * @var string $labelclass Classes to apply to the label. * @var boolean $multiple Does this field support multiple values? * @var string $name Name of the input field. * @var string $onchange Onchange attribute for the field. * @var string $onclick Onclick attribute for the field. * @var string $pattern Pattern (Reg Ex) of value of the form field. * @var boolean $readonly Is this field read only? * @var boolean $repeat Allows extensions to duplicate elements. * @var boolean $required Is this field required? * @var integer $size Size attribute of the input. * @var boolean $spellcheck Spellcheck state for the form field. * @var string $validate Validation rules to apply. * @var array $value Value of the field. * * @var array $menus List of the menu items * @var array $menubarSource Menu items for builder * @var array $buttons List of the buttons * @var array $buttonsSource Buttons by group, for the builder * @var array $toolbarPreset Toolbar preset (default values) * @var int $setsAmount Amount of sets * @var array $setsNames List of Sets names * @var JForm[] $setsForms Form with extra options for an each set * @var string $languageFile TinyMCE language file to translate the buttons * * @var JLayoutFile $this Context */ JHtml::_('behavior.core'); JHtml::_('stylesheet', 'media/editors/tinymce/skins/lightgray/skin.min.css', array('version' => 'auto', 'relative' => false)); JHtml::_('jquery.ui', array('core', 'sortable')); JHtml::_('script', 'editors/tinymce/tinymce-builder.js', array('version' => 'auto', 'relative' => true)); if ($languageFile) { JHtml::_('script', $languageFile, array('version' => 'auto', 'relative' => false)); } $doc = JFactory::getDocument(); $doc->addScriptOptions('plg_editors_tinymce_builder', array( 'menus' => $menus, 'buttons' => $buttons, 'toolbarPreset' => $toolbarPreset, 'formControl' => $name . '[toolbars]', ) ); $doc->addStyleDeclaration(' #joomla-tinymce-builder{ margin-left: -180px; } .mce-menubar, .mce-panel { min-height: 18px; border-bottom: 1px solid rgba(217,217,217,0.52); white-space: normal; } .mce-tinymce { margin-bottom: 20px; } .mce-panel .drop-area-highlight{ background-color: #d0d0d0; } .mce-panel .mce-btn.ui-state-highlight{ height: 28px; width: 40px; background-color: #409740; border: 1px solid #f0f0f0; } .tinymce-builder-toolbar .mce-btn.ui-state-highlight{ height: 22px; width: 28px; } '); ?> <div id="joomla-tinymce-builder"> <p><?php echo JText::_('PLG_TINY_SET_SOURCE_PANEL_DESCRIPTION'); ?></p> <div class="mce-tinymce mce-container mce-panel"> <div class="mce-container-body mce-stack-layout"> <div class="mce-container mce-menubar mce-toolbar mce-stack-layout-item"> <div class="mce-container-body mce-flow-layout tinymce-builder-menu source" data-group="menu" data-value="<?php echo $this->escape(json_encode($menubarSource)); ?>"> </div> </div> <div class="mce-toolbar-grp mce-container mce-panel mce-stack-layout-item"> <div class="mce-container-body mce-flow-layout tinymce-builder-toolbar source" data-group="toolbar" data-value="<?php echo $this->escape(json_encode($buttonsSource)); ?>"> </div> </div> </div> </div> <hr /> <p><?php echo JText::_('PLG_TINY_SET_TARGET_PANEL_DESCRIPTION'); ?></p> <!-- Render tabs for each set --> <ul class="nav nav-tabs" id="set-tabs"> <?php foreach ( $setsNames as $num => $title ) : ?> <li class="<?php echo $num === $setsAmount - 1 ? 'active' : ''; ?>"> <a href="#set-<?php echo $num; ?>"><?php echo $title; ?></a> </li> <?php endforeach; ?> </ul> <!-- Render tab content for each set --> <div class="tab-content"> <?php $presetButtonClases = array( 'simple' => 'btn-success', 'medium' => 'btn-info', 'advanced' => 'btn-warning', ); foreach ( $setsNames as $num => $title ) : // Check whether the values exists, and if empty then use from preset if (empty($value['toolbars'][$num]['menu']) && empty($value['toolbars'][$num]['toolbar1']) && empty($value['toolbars'][$num]['toolbar2'])) { // Take the preset for default value switch ($num) { case 0: $preset = $toolbarPreset['advanced']; break; case 1: $preset = $toolbarPreset['medium']; break; default: $preset = $toolbarPreset['simple']; } $value['toolbars'][$num] = $preset; } // Take existing values $valMenu = empty($value['toolbars'][$num]['menu']) ? array() : $value['toolbars'][$num]['menu']; $valBar1 = empty($value['toolbars'][$num]['toolbar1']) ? array() : $value['toolbars'][$num]['toolbar1']; $valBar2 = empty($value['toolbars'][$num]['toolbar2']) ? array() : $value['toolbars'][$num]['toolbar2']; ?> <div class="tab-pane <?php echo $num === $setsAmount - 1 ? 'active' : ''; ?>" id="set-<?php echo $num; ?>"> <div class="btn-toolbar clearfix"> <div class="btn-group pull-right"> <?php foreach(array_keys($toolbarPreset) as $presetName) : $btnClass = empty($presetButtonClases[$presetName]) ? 'btn-primary' : $presetButtonClases[$presetName]; ?> <button type="button" class="btn btn-mini <?php echo $btnClass; ?> button-action" data-action="setPreset" data-preset="<?php echo $presetName; ?>" data-set="<?php echo $num; ?>"> <?php echo JText::_('PLG_TINY_SET_PRESET_BUTTON_' . $presetName); ?> </button> <?php endforeach; ?> <button type="button" class="btn btn-mini btn-danger button-action" data-action="clearPane" data-set="<?php echo $num; ?>"> <?php echo JText::_('JCLEAR'); ?></button> </div> </div> <div class="mce-tinymce mce-container mce-panel"> <div class="mce-container-body mce-stack-layout"> <div class="mce-container mce-menubar mce-toolbar tinymce-builder-menu target" data-group="menu" data-set="<?php echo $num; ?>" data-value="<?php echo $this->escape(json_encode($valMenu)); ?>"> </div> <div class="mce-toolbar-grp mce-container mce-panel tinymce-builder-toolbar target" data-group="toolbar1" data-set="<?php echo $num; ?>" data-value="<?php echo $this->escape(json_encode($valBar1)); ?>"> </div> <div class="mce-toolbar-grp mce-container mce-panel tinymce-builder-toolbar target" data-group="toolbar2" data-set="<?php echo $num; ?>" data-value="<?php echo $this->escape(json_encode($valBar2)); ?>"> </div> </div> </div> <!-- Render the form for extra options --> <?php echo $this->sublayout('setoptions', array('form' => $setsForms[$num])); ?> </div> <?php endforeach; ?> </div> </div> editors/tinymce/field/uploaddirs.php 0000644 00000004550 14735702224 0013641 0 ustar 00 <?php /** * @package Joomla.Plugin * @subpackage Editors.tinymce * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; jimport('joomla.form.helper'); JFormHelper::loadFieldClass('folderlist'); /** * Generates the list of directories available for drag and drop upload. * * @package Joomla.Plugin * @subpackage Editors.tinymce * @since 3.7.0 */ class JFormFieldUploaddirs extends JFormFieldFolderList { protected $type = 'uploaddirs'; /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.7.0 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); // Get the path in which to search for file options. $this->directory = JComponentHelper::getParams('com_media')->get('image_path'); $this->recursive = true; $this->hideDefault = true; return $return; } /** * Method to get the directories options. * * @return array The dirs option objects. * * @since 3.7.0 */ public function getOptions() { return parent::getOptions(); } /** * Method to get the field input markup for the list of directories. * * @return string The field input markup. * * @since 3.7.0 */ protected function getInput() { $html = array(); // Get the field options. $options = (array) $this->getOptions(); // Reset the non selected value to null if ($options[0]->value === '-1') { $options[0]->value = ''; } // Create a regular list. $html[] = JHtml::_('select.genericlist', $options, $this->name, '', 'value', 'text', $this->value, $this->id); return implode($html); } } editors/tinymce/form/setoptions.xml 0000644 00000017641 14735702224 0013600 0 ustar 00 <?xml version="1.0" encoding="utf-8"?> <form> <field name="access" type="usergrouplist" label="PLG_TINY_FIELD_SETACCESS_LABEL" description="PLG_TINY_FIELD_SETACCESS_DESC" multiple="true" class="access-select" labelclass="label label-success" /> <field name="skins" type="note" label="PLG_TINY_FIELD_SKIN_INFO_LABEL" description="PLG_TINY_FIELD_SKIN_INFO_DESC" /> <field name="skin" type="skins" label="PLG_TINY_FIELD_SKIN_LABEL" description="PLG_TINY_FIELD_SKIN_DESC" /> <field name="skin_admin" type="skins" label="PLG_TINY_FIELD_SKIN_ADMIN_LABEL" description="PLG_TINY_FIELD_SKIN_ADMIN_DESC" /> <field name="mobile" type="radio" label="PLG_TINY_FIELD_MOBILE_LABEL" description="PLG_TINY_FIELD_MOBILE_DESC" class="btn-group btn-group-yesno" default="0" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="drag_drop" type="radio" label="PLG_TINY_FIELD_DRAG_DROP_LABEL" description="PLG_TINY_FIELD_DRAG_DROP_DESC" class="btn-group btn-group-yesno" default="1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="path" type="uploaddirs" label="PLG_TINY_FIELD_CUSTOM_PATH_LABEL" description="PLG_TINY_FIELD_CUSTOM_PATH_DESC" class="input-xxlarge" showon="drag_drop:1" /> <field name="entity_encoding" type="list" label="PLG_TINY_FIELD_ENCODING_LABEL" description="PLG_TINY_FIELD_ENCODING_DESC" default="raw" > <option value="named">PLG_TINY_FIELD_VALUE_NAMED</option> <option value="numeric">PLG_TINY_FIELD_VALUE_NUMERIC</option> <option value="raw">PLG_TINY_FIELD_VALUE_RAW</option> </field> <field name="lang_mode" type="radio" label="PLG_TINY_FIELD_LANGSELECT_LABEL" description="PLG_TINY_FIELD_LANGSELECT_DESC" class="btn-group btn-group-yesno" default="1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="lang_code" type="filelist" label="PLG_TINY_FIELD_LANGCODE_LABEL" description="PLG_TINY_FIELD_LANGCODE_DESC" class="inputbox" stripext="1" directory="media/editors/tinymce/langs/" hide_none="1" default="en" hide_default="1" filter="\.js$" size="10" showon="lang_mode:0" /> <field name="text_direction" type="list" label="PLG_TINY_FIELD_DIRECTION_LABEL" description="PLG_TINY_FIELD_DIRECTION_DESC" default="ltr" > <option value="ltr">PLG_TINY_FIELD_VALUE_LTR</option> <option value="rtl">PLG_TINY_FIELD_VALUE_RTL</option> </field> <field name="content_css" type="radio" label="PLG_TINY_FIELD_CSS_LABEL" description="PLG_TINY_FIELD_CSS_DESC" class="btn-group btn-group-yesno" default="1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="content_css_custom" type="text" label="PLG_TINY_FIELD_CUSTOM_CSS_LABEL" description="PLG_TINY_FIELD_CUSTOM_CSS_DESC" class="input-xxlarge" /> <field name="relative_urls" type="list" label="PLG_TINY_FIELD_URLS_LABEL" description="PLG_TINY_FIELD_URLS_DESC" default="1" > <option value="0">PLG_TINY_FIELD_VALUE_ABSOLUTE</option> <option value="1">PLG_TINY_FIELD_VALUE_RELATIVE</option> </field> <field name="newlines" type="list" label="PLG_TINY_FIELD_NEWLINES_LABEL" description="PLG_TINY_FIELD_NEWLINES_DESC" default="0" > <option value="1">PLG_TINY_FIELD_VALUE_BR</option> <option value="0">PLG_TINY_FIELD_VALUE_P</option> </field> <field name="use_config_textfilters" type="radio" label="PLG_TINY_CONFIG_TEXTFILTER_ACL_LABEL" description="PLG_TINY_CONFIG_TEXTFILTER_ACL_DESC" class="btn-group btn-group-yesno" default="0" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="invalid_elements" type="text" label="PLG_TINY_FIELD_PROHIBITED_LABEL" description="PLG_TINY_FIELD_PROHIBITED_DESC" showon="use_config_textfilters:0" default="script,applet,iframe" class="input-xxlarge" /> <field name="valid_elements" type="text" label="PLG_TINY_FIELD_VALIDELEMENTS_LABEL" description="PLG_TINY_FIELD_VALIDELEMENTS_DESC" showon="use_config_textfilters:0" class="input-xxlarge" /> <field name="extended_elements" type="text" label="PLG_TINY_FIELD_ELEMENTS_LABEL" description="PLG_TINY_FIELD_ELEMENTS_DESC" showon="use_config_textfilters:0" class="input-xxlarge" /> <!-- Extra plugins --> <field name="resizing" type="radio" label="PLG_TINY_FIELD_RESIZING_LABEL" description="PLG_TINY_FIELD_RESIZING_DESC" class="btn-group btn-group-yesno" default="1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="resize_horizontal" type="radio" label="PLG_TINY_FIELD_RESIZE_HORIZONTAL_LABEL" description="PLG_TINY_FIELD_RESIZE_HORIZONTAL_DESC" class="btn-group btn-group-yesno" default="1" showon="resizing:1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="element_path" type="radio" label="PLG_TINY_FIELD_PATH_LABEL" description="PLG_TINY_FIELD_PATH_DESC" class="btn-group btn-group-yesno" default="0" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="wordcount" type="radio" label="PLG_TINY_FIELD_WORDCOUNT_LABEL" description="PLG_TINY_FIELD_WORDCOUNT_DESC" class="btn-group btn-group-yesno" default="1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="image_advtab" type="radio" label="PLG_TINY_FIELD_ADVIMAGE_LABEL" description="PLG_TINY_FIELD_ADVIMAGE_DESC" class="btn-group btn-group-yesno" default="1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="advlist" type="radio" label="PLG_TINY_FIELD_ADVLIST_LABEL" description="PLG_TINY_FIELD_ADVLIST_DESC" class="btn-group btn-group-yesno" default="1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="contextmenu" type="radio" label="PLG_TINY_FIELD_CONTEXTMENU_LABEL" description="PLG_TINY_FIELD_CONTEXTMENU_DESC" class="btn-group btn-group-yesno" default="1" > <option value="1">JON</option> <option value="0">JOFF</option> </field> <field name="custom_plugin" type="text" label="PLG_TINY_FIELD_CUSTOMPLUGIN_LABEL" description="PLG_TINY_FIELD_CUSTOMPLUGIN_DESC" class="input-xxlarge" /> <field name="custom_button" type="text" label="PLG_TINY_FIELD_CUSTOMBUTTON_LABEL" description="PLG_TINY_FIELD_CUSTOMBUTTON_DESC" class="input-xxlarge" /> </form>