From d4f2a75a40b6f4a015ae29e8c4ea5547e20d9f22 Mon Sep 17 00:00:00 2001 From: Thomas Steur <thomas.steur@gmail.com> Date: Tue, 22 Oct 2013 04:05:21 +0000 Subject: [PATCH] refs #4126 started to work on plugin settings --- core/Plugin/Settings.php | 184 ++++++++++++++++++ core/Settings/Manager.php | 65 +++++++ plugins/CoreAdminHome/Controller.php | 54 +++++ plugins/CoreAdminHome/CoreAdminHome.php | 10 + .../javascripts/pluginSettings.js | 60 ++++++ .../templates/pluginSettings.twig | 62 ++++++ 6 files changed, 435 insertions(+) create mode 100644 core/Plugin/Settings.php create mode 100644 core/Settings/Manager.php create mode 100644 plugins/CoreAdminHome/javascripts/pluginSettings.js create mode 100644 plugins/CoreAdminHome/templates/pluginSettings.twig diff --git a/core/Plugin/Settings.php b/core/Plugin/Settings.php new file mode 100644 index 0000000000..9fc7fa9f3b --- /dev/null +++ b/core/Plugin/Settings.php @@ -0,0 +1,184 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + * @category Piwik + * @package Piwik + */ +namespace Piwik\Plugin; + +use Piwik\Option; +use Piwik\Piwik; + +class Settings +{ + const TYPE_INT = 'integer'; + const TYPE_FLOAT = 'float'; + const TYPE_STRING = 'string'; + const TYPE_BOOL = 'boolean'; + const TYPE_ARRAY = 'array'; + + const FIELD_TEXT = 'text'; + const FIELD_TEXTBOX = 'textbox'; + const FIELD_RADIO = 'radio'; + const FIELD_CHECKBOX = 'checkbox'; + const FIELD_MULTI_SELECT = 'multiselect'; + const FIELD_SINGLE_SELECT = 'select'; + + // what about stuff like date etc? + + protected $defaultTypes = array(); + protected $defaultFields = array(); + protected $defaultOptions = array(); + + protected $settings = array(); + protected $settingsValues = array(); + + private $pluginName; + + public function __construct($pluginName) + { + $this->pluginName = $pluginName; + + $this->defaultTypes = array( + static::FIELD_TEXT => static::TYPE_STRING, + static::FIELD_TEXTBOX => static::TYPE_STRING, + static::FIELD_RADIO => static::TYPE_STRING, + static::FIELD_CHECKBOX => static::TYPE_BOOL, + static::FIELD_MULTI_SELECT => static::TYPE_ARRAY, + static::FIELD_SINGLE_SELECT => static::TYPE_STRING, + ); + $this->defaultFields = array( + static::TYPE_INT => static::FIELD_TEXT, + static::TYPE_FLOAT => static::FIELD_TEXT, + static::TYPE_STRING => static::FIELD_TEXT, + static::TYPE_BOOL => static::FIELD_CHECKBOX, + static::TYPE_ARRAY => static::FIELD_MULTI_SELECT, + ); + $this->defaultOptions = array( + 'type' => static::TYPE_STRING, + 'field' => static::FIELD_TEXT, + 'displayedForCurrentUser' => Piwik::isUserHasSomeAdminAccess(), + 'fieldAttributes' => array(), + 'description' => null, + 'inlineHelp' => null, + 'filter' => null, + 'validate' => null, + ); + + $this->init(); + $this->loadSettings(); + } + + protected function init() + { + } + + protected function addSetting($name, $title, array $options = array()) + { + if (array_key_exists('field', $options) && !array_key_exists('type', $options)) { + $options['type'] = $this->defaultTypes[$options['field']]; + } elseif (array_key_exists('type', $options) && !array_key_exists('field', $options)) { + $options['field'] = $this->defaultFields[$options['type']]; + } + + $setting = array_merge($this->defaultOptions, $options); + $setting['name'] = $name; + $setting['title'] = $title; + + $this->settings[] = $setting; + } + + public function getSettingsForCurrentUser() + { + return array_values(array_filter($this->getSettings(), function ($setting) { + return $setting['displayedForCurrentUser']; + })); + } + + public function getSettingValue($name) + { + $this->checkIsValidSetting($name); + + if (!array_key_exists($name, $this->settingsValues)) { + $setting = $this->getSetting($name); + + return $setting['defaultValue']; + } + + return $this->settingsValues[$name]; + } + + public function setSettingValue($name, $value) + { + $this->checkIsValidSetting($name); + $setting = $this->getSetting($name); + + if ($setting['validate'] && $setting['validate'] instanceof \Closure) { + call_user_func($setting['validate'], $value, $setting); + } + + if ($setting['filter'] && $setting['filter'] instanceof \Closure) { + $value = call_user_func($setting['filter'], $value, $setting); + } else { + settype($value, $setting['type']); + } + + $this->settingsValues[$name] = $value; + } + + public function save() + { + Option::set($this->getOptionKey(), serialize($this->settingsValues)); + } + + public function removeAllPluginSettings() + { + Option::delete($this->getOptionKey()); + } + + private function getOptionKey() + { + return 'Plugin_' . $this->pluginName . '_Settings'; + } + + private function loadSettings() + { + $values = Option::get($this->getOptionKey()); + + if (!empty($values)) { + $this->settingsValues = unserialize($values); + } + } + + private function checkIsValidSetting($name) + { + $setting = $this->getSetting($name); + + if (empty($setting)) { + throw new \Exception('This setting does not exist'); + } + + if (!$setting['displayedForCurrentUser']) { + throw new \Exception('You are not allowed to change the value of this setting'); + } + } + + private function getSettings() + { + return $this->settings; + } + + private function getSetting($name) + { + foreach ($this->settings as $setting) { + if ($name == $setting['name']) { + return $setting; + } + } + } + +} diff --git a/core/Settings/Manager.php b/core/Settings/Manager.php new file mode 100644 index 0000000000..045ae4e49b --- /dev/null +++ b/core/Settings/Manager.php @@ -0,0 +1,65 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + * @category Piwik + * @package Piwik + */ + +namespace Piwik\Settings; + +use Piwik\Piwik; + +/** + * Settings manager + * + * @package Piwik + * @subpackage Manager + */ +class Manager +{ + private static $settings = array(); + + /** + * @return \Piwik\Plugin\Settings[] + */ + public static function getAllPluginSettings() + { + if (empty(static::$settings)) { + + $pluginSettings = array('Login' => 'Piwik\\Plugins\\Login\\Settings'); + // TODO: document hook and think about better name + + Piwik::postEvent('Plugin.addSettings', $pluginSettings); + + $settings = array(); + foreach ($pluginSettings as $pluginName => $pluginSetting) { + $settings[$pluginName] = new $pluginSetting($pluginName); + } + + static::$settings = $settings; + } + + return static::$settings; + } + + /** + * @return bool + */ + public static function hasPluginSettingsForCurrentUser() + { + $settings = static::getAllPluginSettings(); + + foreach ($settings as $setting) { + $forUser = $setting->getSettingsForCurrentUser(); + if (!empty($forUser)) { + return true; + } + } + + return false; + } +} diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php index 34ad95a7e2..ddc71608bf 100644 --- a/plugins/CoreAdminHome/Controller.php +++ b/plugins/CoreAdminHome/Controller.php @@ -11,19 +11,24 @@ namespace Piwik\Plugins\CoreAdminHome; use Exception; +use Piwik\API\Request; use Piwik\API\ResponseBuilder; use Piwik\ArchiveProcessor\Rules; use Piwik\Common; use Piwik\Config; +use Piwik\DataTable\Renderer\Json; use Piwik\Menu\MenuTop; use Piwik\Nonce; use Piwik\Piwik; +use Piwik\Settings\Manager as SettingsManager; +use Piwik\Plugin\Manager; use Piwik\Plugins\LanguagesManager\API as APILanguagesManager; use Piwik\Plugins\LanguagesManager\LanguagesManager; use Piwik\Plugins\SitesManager\API as APISitesManager; use Piwik\Site; use Piwik\Tracker\IgnoreCookie; use Piwik\Url; +use Piwik\UrlHelper; use Piwik\View; /** @@ -88,6 +93,55 @@ class Controller extends \Piwik\Plugin\ControllerAdmin echo $view->render(); } + public function pluginSettings() + { + Piwik::checkUserIsNotAnonymous(); + + $view = new View('@CoreAdminHome/pluginSettings'); + + $view->pluginSettings = SettingsManager::getAllPluginSettings(); + $this->setBasicVariablesView($view); + + echo $view->render(); + } + + public function setPluginSettings() + { + Piwik::checkUserIsNotAnonymous(); + Json::sendHeaderJSON(); + + $updateSettings = Common::getRequestVar('settings', null, 'json'); + $pluginSettings = SettingsManager::getAllPluginSettings(); + + try { + + foreach ($updateSettings as $pluginName => $serializedSetting) { + $unserializedSettings = UrlHelper::getArrayFromQueryString($serializedSetting); + + if (!array_key_exists($pluginName, $pluginSettings)) { + // this plugin is not using settings, skip it + continue; + } + + $pluginSetting = $pluginSettings[$pluginName]; + + foreach ($unserializedSettings as $key => $value) { + $pluginSetting->setSettingValue($key, $value); + } + } + + foreach ($pluginSettings as $pluginSetting) { + $pluginSetting->save(); + } + + } catch (Exception $e) { + echo json_encode(array('result' => 'error', 'message' => $e->getMessage())); + return; + } + + echo json_encode(array('result' => 'success')); + } + public function setGeneralSettings() { Piwik::checkUserIsSuperUser(); diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php index 923c729555..2ae9202a3e 100644 --- a/plugins/CoreAdminHome/CoreAdminHome.php +++ b/plugins/CoreAdminHome/CoreAdminHome.php @@ -18,6 +18,7 @@ use Piwik\Menu\MenuAdmin; use Piwik\Piwik; use Piwik\ScheduledTask; use Piwik\ScheduledTime\Daily; +use Piwik\Settings\Manager as SettingsManager; /** * @@ -77,6 +78,7 @@ class CoreAdminHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/javascripts/broadcast.js"; $jsFiles[] = "plugins/CoreAdminHome/javascripts/generalSettings.js"; $jsFiles[] = "plugins/CoreHome/javascripts/donate.js"; + $jsFiles[] = "plugins/CoreAdminHome/javascripts/pluginSettings.js"; } function addMenu() @@ -92,6 +94,14 @@ class CoreAdminHome extends \Piwik\Plugin array('module' => 'CoreAdminHome', 'action' => 'trackingCodeGenerator'), Piwik::isUserHasSomeAdminAccess(), $order = 4); + + if (SettingsManager::hasPluginSettingsForCurrentUser()) { + MenuAdmin::getInstance()->add('General_Settings', 'General_Plugins', + array('module' => 'CoreAdminHome', 'action' => 'pluginSettings'), + Piwik::isUserHasSomeAdminAccess(), + $order = 7); + } + } function purgeOutdatedArchives() diff --git a/plugins/CoreAdminHome/javascripts/pluginSettings.js b/plugins/CoreAdminHome/javascripts/pluginSettings.js new file mode 100644 index 0000000000..9a6048b280 --- /dev/null +++ b/plugins/CoreAdminHome/javascripts/pluginSettings.js @@ -0,0 +1,60 @@ +/*! + * Piwik - Web Analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +$(document).ready(function () { + + $submit = $('.pluginsSettingsSubmit'); + + if (!$submit) { + return; + } + + $submit.click(updatePluginSettings); + + function updatePluginSettings() + { + var ajaxHandler = new ajaxHelper(); + ajaxHandler.addParams({ + module: 'CoreAdminHome', + action: 'setPluginSettings' + }, 'GET'); + ajaxHandler.addParams({settings: JSON.stringify(getSettings())}, 'POST'); + ajaxHandler.redirectOnSuccess(); + ajaxHandler.setLoadingElement(getLoadingElement()); + ajaxHandler.setErrorElement(getErrorElement()); + ajaxHandler.send(true); + } + + function getSettings() + { + var $pluginSections = $( "#pluginSettings[data-pluginname]" ); + + var values = {}; + + $pluginSections.each(function (index, pluginSection) { + $pluginSection = $(pluginSection); + + var pluginName = $pluginSection.attr('data-pluginname'); + var serialized = $('input, textarea, select', $pluginSection ).serialize(); + + values[pluginName] = serialized; + }); + + return values; + } + + function getErrorElement() + { + return $('#ajaxErrorPluginSettings'); + } + + function getLoadingElement() + { + return $('#ajaxLoadingPluginSettings'); + } + +}); \ No newline at end of file diff --git a/plugins/CoreAdminHome/templates/pluginSettings.twig b/plugins/CoreAdminHome/templates/pluginSettings.twig new file mode 100644 index 0000000000..ea98c12453 --- /dev/null +++ b/plugins/CoreAdminHome/templates/pluginSettings.twig @@ -0,0 +1,62 @@ +{% extends 'admin.twig' %} + +{% block content %} + + {% import 'macros.twig' as piwik %} + {% import 'ajaxMacros.twig' as ajax %} + + <h2>{{ 'CoreAdminHome_PluginSettings'|translate }}</h2> + + {{ ajax.errorDiv('ajaxErrorPluginSettings') }} + {{ ajax.loadingDiv('ajaxLoadingPluginSettings') }} + + <input type="submit" value="{{ 'General_Save'|translate }}" class="pluginsSettingsSubmit submit"/> + + {% for plugin, settings in pluginSettings %} + + <h3 id="{{ plugin }}">{{ plugin }}</h3> + + <table class="adminTable" style='width:620px;' id="pluginSettings" data-pluginname="{{ plugin }}"> + + {% for setting in settings.getSettingsForCurrentUser %} + <tr> + <td style='width:400px'> + {{ setting.title }} + <br /> + <span class='form-description'> + {{ setting.description }} + </span> + + </td> + <td style='width:220px'> + <fieldset> + <label> + <input type="{{ setting.field }}" + name="{{ setting.name }}" + {% for attr, val in setting.fieldAttributes %} + {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}" + {% endfor %} + value="{{ settings.getSettingValue(setting.name)|e('html_attr') }}" + > + + {% if setting.defaultValue %} + <br/> + <span class='form-description'> + {{ 'General_Default'|translate }} {{ setting.defaultValue }} + </span> + {% endif %} + + </label> + </fieldset> + </td> + </tr> + + {% endfor %} + + </table> + {% endfor %} + + + <input type="submit" value="{{ 'General_Save'|translate }}" class="pluginsSettingsSubmit submit"/> + +{% endblock %} \ No newline at end of file -- GitLab