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