diff options
author | Thomas Steur <thomas.steur@gmail.com> | 2016-03-10 00:55:45 +0300 |
---|---|---|
committer | Thomas Steur <thomas.steur@gmail.com> | 2016-04-11 05:11:33 +0300 |
commit | b52ae4e7e488e0474d67c54578e1d6c1aa066bff (patch) | |
tree | f94b02f774cbc24faaa18f29ee1e19fef8b338af | |
parent | 6ba622a68a26792af8cc22131f488f7ff5189d2c (diff) |
refs #7983 let plugins add or remove fields to websites and better settings api
197 files changed, 8835 insertions, 4151 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index cbe224bc10..007d94b312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,9 +18,12 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API' * The method `Piwik\Plugin\Controller::getEvolutionHtml` has been removed without a replacement as it should be no longer needed. The evolution is generated by ViewDataTables directly * The `core:plugin` console command has been removed in favor of the new `plugin:list`, `plugin:activate` and `plugin:deactivate` commands as anounced in Piwik 2.11 * The visibility of private properties and methods in `Piwik\Plugins\Login\Controller` were changed to `protected` -* Controller actions are now case sensitive. This means the URL and events have to use the same case as the name of the action defined in a controller. +* Controller actions are now case sensitive. This means the URL and events have to use the same case as the name of the action defined in a controller. * The "User Menu" was removed and should be replaced by "Admin Menu". Change `configureUserMenu(MenuUser $menu)` to `configureAdminMenu(MenuAdmin $menu)` in your `Menu.php`. * The method `Piwik\Menu\MenuAdmin::addSettingsItem()` was removed, use `Piwik\Menu\MenuAdmin::addManageItem()` instead. +* The class `Piwik\Plugin\Settings` has been splitted to `Piwik\Settings\Plugin\SystemSettings` and `Piwik\Settings\Plugin\UserSettings`. +* The creation of settings has slightly changed to improve performance. It is now possible to create new settings via the method `$this->makeSetting()` see `Piwik\Plugins\ExampleSettingsPlugin\SystemSettings` for an example. +* It is no possible to define an introduction text for settings. ### New APIs * Multiple widgets for one report can now be created via the `Report::configureWidgets()` method via the new classes `Piwik\Widget\ReportWidgetFactory` and `Piwik\Widget\ReportWidgetConfig` @@ -29,6 +32,7 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API' * The new class `Piwik\Category\Subcategory` let you change the name and order of menu items * New HTTP API method `API.getWidgetMetadata` to get a list of available widgets * New HTTP API method `API.getReportPagesMetadata` to get a list of all available pages that exist including the widgets they include +* New HTTP API method `SitesManager.getSiteSettings` to get a list of all available settings for a specific site * The JavaScript AjaxHelper has a new method `ajaxHelper.withTokenInUrl()` to easily send a token along a XHR. Within the Controller the existence of this token can be checked via `$this->checkTokenInUrl();` to prevent CSRF attacks. ### New features diff --git a/core/API/DataTableManipulator/ReportTotalsCalculator.php b/core/API/DataTableManipulator/ReportTotalsCalculator.php index 7906077b1e..e1e467d0c1 100644 --- a/core/API/DataTableManipulator/ReportTotalsCalculator.php +++ b/core/API/DataTableManipulator/ReportTotalsCalculator.php @@ -13,7 +13,7 @@ use Piwik\DataTable; use Piwik\Metrics; use Piwik\Period; use Piwik\Plugin\Report; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; /** * This class is responsible for setting the metadata property 'totals' on each dataTable if the report @@ -212,7 +212,7 @@ class ReportTotalsCalculator extends DataTableManipulator private function findFirstLevelReport() { - $reports = new Reports(); + $reports = new ReportsProvider(); foreach ($reports->getAllReports() as $report) { $actionToLoadSubtables = $report->getActionToLoadSubTables(); if ($actionToLoadSubtables == $this->apiMethod diff --git a/core/API/DataTablePostProcessor.php b/core/API/DataTablePostProcessor.php index c6424ea793..c2fdd1569c 100644 --- a/core/API/DataTablePostProcessor.php +++ b/core/API/DataTablePostProcessor.php @@ -19,7 +19,7 @@ use Piwik\DataTable\Filter\PivotByDimension; use Piwik\Metrics\Formatter; use Piwik\Plugin\ProcessedMetric; use Piwik\Plugin\Report; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; /** * Processes DataTables that should be served through Piwik's APIs. This processing handles @@ -72,7 +72,7 @@ class DataTablePostProcessor $this->apiMethod = $apiMethod; $this->setRequest($request); - $this->report = Reports::factory($apiModule, $apiMethod); + $this->report = ReportsProvider::factory($apiModule, $apiMethod); $this->apiInconsistencies = new Inconsistencies(); $this->setFormatter(new Formatter()); } diff --git a/core/DataTable/Filter/PivotByDimension.php b/core/DataTable/Filter/PivotByDimension.php index bbc72f46d9..f668afb0b9 100644 --- a/core/DataTable/Filter/PivotByDimension.php +++ b/core/DataTable/Filter/PivotByDimension.php @@ -21,7 +21,7 @@ use Piwik\Period; use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugin\Segment; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Site; /** @@ -329,7 +329,7 @@ class PivotByDimension extends BaseFilter { list($module, $method) = explode('.', $report); - $this->thisReport = Reports::factory($module, $method); + $this->thisReport = ReportsProvider::factory($module, $method); if (empty($this->thisReport)) { throw new Exception("Unable to find report '$report'."); } diff --git a/core/DataTable/Renderer/Xml.php b/core/DataTable/Renderer/Xml.php index b01f5596a2..31e56a4fde 100644 --- a/core/DataTable/Renderer/Xml.php +++ b/core/DataTable/Renderer/Xml.php @@ -176,9 +176,9 @@ class Xml extends Renderer } // render the array item - if (is_array($value)) { + if (is_array($value) || $value instanceof \stdClass) { $result .= $prefixLines . $prefix . "\n"; - $result .= $this->renderArray($value, $prefixLines . "\t"); + $result .= $this->renderArray((array) $value, $prefixLines . "\t"); $result .= $prefixLines . $suffix . "\n"; } elseif ($value instanceof DataTable || $value instanceof Map @@ -198,6 +198,7 @@ class Xml extends Renderer } } else { $xmlValue = self::formatValueXml($value); + if (strlen($xmlValue) != 0) { $result .= $prefixLines . $prefix . $xmlValue . $suffix . "\n"; } else { diff --git a/core/Db/Schema/Mysql.php b/core/Db/Schema/Mysql.php index 07cb1490d0..61532c47ef 100644 --- a/core/Db/Schema/Mysql.php +++ b/core/Db/Schema/Mysql.php @@ -76,11 +76,21 @@ class Mysql implements SchemaInterface ) ENGINE=$engine DEFAULT CHARSET=utf8 ", + 'plugin_setting' => "CREATE TABLE {$prefixTables}plugin_setting ( + `plugin_name` VARCHAR(60) NOT NULL, + `setting_name` VARCHAR(255) NOT NULL, + `setting_value` LONGTEXT NOT NULL, + `user_login` VARCHAR(100) NOT NULL DEFAULT '', + INDEX(plugin_name, user_login) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + ", + 'site_setting' => "CREATE TABLE {$prefixTables}site_setting ( idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `plugin_name` VARCHAR(60) NOT NULL, `setting_name` VARCHAR(255) NOT NULL, `setting_value` LONGTEXT NOT NULL, - PRIMARY KEY(idsite, setting_name) + INDEX(idsite, plugin_name) ) ENGINE=$engine DEFAULT CHARSET=utf8 ", diff --git a/core/Http/ControllerResolver.php b/core/Http/ControllerResolver.php index e0fe7d98e3..b9e71b0f11 100644 --- a/core/Http/ControllerResolver.php +++ b/core/Http/ControllerResolver.php @@ -12,9 +12,9 @@ use DI\FactoryInterface; use Exception; use Piwik\Plugin; use Piwik\Plugin\Controller; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Session; -use Piwik\Plugin\Widgets; +use Piwik\Plugin\WidgetsProvider; /** * Resolves the controller that will handle the request. @@ -29,11 +29,11 @@ class ControllerResolver private $abstractFactory; /** - * @var Widgets + * @var WidgetsProvider */ private $widgets; - public function __construct(FactoryInterface $abstractFactory, Widgets $widgets) + public function __construct(FactoryInterface $abstractFactory, WidgetsProvider $widgets) { $this->abstractFactory = $abstractFactory; $this->widgets = $widgets; @@ -100,7 +100,7 @@ class ControllerResolver private function createReportController($module, $action, array &$parameters) { - $report = Reports::factory($module, $action); + $report = ReportsProvider::factory($module, $action); if (!$report) { return null; diff --git a/core/Measurable/Measurable.php b/core/Measurable/Measurable.php index d80c1f0323..6223a6931f 100644 --- a/core/Measurable/Measurable.php +++ b/core/Measurable/Measurable.php @@ -9,7 +9,6 @@ namespace Piwik\Measurable; -use Exception; use Piwik\Site; /** @@ -17,16 +16,4 @@ use Piwik\Site; */ class Measurable extends Site { - - public function getSettingValue($name) - { - $settings = new MeasurableSettings($this->id, $this->getType()); - $setting = $settings->getSetting($name); - - if (!empty($setting)) { - return $setting->getValue(); // Calling `getValue` makes sure we respect read permission of this setting - } - - throw new Exception(sprintf('Setting %s does not exist', $name)); - } } diff --git a/core/Measurable/MeasurableSetting.php b/core/Measurable/MeasurableSetting.php deleted file mode 100644 index 91e0970442..0000000000 --- a/core/Measurable/MeasurableSetting.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -namespace Piwik\Measurable; - -use Piwik\Piwik; - -/** - * Describes a Type setting for a website, mobile app, ... - * - * See {@link \Piwik\Plugin\Settings}. - */ -class MeasurableSetting extends \Piwik\Settings\Setting -{ - /** - * By default the value of the type setting is only readable by users having at least view access to one site - * - * @var bool - * @since 2.14.0 - */ - public $readableByCurrentUser = false; - - /** - * By default the value of the type setting is only writable by users having at least admin access to one site - * @var bool - * @internal - */ - public $writableByCurrentUser = false; - - /** - * Constructor. - * - * @param string $name The persisted name of the setting. - * @param string $title The display name of the setting. - */ - public function __construct($name, $title) - { - parent::__construct($name, $title); - - $this->writableByCurrentUser = Piwik::isUserHasSomeAdminAccess(); - $this->readableByCurrentUser = Piwik::isUserHasSomeViewAccess(); - } - - /** - * Returns `true` if this setting is writable for the current user, `false` if otherwise. In case it returns - * writable for the current user it will be visible in the Plugin settings UI. - * - * @return bool - */ - public function isWritableByCurrentUser() - { - return $this->writableByCurrentUser; - } - - /** - * Returns `true` if this setting can be displayed for the current user, `false` if otherwise. - * - * @return bool - */ - public function isReadableByCurrentUser() - { - return $this->readableByCurrentUser; - } -} diff --git a/core/Measurable/MeasurableSettings.php b/core/Measurable/MeasurableSettings.php deleted file mode 100644 index 7d627e0a2c..0000000000 --- a/core/Measurable/MeasurableSettings.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Measurable; - -use Piwik\Db; -use Piwik\Piwik; -use Piwik\Plugin\Settings; -use Piwik\Measurable\Settings\Storage; -use Piwik\Settings\Setting; -use Piwik\Measurable\Type\TypeManager; - -class MeasurableSettings extends Settings -{ - - /** - * @var int - */ - private $idSite = null; - - /** - * @var string - */ - private $idType = null; - - /** - * @param int $idSite The id of a site. If you want to get settings for a not yet created site just pass an empty value ("0") - * @param string $idType If no typeId is given, the type of the site will be used. - * - * @throws \Exception - */ - public function __construct($idSite, $idType) - { - $this->idSite = $idSite; - $this->idType = $idType; - $this->storage = new Storage(Db::get(), $this->idSite); - $this->pluginName = 'MeasurableSettings'; - - $this->init(); - } - - protected function init() - { - $typeManager = new TypeManager(); - $type = $typeManager->getType($this->idType); - $type->configureMeasurableSettings($this); - - /** - * This event is posted when generating settings for a Measurable (website). You can add any Measurable settings - * that you wish to be shown in the Measurable manager (websites manager). If you need to add settings only for - * eg MobileApp measurables you can use eg `$type->getId() === Piwik\Plugins\MobileAppMeasurable\Type::ID` and - * add only settings if the condition is true. - * - * @since Piwik 2.14.0 - * @deprecated will be removed in Piwik 3.0.0 - * - * @param MeasurableSettings $this - * @param \Piwik\Measurable\Type $type - * @param int $idSite - */ - Piwik::postEvent('Measurable.initMeasurableSettings', array($this, $type, $this->idSite)); - } - - public function addSetting(Setting $setting) - { - if ($this->idSite && $setting instanceof MeasurableSetting) { - $setting->writableByCurrentUser = Piwik::isUserHasAdminAccess($this->idSite); - } - - parent::addSetting($setting); - } - - public function save() - { - Piwik::checkUserHasAdminAccess($this->idSite); - - $typeManager = new TypeManager(); - $type = $typeManager->getType($this->idType); - - /** - * Triggered just before Measurable settings are about to be saved. You can use this event for example - * to validate not only one setting but multiple ssetting. For example whether username - * and password matches. - * - * @since Piwik 2.14.0 - * @deprecated will be removed in Piwik 3.0.0 - * - * @param MeasurableSettings $this - * @param \Piwik\Measurable\Type $type - * @param int $idSite - */ - Piwik::postEvent('Measurable.beforeSaveSettings', array($this, $type, $this->idSite)); - - $this->storage->save(); - } - -} - diff --git a/core/Measurable/Settings/Storage.php b/core/Measurable/Settings/Storage.php deleted file mode 100644 index df9748af5e..0000000000 --- a/core/Measurable/Settings/Storage.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -namespace Piwik\Measurable\Settings; - -use Piwik\Db; -use Piwik\Common; -use Piwik\Settings\Setting; - -/** - * Storage for site settings - */ -class Storage extends \Piwik\Settings\Storage -{ - private $idSite = null; - - /** - * @var Db - */ - private $db = null; - - private $toBeDeleted = array(); - - public function __construct(Db\AdapterInterface $db, $idSite) - { - $this->db = $db; - $this->idSite = $idSite; - } - - protected function deleteSettingsFromStorage() - { - $table = $this->getTableName(); - $sql = "DELETE FROM $table WHERE `idsite` = ?"; - $bind = array($this->idSite); - - $this->db->query($sql, $bind); - } - - public function deleteValue(Setting $setting) - { - $this->toBeDeleted[$setting->getName()] = true; - parent::deleteValue($setting); - } - - public function setValue(Setting $setting, $value) - { - $this->toBeDeleted[$setting->getName()] = false; // prevent from deleting this setting, we will create/update it - parent::setValue($setting, $value); - } - - /** - * Saves (persists) the current setting values in the database. - */ - public function save() - { - $table = $this->getTableName(); - - foreach ($this->toBeDeleted as $name => $delete) { - if ($delete) { - $sql = "DELETE FROM $table WHERE `idsite` = ? and `setting_name` = ?"; - $bind = array($this->idSite, $name); - - $this->db->query($sql, $bind); - } - } - - $this->toBeDeleted = array(); - - foreach ($this->settingsValues as $name => $value) { - $value = serialize($value); - - $sql = "INSERT INTO $table (`idsite`, `setting_name`, `setting_value`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `setting_value` = ?"; - $bind = array($this->idSite, $name, $value, $value); - - $this->db->query($sql, $bind); - } - } - - protected function loadSettings() - { - $sql = "SELECT `setting_name`, `setting_value` FROM " . $this->getTableName() . " WHERE idsite = ?"; - $bind = array($this->idSite); - - $settings =$this->db->fetchAll($sql, $bind); - - $flat = array(); - foreach ($settings as $setting) { - $flat[$setting['setting_name']] = unserialize($setting['setting_value']); - } - - return $flat; - } - - private function getTableName() - { - return Common::prefixTable('site_setting'); - } -} diff --git a/core/Measurable/Type.php b/core/Measurable/Type.php index e9457a660f..5d24f04409 100644 --- a/core/Measurable/Type.php +++ b/core/Measurable/Type.php @@ -55,8 +55,5 @@ class Type return $this->howToSetupUrl; } - public function configureMeasurableSettings(MeasurableSettings $settings) - { - } } diff --git a/core/Measurable/Type/TypeManager.php b/core/Measurable/Type/TypeManager.php index af40d9c624..a514abbbdc 100644 --- a/core/Measurable/Type/TypeManager.php +++ b/core/Measurable/Type/TypeManager.php @@ -8,6 +8,7 @@ */ namespace Piwik\Measurable\Type; +use Piwik\Container\StaticContainer; use Piwik\Plugin\Manager as PluginManager; use Piwik\Measurable\Type; @@ -18,7 +19,14 @@ class TypeManager */ public function getAllTypes() { - return PluginManager::getInstance()->findComponents('Type', '\\Piwik\\Measurable\\Type'); + $components = PluginManager::getInstance()->findComponents('Type', '\\Piwik\\Measurable\\Type'); + + $instances = array(); + foreach ($components as $component) { + $instances[] = StaticContainer::get($component); + } + + return $instances; } /** diff --git a/core/Menu/MenuAbstract.php b/core/Menu/MenuAbstract.php index 777d97b523..25e6ce0f53 100644 --- a/core/Menu/MenuAbstract.php +++ b/core/Menu/MenuAbstract.php @@ -8,6 +8,7 @@ */ namespace Piwik\Menu; +use Piwik\Container\StaticContainer; use Piwik\Plugins\SitesManager\API; use Piwik\Singleton; use Piwik\Plugin\Manager as PluginManager; @@ -71,7 +72,12 @@ abstract class MenuAbstract extends Singleton return self::$menus; } - self::$menus = PluginManager::getInstance()->findComponents('Menu', 'Piwik\\Plugin\\Menu'); + $components = PluginManager::getInstance()->findComponents('Menu', 'Piwik\\Plugin\\Menu'); + + self::$menus = array(); + foreach ($components as $component) { + self::$menus[] = StaticContainer::get($component); + } return self::$menus; } diff --git a/core/Notification/Manager.php b/core/Notification/Manager.php index bdf1f130dd..4ae3acde42 100644 --- a/core/Notification/Manager.php +++ b/core/Notification/Manager.php @@ -129,6 +129,10 @@ class Manager private static function removeOldestNotificationsIfThereAreTooMany() { + if (!self::isSessionEnabled()) { + return; + } + $maxNotificationsInSession = 30; $session = static::getSession(); diff --git a/core/Plugin.php b/core/Plugin.php index 852e09522e..91d5a8a8ad 100644 --- a/core/Plugin.php +++ b/core/Plugin.php @@ -324,7 +324,7 @@ class Plugin * given subclass. If the requested file exists but does not extend this class * a warning will be shown to advice a developer to extend this certain class. * - * @return \stdClass|null Null if the requested component does not exist or an instance of the found + * @return string|null Null if the requested component does not exist or an instance of the found * component. */ public function findComponent($componentName, $expectedSubclass) @@ -369,7 +369,7 @@ class Plugin $this->cache->save($cacheId, $classname); } - return StaticContainer::get($classname); + return $classname; } public function findMultipleComponents($directoryWithinPlugin, $expectedSubclass) diff --git a/core/Plugin/API.php b/core/Plugin/API.php index c54e10a82b..0f48816af3 100644 --- a/core/Plugin/API.php +++ b/core/Plugin/API.php @@ -86,6 +86,16 @@ abstract class API } /** + * Used in tests only + * @ignore + * @deprecated + */ + public static function unsetAllInstances() + { + self::$instances = array(); + } + + /** * Sets the singleton instance. For testing purposes. * @ignore * @deprecated diff --git a/core/Plugin/Controller.php b/core/Plugin/Controller.php index 74691e577c..ad4addf85c 100644 --- a/core/Plugin/Controller.php +++ b/core/Plugin/Controller.php @@ -33,7 +33,7 @@ use Piwik\Piwik; use Piwik\Plugins\CoreAdminHome\CustomLogo; use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; use Piwik\Plugins\LanguagesManager\LanguagesManager; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\SettingsPiwik; use Piwik\Site; use Piwik\Url; @@ -314,7 +314,7 @@ abstract class Controller protected function renderReport($apiAction, $controllerAction = false) { if (empty($controllerAction) && is_string($apiAction)) { - $report = Reports::factory($this->pluginName, $apiAction); + $report = ReportsProvider::factory($this->pluginName, $apiAction); if (!empty($report)) { $apiAction = $report; diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php index 2b552f48e0..b490451747 100644 --- a/core/Plugin/Manager.php +++ b/core/Plugin/Manager.php @@ -12,8 +12,11 @@ namespace Piwik\Plugin; use Piwik\Application\Kernel\PluginList; use Piwik\Cache; use Piwik\Columns\Dimension; +use Piwik\Common; use Piwik\Config as PiwikConfig; use Piwik\Config; +use Piwik\Db; +use Piwik\Settings\Storage as SettingsStorage; use Piwik\Container\StaticContainer; use Piwik\EventDispatcher; use Piwik\Filesystem; @@ -398,7 +401,8 @@ class Manager } $this->loadAllPluginsAndGetTheirInfo(); - \Piwik\Settings\Manager::cleanupPluginSettings($pluginName); + SettingsStorage\Backend\PluginSettingsTable::removeAllSettingsForPlugin($pluginName); + SettingsStorage\Backend\MeasurableSettingsTable::removeAllSettingsForPlugin($pluginName); $this->executePluginDeactivate($pluginName); $this->executePluginUninstall($pluginName); diff --git a/core/Plugin/Menu.php b/core/Plugin/Menu.php index 384c3a199a..99275a5aeb 100644 --- a/core/Plugin/Menu.php +++ b/core/Plugin/Menu.php @@ -236,7 +236,7 @@ class Menu } $reportAction = lcfirst(substr($action, 4)); - if (Reports::factory($module, $reportAction)) { + if (ReportsProvider::factory($module, $reportAction)) { return; } diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php index c98d38e849..38e4857498 100644 --- a/core/Plugin/Report.php +++ b/core/Plugin/Report.php @@ -20,7 +20,7 @@ use Piwik\Cache as PiwikCache; use Piwik\Piwik; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\ViewDataTable\Factory as ViewDataTableFactory; use Exception; use Piwik\Widget\WidgetsList; @@ -699,7 +699,7 @@ class Report list($subtableReportModule, $subtableReportAction) = $this->getSubtableApiMethod(); - $subtableReport = Reports::factory($subtableReportModule, $subtableReportAction); + $subtableReport = ReportsProvider::factory($subtableReportModule, $subtableReportAction); if (empty($subtableReport)) { return null; } diff --git a/core/Plugin/Reports.php b/core/Plugin/ReportsProvider.php index 56d69e5453..021ca891fe 100644 --- a/core/Plugin/Reports.php +++ b/core/Plugin/ReportsProvider.php @@ -16,7 +16,7 @@ use Piwik\Cache as PiwikCache; /** * Get reports that are defined by plugins. */ -class Reports +class ReportsProvider { /** diff --git a/core/Plugin/Settings.php b/core/Plugin/Settings.php deleted file mode 100644 index c26581e4b1..0000000000 --- a/core/Plugin/Settings.php +++ /dev/null @@ -1,308 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugin; - -use Piwik\Piwik; -use Piwik\Settings\Setting; -use Piwik\Settings\Storage; -use Piwik\Settings\StorageInterface; -use Piwik\Tracker\SettingsStorage; - -/** - * Base class of all plugin settings providers. Plugins that define their own configuration settings - * can extend this class to easily make their settings available to Piwik users. - * - * Descendants of this class should implement the {@link init()} method and call the - * {@link addSetting()} method for each of the plugin's settings. - * - * For an example, see the {@link Piwik\Plugins\ExampleSettingsPlugin\ExampleSettingsPlugin} plugin. - * - * @api - */ -abstract class Settings -{ - const TYPE_INT = 'integer'; - const TYPE_FLOAT = 'float'; - const TYPE_STRING = 'string'; - const TYPE_BOOL = 'boolean'; - const TYPE_ARRAY = 'array'; - - const CONTROL_RADIO = 'radio'; - const CONTROL_TEXT = 'text'; - const CONTROL_TEXTAREA = 'textarea'; - const CONTROL_CHECKBOX = 'checkbox'; - const CONTROL_PASSWORD = 'password'; - const CONTROL_MULTI_SELECT = 'multiselect'; - const CONTROL_SINGLE_SELECT = 'select'; - - /** - * An array containing all available settings: Array ( [setting-name] => [setting] ) - * - * @var Settings[] - */ - private $settings = array(); - - private $introduction; - protected $pluginName; - - /** - * @var StorageInterface - */ - protected $storage; - - /** - * Constructor. - */ - public function __construct($pluginName = null) - { - if (!empty($pluginName)) { - $this->pluginName = $pluginName; - } else { - $classname = get_class($this); - $parts = explode('\\', $classname); - - if (3 <= count($parts)) { - $this->pluginName = $parts[2]; - } - } - - $this->storage = Storage\Factory::make($this->pluginName); - - $this->init(); - } - - /** - * @ignore - */ - public function getPluginName() - { - return $this->pluginName; - } - - /** - * @ignore - * @return Setting - */ - public function getSetting($name) - { - if (array_key_exists($name, $this->settings)) { - return $this->settings[$name]; - } - } - - /** - * Implemented by descendants. This method should define plugin settings (via the - * {@link addSetting()}) method and set the introduction text (via the - * {@link setIntroduction()}). - */ - abstract protected function init(); - - /** - * Sets the text used to introduce this plugin's settings in the _Plugin Settings_ page. - * - * @param string $introduction - */ - protected function setIntroduction($introduction) - { - $this->introduction = $introduction; - } - - /** - * Returns the introduction text for this plugin's settings. - * - * @return string - */ - public function getIntroduction() - { - return $this->introduction; - } - - /** - * Returns the settings that can be displayed for the current user. - * - * @return Setting[] - */ - public function getSettingsForCurrentUser() - { - $settings = array_filter($this->getSettings(), function (Setting $setting) { - return $setting->isWritableByCurrentUser(); - }); - - $settings2 = $settings; - - uasort($settings, function ($setting1, $setting2) use ($settings2) { - - /** @var Setting $setting1 */ /** @var Setting $setting2 */ - if ($setting1->getOrder() == $setting2->getOrder()) { - // preserve order for settings having same order - foreach ($settings2 as $setting) { - if ($setting1 === $setting) { - return -1; - } - if ($setting2 === $setting) { - return 1; - } - } - - return 0; - } - - return $setting1->getOrder() > $setting2->getOrder() ? -1 : 1; - }); - - return $settings; - } - - /** - * Returns all available settings. This will include settings that are not available - * to the current user (such as settings available only to the Super User). - * - * @return Setting[] - */ - public function getSettings() - { - return $this->settings; - } - - /** - * Makes a new plugin setting available. - * - * @param Setting $setting - * @throws \Exception If there is a setting with the same name that already exists. - * If the name contains non-alphanumeric characters. - */ - protected function addSetting(Setting $setting) - { - $name = $setting->getName(); - - if (!ctype_alnum(str_replace('_', '', $name))) { - $msg = sprintf('The setting name "%s" in plugin "%s" is not valid. Only underscores, alpha and numerical characters are allowed', $setting->getName(), $this->pluginName); - throw new \Exception($msg); - } - - if (array_key_exists($name, $this->settings)) { - throw new \Exception(sprintf('A setting with name "%s" does already exist for plugin "%s"', $setting->getName(), $this->pluginName)); - } - - $this->setDefaultTypeAndFieldIfNeeded($setting); - $this->addValidatorIfNeeded($setting); - - $setting->setStorage($this->storage); - $setting->setPluginName($this->pluginName); - - $this->settings[$name] = $setting; - } - - /** - * Saves (persists) the current setting values in the database. - */ - public function save() - { - $this->storage->save(); - - SettingsStorage::clearCache(); - - /** - * Triggered after a plugin settings have been updated. - * - * **Example** - * - * Piwik::addAction('Settings.MyPlugin.settingsUpdated', function (Settings $settings) { - * $value = $settings->someSetting->getValue(); - * // Do something with the new setting value - * }); - * - * @param Settings $settings The plugin settings object. - */ - Piwik::postEvent(sprintf('Settings.%s.settingsUpdated', $this->pluginName), array($this)); - } - - /** - * Removes all settings for this plugin from the database. Useful when uninstalling - * a plugin. - */ - public function removeAllPluginSettings() - { - Piwik::checkUserHasSuperUserAccess(); - - $this->storage->deleteAllValues(); - - SettingsStorage::clearCache(); - } - - private function getDefaultType($controlType) - { - $defaultTypes = array( - static::CONTROL_TEXT => static::TYPE_STRING, - static::CONTROL_TEXTAREA => static::TYPE_STRING, - static::CONTROL_PASSWORD => static::TYPE_STRING, - static::CONTROL_CHECKBOX => static::TYPE_BOOL, - static::CONTROL_MULTI_SELECT => static::TYPE_ARRAY, - static::CONTROL_RADIO => static::TYPE_STRING, - static::CONTROL_SINGLE_SELECT => static::TYPE_STRING, - ); - - return $defaultTypes[$controlType]; - } - - private function getDefaultCONTROL($type) - { - $defaultControlTypes = array( - static::TYPE_INT => static::CONTROL_TEXT, - static::TYPE_FLOAT => static::CONTROL_TEXT, - static::TYPE_STRING => static::CONTROL_TEXT, - static::TYPE_BOOL => static::CONTROL_CHECKBOX, - static::TYPE_ARRAY => static::CONTROL_MULTI_SELECT, - ); - - return $defaultControlTypes[$type]; - } - - private function setDefaultTypeAndFieldIfNeeded(Setting $setting) - { - $hasControl = !is_null($setting->uiControlType); - $hasType = !is_null($setting->type); - - if ($hasControl && !$hasType) { - $setting->type = $this->getDefaultType($setting->uiControlType); - } elseif ($hasType && !$hasControl) { - $setting->uiControlType = $this->getDefaultCONTROL($setting->type); - } elseif (!$hasControl && !$hasType) { - $setting->type = static::TYPE_STRING; - $setting->uiControlType = static::CONTROL_TEXT; - } - } - - private function addValidatorIfNeeded(Setting $setting) - { - if (!is_null($setting->validate) || is_null($setting->availableValues)) { - return; - } - - $pluginName = $this->pluginName; - - $setting->validate = function ($value) use ($setting, $pluginName) { - - $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed', - array($setting->title, $pluginName)); - - if (is_array($value) && $setting->type == Settings::TYPE_ARRAY) { - foreach ($value as $val) { - if (!array_key_exists($val, $setting->availableValues)) { - throw new \Exception($errorMsg); - } - } - } else { - if (!array_key_exists($value, $setting->availableValues)) { - throw new \Exception($errorMsg); - } - } - }; - } -} diff --git a/core/Plugin/SettingsProvider.php b/core/Plugin/SettingsProvider.php new file mode 100644 index 0000000000..5cc05bd32c --- /dev/null +++ b/core/Plugin/SettingsProvider.php @@ -0,0 +1,215 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugin; + +use Piwik\CacheId; +use Piwik\Container\StaticContainer; +use Piwik\Plugin; +use Piwik\Cache as PiwikCache; +use Piwik\Settings\Measurable\MeasurableSettings; +use \Piwik\Settings\Plugin\UserSettings; +use \Piwik\Settings\Plugin\SystemSettings; + +/** + * Base class of all plugin settings providers. Plugins that define their own configuration settings + * can extend this class to easily make their settings available to Piwik users. + * + * Descendants of this class should implement the {@link init()} method and call the + * {@link addSetting()} method for each of the plugin's settings. + * + * For an example, see the {@link Piwik\Plugins\ExampleSettingsPlugin\ExampleSettingsPlugin} plugin. + */ +class SettingsProvider +{ + /** + * @var Plugin\Manager + */ + private $pluginManager; + + public function __construct(Plugin\Manager $pluginManager) + { + $this->pluginManager = $pluginManager; + } + + /** + * + * Get user settings implemented by a specific plugin (if implemented by this plugin). + * @param string $pluginName + * @return SystemSettings|null + */ + public function getSystemSettings($pluginName) + { + $plugin = $this->getLoadedAndActivated($pluginName); + + if ($plugin) { + $settings = $plugin->findComponent('SystemSettings', 'Piwik\\Settings\\Plugin\\SystemSettings'); + + if ($settings) { + return StaticContainer::get($settings); + } + } + } + + /** + * Get user settings implemented by a specific plugin (if implemented by this plugin). + * @param string $pluginName + * @return UserSettings|null + */ + public function getUserSettings($pluginName) + { + $plugin = $this->getLoadedAndActivated($pluginName); + + if ($plugin) { + $settings = $plugin->findComponent('UserSettings', 'Piwik\\Settings\\Plugin\\UserSettings'); + + if ($settings) { + return StaticContainer::get($settings); + } + } + } + + /** + * Returns all available system settings. A plugin has to specify a file named `SystemSettings.php` containing a + * class named `SystemSettings` that extends `Piwik\Settings\Plugin\SystemSettings` in order to be considered as + * a system setting. Otherwise the settings for a plugin won't be available. + * + * @return SystemSettings[] An array containing array([pluginName] => [setting instance]). + */ + public function getAllSystemSettings() + { + $cacheId = CacheId::languageAware('AllSystemSettings'); + $cache = PiwikCache::getTransientCache(); + + if (!$cache->contains($cacheId)) { + $pluginNames = $this->pluginManager->getActivatedPlugins(); + $byPluginName = array(); + + foreach ($pluginNames as $plugin) { + $component = $this->getSystemSettings($plugin); + + if (!empty($component)) { + $byPluginName[$plugin] = $component; + } + } + + $cache->save($cacheId, $byPluginName); + } + + return $cache->fetch($cacheId); + } + + /** + * Returns all available user settings. A plugin has to specify a file named `UserSettings.php` containing a class + * named `UserSettings` that extends `Piwik\Settings\Plugin\UserSettings` in order to be considered as a plugin + * setting. Otherwise the settings for a plugin won't be available. + * + * @return UserSettings[] An array containing array([pluginName] => [setting instance]). + */ + public function getAllUserSettings() + { + $cacheId = CacheId::languageAware('AllUserSettings'); + $cache = PiwikCache::getTransientCache(); + + if (!$cache->contains($cacheId)) { + $pluginNames = $this->pluginManager->getActivatedPlugins(); + $byPluginName = array(); + + foreach ($pluginNames as $plugin) { + $component = $this->getUserSettings($plugin); + + if (!empty($component)) { + $byPluginName[$plugin] = $component; + } + } + + $cache->save($cacheId, $byPluginName); + } + + return $cache->fetch($cacheId); + } + + /** + * @api + * + * Get measurable settings for a specific plugin. + * + * @param string $pluginName The name of a plugin. + * @param int $idSite The ID of a site. If a site is about to be created pass idSite = 0. + * @param string|null $idType If null, idType will be detected automatically if the site already exists. Only + * needed to set a value when idSite = 0 (this is the case when a site is about) + * to be created. + * + * @return MeasurableSettings|null Returns null if no MeasurableSettings implemented by this plugin or when plugin + * is not loaded and activated. Returns an instance of the settings otherwise. + */ + public function getMeasurableSettings($pluginName, $idSite, $idType = null) + { + $plugin = $this->getLoadedAndActivated($pluginName); + + if ($plugin) { + $component = $plugin->findComponent('MeasurableSettings', 'Piwik\\Settings\\Measurable\\MeasurableSettings'); + + if ($component) { + return StaticContainer::getContainer()->make($component, array( + 'idSite' => $idSite, + 'idMeasurableType' => $idType + )); + } + } + } + + /** + * @api + * + * Get all available measurable settings implemented by loaded and activated plugins. + * + * @param int $idSite The ID of a site. If a site is about to be created pass idSite = 0. + * @param string|null $idMeasurableType If null, idType will be detected automatically if the site already exists. + * Only needed to set a value when idSite = 0 (this is the case when a site + * is about) to be created. + * + * @return MeasurableSettings[] + */ + public function getAllMeasurableSettings($idSite, $idMeasurableType = null) + { + $pluginNames = $this->pluginManager->getActivatedPlugins(); + $byPluginName = array(); + + foreach ($pluginNames as $plugin) { + $component = $this->getMeasurableSettings($plugin, $idSite, $idMeasurableType); + + if (!empty($component)) { + $byPluginName[$plugin] = $component; + } + } + + return $byPluginName; + } + + private function getLoadedAndActivated($pluginName) + { + if (!$this->pluginManager->isPluginLoaded($pluginName)) { + return; + } + + try { + if (!$this->pluginManager->isPluginActivated($pluginName)) { + return; + } + + $plugin = $this->pluginManager->getLoadedPlugin($pluginName); + } catch (\Exception $e) { + // we are not allowed to use possible settings from this plugin, plugin is not active + return; + } + + return $plugin; + } + +} diff --git a/core/Plugin/ViewDataTable.php b/core/Plugin/ViewDataTable.php index 5ca7e2e9ba..3b7bcf4ff8 100644 --- a/core/Plugin/ViewDataTable.php +++ b/core/Plugin/ViewDataTable.php @@ -13,7 +13,7 @@ use Piwik\Common; use Piwik\DataTable; use Piwik\Period; use Piwik\Piwik; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\View; use Piwik\View\ViewInterface; use Piwik\ViewDataTable\Config as VizConfig; @@ -192,7 +192,7 @@ abstract class ViewDataTable implements ViewInterface $this->requestConfig->apiMethodToRequestDataTable = $apiMethodToRequestDataTable; - $report = Reports::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest()); + $report = ReportsProvider::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest()); if (!empty($report)) { /** @var Report $report */ diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php index da2c60c18e..e19da81d9a 100644 --- a/core/Plugin/Visualization.php +++ b/core/Plugin/Visualization.php @@ -23,7 +23,7 @@ use Piwik\Period; use Piwik\Piwik; use Piwik\Plugins\API\API as ApiApi; use Piwik\Plugins\PrivacyManager\PrivacyManager; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\View; use Piwik\ViewDataTable\Manager as ViewDataTableManager; use Piwik\Plugin\Manager as PluginManager; @@ -169,7 +169,7 @@ class Visualization extends ViewDataTable parent::__construct($controllerAction, $apiMethodToRequestDataTable, $params); - $this->report = Reports::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest()); + $this->report = ReportsProvider::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest()); } public function render() diff --git a/core/Plugin/Widgets.php b/core/Plugin/WidgetsProvider.php index e67f2bce61..8fe015411b 100644 --- a/core/Plugin/Widgets.php +++ b/core/Plugin/WidgetsProvider.php @@ -19,7 +19,7 @@ use Piwik\Widget\WidgetContainerConfig; /** * Get widgets that are defined by plugins. */ -class Widgets +class WidgetsProvider { /** * @var Plugin\Manager diff --git a/core/Scheduler/TaskLoader.php b/core/Scheduler/TaskLoader.php index 60b9e328b6..95c3394cfb 100644 --- a/core/Scheduler/TaskLoader.php +++ b/core/Scheduler/TaskLoader.php @@ -8,6 +8,7 @@ namespace Piwik\Scheduler; +use Piwik\Container\StaticContainer; use Piwik\Plugin\Manager as PluginManager; use Piwik\Plugin\Tasks; @@ -27,6 +28,7 @@ class TaskLoader $pluginTasks = PluginManager::getInstance()->findComponents('Tasks', 'Piwik\Plugin\Tasks'); foreach ($pluginTasks as $pluginTask) { + $pluginTask = StaticContainer::get($pluginTask); $pluginTask->schedule(); foreach ($pluginTask->getScheduledTasks() as $task) { diff --git a/core/Settings/FieldConfig.php b/core/Settings/FieldConfig.php new file mode 100644 index 0000000000..4c75fc1719 --- /dev/null +++ b/core/Settings/FieldConfig.php @@ -0,0 +1,208 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings; + +/** + * Lets you configure a form field. + * + * @api + */ +class FieldConfig +{ + /** + * Shows a radio field + */ + const UI_CONTROL_RADIO = 'radio'; + + /** + * Shows a text field + */ + const UI_CONTROL_TEXT = 'text'; + + /** + * Shows a text area + */ + const UI_CONTROL_TEXTAREA = 'textarea'; + + /** + * Shows a checkbox + */ + const UI_CONTROL_CHECKBOX = 'checkbox'; + + /** + * Shows a password field + */ + const UI_CONTROL_PASSWORD = 'password'; + + /** + * Shows a select field where a user can select multiple values. The type "Array" is required for this ui control. + */ + const UI_CONTROL_MULTI_SELECT = 'multiselect'; + + /** + * Shows a select field + */ + const UI_CONTROL_SINGLE_SELECT = 'select'; + + /** + * Generates a hidden form field + */ + const UI_CONTROL_HIDDEN = 'hidden'; + + /** + * Expects an integer value + */ + const TYPE_INT = 'integer'; + + /** + * Expects a float value + */ + const TYPE_FLOAT = 'float'; + + /** + * Expects a string + */ + const TYPE_STRING = 'string'; + + /** + * Expects a boolean + */ + const TYPE_BOOL = 'boolean'; + + /** + * Expects an array containing multiple values + */ + const TYPE_ARRAY = 'array'; + + /** + * Describes what HTML element should be used to manipulate the setting through Piwik's UI. + * + * See {@link Piwik\Plugin\Settings} for a list of supported control types. + * + * @var string + */ + public $uiControl = null; + + /** + * Name-value mapping of HTML attributes that will be added HTML form control, eg, + * `array('size' => 3)`. Attributes will be escaped before outputting. + * + * @var array + */ + public $uiControlAttributes = array(); + + /** + * The list of all available values for this setting. If null, the setting can have any value. + * + * If supplied, this field should be an array mapping available values with their prettified + * display value. Eg, if set to `array('nb_visits' => 'Visits', 'nb_actions' => 'Actions')`, + * the UI will display **Visits** and **Actions**, and when the user selects one, Piwik will + * set the setting to **nb_visits** or **nb_actions** respectively. + * + * The setting value will be validated if this field is set. If the value is not one of the + * available values, an error will be triggered. + * + * _Note: If a custom validator is supplied (see {@link $validate}), the setting value will + * not be validated._ + * + * @var null|array + */ + public $availableValues = null; + + /** + * Text that will appear above this setting's section in the _Plugin Settings_ admin page. + * + * @var null|string + */ + public $introduction = null; + + /** + * Text that will appear directly underneath the setting title in the _Plugin Settings_ admin + * page. If set, should be a short description of the setting. + * + * @var null|string + */ + public $description = null; + + /** + * Text that will appear next to the setting's section in the _Plugin Settings_ admin page. If set, + * it should contain information about the setting that is more specific than a general description, + * such as the format of the setting value if it has a special format. + * + * Be sure to escape any user input as HTML can be used here. + * + * @var null|string + */ + public $inlineHelp = null; + + /** + * A closure that does some custom validation on the setting before the setting is persisted. + * + * The closure should take two arguments: the setting value and the {@link Setting} instance being + * validated. If the value is found to be invalid, the closure should throw an exception with + * a message that describes the error. + * + * **Example** + * + * $setting->validate = function ($value, Setting $setting) { + * if ($value > 60) { + * throw new \Exception('The time limit is not allowed to be greater than 60 minutes.'); + * } + * } + * + * @var null|\Closure + */ + public $validate = null; + + /** + * A closure that transforms the setting value. If supplied, this closure will be executed after + * the setting has been validated. + * + * _Note: If a transform is supplied, the setting's {@link $type} has no effect. This means the + * transformation function will be responsible for casting the setting value to the appropriate + * data type._ + * + * **Example** + * + * $setting->transform = function ($value, Setting $setting) { + * if ($value > 30) { + * $value = 30; + * } + * + * return (int) $value; + * } + * + * @var null|\Closure + */ + public $transform = null; + + /** + * This setting's display name, for example, `'Refresh Interval'`. + * + * Be sure to escape any user input as HTML can be used here. + * + * @var string + */ + public $title = ''; + + /** + * Here you can define conditions so that certain form fields will be only shown when a certain condition + * is true. This condition is supposed to be evaluated on the client side dynamically. This way you can hide + * for example some fields depending on another field. For example if SiteSearch is disabled, fields to enter + * site search keywords is not needed anymore and can be disabled. + * + * For example 'sitesearch', or 'sitesearch && !use_sitesearch_default' where 'sitesearch' and 'use_sitesearch_default' + * are both values of fields. + * + * @var string + */ + public $condition; + +} diff --git a/core/Settings/Manager.php b/core/Settings/Manager.php deleted file mode 100644 index e757e44d60..0000000000 --- a/core/Settings/Manager.php +++ /dev/null @@ -1,157 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -namespace Piwik\Settings; - -use Piwik\Plugin\Manager as PluginManager; - -/** - * Settings manager. - * - */ -class Manager -{ - private static $settings = array(); - private static $numPluginsChecked = 0; - - /** - * Returns all available plugin settings, even settings for inactive plugins. A plugin has to specify a file named - * `Settings.php` containing a class named `Settings` that extends `Piwik\Plugin\Settings` in order to be - * considered as a plugin setting. Otherwise the settings for a plugin won't be available. - * - * @return \Piwik\Plugin\Settings[] An array containing array([pluginName] => [setting instance]). - */ - public static function getAllPluginSettings() - { - $numActivatedPlugins = PluginManager::getInstance()->getNumberOfActivatedPlugins(); - - if (static::$numPluginsChecked != $numActivatedPlugins) { - static::$numPluginsChecked = $numActivatedPlugins; - static::$settings = array(); - } - - if (empty(static::$settings)) { - $settings = PluginManager::getInstance()->findComponents('Settings', 'Piwik\\Plugin\\Settings'); - $byPluginName = array(); - - foreach ($settings as $setting) { - $byPluginName[$setting->getPluginName()] = $setting; - } - - static::$settings = $byPluginName; - } - - return static::$settings; - } - - private static function isActivatedPlugin($pluginName) - { - return PluginManager::getInstance()->isPluginActivated($pluginName); - } - - /** - * Removes all settings made for a specific plugin. Useful while uninstalling a plugin. - * - * @param string $pluginName - */ - public static function cleanupPluginSettings($pluginName) - { - $pluginManager = PluginManager::getInstance(); - - if (!$pluginManager->isPluginLoaded($pluginName)) { - return; - } - - $plugin = $pluginManager->loadPlugin($pluginName); - $settings = $plugin->findComponent('Settings', 'Piwik\\Plugin\\Settings'); - - if (!empty($settings)) { - $settings->removeAllPluginSettings(); - } - } - - /** - * Gets all plugins settings that have at least one settings a user is allowed to change. Only the settings for - * activated plugins are returned. - * - * @return \Piwik\Plugin\Settings[] An array containing array([pluginName] => [setting instance]). - */ - public static function getPluginSettingsForCurrentUser() - { - $settings = static::getAllPluginSettings(); - - $settingsForUser = array(); - foreach ($settings as $pluginName => $setting) { - if (!static::isActivatedPlugin($pluginName)) { - continue; - } - - $forUser = $setting->getSettingsForCurrentUser(); - if (!empty($forUser)) { - $settingsForUser[$pluginName] = $setting; - } - } - - return $settingsForUser; - } - - public static function hasSystemPluginSettingsForCurrentUser($pluginName) - { - $pluginNames = static::getPluginNamesHavingSystemSettings(); - - return in_array($pluginName, $pluginNames); - } - - /** - * Detects whether there are user settings for activated plugins available that the current user can change. - * - * @return bool - */ - public static function hasUserPluginsSettingsForCurrentUser() - { - $settings = static::getPluginSettingsForCurrentUser(); - - foreach ($settings as $setting) { - foreach ($setting->getSettingsForCurrentUser() as $set) { - if ($set instanceof UserSetting) { - return true; - } - } - } - - return false; - } - - public static function getPluginNamesHavingSystemSettings() - { - $settings = static::getPluginSettingsForCurrentUser(); - $plugins = array(); - - foreach ($settings as $pluginName => $setting) { - foreach ($setting->getSettingsForCurrentUser() as $set) { - if ($set instanceof SystemSetting) { - $plugins[] = $pluginName; - } - } - } - - return array_unique($plugins); - } - /** - * Detects whether there are system settings for activated plugins available that the current user can change. - * - * @return bool - */ - public static function hasSystemPluginsSettingsForCurrentUser() - { - $settings = static::getPluginNamesHavingSystemSettings(); - - return !empty($settings); - } -} diff --git a/core/Settings/Measurable/MeasurableProperty.php b/core/Settings/Measurable/MeasurableProperty.php new file mode 100644 index 0000000000..b49ee04698 --- /dev/null +++ b/core/Settings/Measurable/MeasurableProperty.php @@ -0,0 +1,89 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Measurable; + +use Piwik\Container\StaticContainer; +use Piwik\Piwik; +use Piwik\Settings\Storage; +use Exception; + +/** + * Describes a Measurable property for a measurable type such as a website, a mobile app, .... + * + * The difference to {@link MeasurableSetting} is that these fields will be stored in the actual site table whereas + * MeasurableSetting will be stored in a site_settings table. For this reasons MeasurableProperty can be used only + * for some specific fields that already exist in site table such as "ecommerce", "sitesearch" etc. + * + * See {@link \Piwik\Settings\Setting}. + */ +class MeasurableProperty extends \Piwik\Settings\Setting +{ + /** + * @var int + */ + private $idSite = 0; + + private $allowedNames = array( + 'ecommerce', 'sitesearch', 'sitesearch_keyword_parameters', + 'sitesearch_category_parameters', + 'exclude_unknown_urls', 'excluded_ips', 'excluded_parameters', + 'excluded_user_agents', 'keep_url_fragment', 'urls' + ); + + /** + * Constructor. + * + * @param string $name The persisted name of the setting. + * @param mixed $defaultValue Default value for this setting if no value was specified. + * @param string $type Eg an array, int, ... see TYPE_* constants + * @param string $pluginName The name of the plugin the setting belongs to. + * @param int $idSite The idSite this property belongs to. + * @throws Exception + */ + public function __construct($name, $defaultValue, $type, $pluginName, $idSite) + { + if (!in_array($name, $this->allowedNames)) { + throw new Exception(sprintf('Name "%s" is not allowed to be used with a MeasurableProperty, use a MeasurableSetting instead.', $name)); + } + + parent::__construct($name, $defaultValue, $type, $pluginName); + + $this->idSite = $idSite; + + $storageFactory = StaticContainer::get('Piwik\Settings\Storage\Factory'); + $this->storage = $storageFactory->getSitesTable($idSite); + } + + /** + * Returns `true` if this setting can be displayed for the current user, `false` if otherwise. + * + * @return bool + */ + public function isWritableByCurrentUser() + { + if (isset($this->hasWritePermission)) { + return $this->hasWritePermission; + } + + // performance improvement, do not detect this in __construct otherwise likely rather "big" query to DB. + if ($this->hasSiteBeenCreated()) { + $this->hasWritePermission = Piwik::isUserHasAdminAccess($this->idSite); + } else { + $this->hasWritePermission = Piwik::hasUserSuperUserAccess(); + } + + return $this->hasWritePermission; + } + + private function hasSiteBeenCreated() + { + return !empty($this->idSite); + } +} diff --git a/core/Settings/Measurable/MeasurableSetting.php b/core/Settings/Measurable/MeasurableSetting.php new file mode 100644 index 0000000000..b03289e44e --- /dev/null +++ b/core/Settings/Measurable/MeasurableSetting.php @@ -0,0 +1,72 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Measurable; + +use Piwik\Container\StaticContainer; +use Piwik\Piwik; +use Piwik\Settings\Storage; + +/** + * Describes a Measurable setting for a measurable type such as a website, a mobile app, ... + * + * See {@link \Piwik\Settings\Setting}. + */ +class MeasurableSetting extends \Piwik\Settings\Setting +{ + /** + * @var int + */ + private $idSite = 0; + + /** + * Constructor. + * + * @param string $name The persisted name of the setting. + * @param mixed $defaultValue Default value for this setting if no value was specified. + * @param string $type Eg an array, int, ... see TYPE_* constants + * @param string $pluginName The name of the plugin the setting belongs to + * @param int $idSite The idSite this setting belongs to. + */ + public function __construct($name, $defaultValue, $type, $pluginName, $idSite) + { + parent::__construct($name, $defaultValue, $type, $pluginName); + + $this->idSite = $idSite; + + $storageFactory = StaticContainer::get('Piwik\Settings\Storage\Factory'); + $this->storage = $storageFactory->getMeasurableSettingsStorage($idSite, $this->pluginName); + } + + /** + * Returns `true` if this setting can be displayed for the current user, `false` if otherwise. + * + * @return bool + */ + public function isWritableByCurrentUser() + { + if (isset($this->hasWritePermission)) { + return $this->hasWritePermission; + } + + // performance improvement, do not detect this in __construct otherwise likely rather "big" query to DB. + if ($this->hasSiteBeenCreated()) { + $this->hasWritePermission = Piwik::isUserHasAdminAccess($this->idSite); + } else { + $this->hasWritePermission = Piwik::hasUserSuperUserAccess(); + } + + return $this->hasWritePermission; + } + + private function hasSiteBeenCreated() + { + return !empty($this->idSite); + } +} diff --git a/core/Settings/Measurable/MeasurableSettings.php b/core/Settings/Measurable/MeasurableSettings.php new file mode 100644 index 0000000000..3d062f9943 --- /dev/null +++ b/core/Settings/Measurable/MeasurableSettings.php @@ -0,0 +1,141 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Settings\Measurable; + +use Piwik\Db; +use Piwik\Piwik; +use Piwik\Settings\Settings; +use Piwik\Settings\Storage; +use Piwik\Site; +use Exception; + +/** + * Base class of all measurable settings providers. Plugins that define their own configuration settings + * can extend this class to easily make their measurable settings available to Piwik users. + * + * Descendants of this class should implement the {@link init()} method and call the + * {@link makeSetting()} method for each of the measurable's settings. + * + * For an example, see the {@link Piwik\Plugins\ExampleSettingsPlugin\MeasurableSettings} plugin. + * + * $settingsProvider = new Piwik\Plugin\SettingsProvider(); // get this instance via dependency injection + * $measurableSettings = $settingProvider->getMeasurableSettings($yourPluginName, $idsite, $idType = null); + * $measurableSettings->yourSetting->getValue(); + * + * @api + */ +abstract class MeasurableSettings extends Settings +{ + /** + * @var int + */ + protected $idSite; + + /** + * @var string + */ + protected $idMeasurableType; + + /** + * Constructor. + * @param int $idSite If creating settings for a new site that is not created yet, use idSite = 0 + * @param string|null $idMeasurableType If null, idType will be detected from idSite + * @throws Exception + */ + public function __construct($idSite, $idMeasurableType = null) + { + parent::__construct(); + + $this->idSite = (int) $idSite; + + if (!empty($idMeasurableType)) { + $this->idMeasurableType = $idMeasurableType; + } elseif (!empty($idSite)) { + $this->idMeasurableType = Site::getTypeFor($idSite); + } else { + throw new Exception('No idType specified for ' . get_class($this)); + } + + $this->init(); + } + + protected function hasMeasurableType($typeId) + { + return $typeId === $this->idMeasurableType; + } + + /** + * Creates a new measurable setting. + * + * Settings will be displayed in the UI depending on the order of `makeSetting` calls. This means you can define + * the order of the displayed settings by calling makeSetting first for more important settings. + * + * @param string $name The name of the setting that shall be created + * @param mixed $defaultValue The default value for this setting. Note the value will not be converted to the + * specified type. + * @param string $type The PHP internal type the value of this setting should have. + * Use one of FieldConfig::TYPE_* constancts + * @param \Closure $fieldConfigCallback A callback method to configure the field that shall be displayed in the + * UI to define the value for this setting + * @return MeasurableSetting Returns an instance of the created measurable setting. + * @throws Exception + */ + protected function makeSetting($name, $defaultValue, $type, $fieldConfigCallback) + { + $setting = new MeasurableSetting($name, $defaultValue, $type, $this->pluginName, $this->idSite); + $setting->setConfigureCallback($fieldConfigCallback); + + $this->addSetting($setting); + + return $setting; + } + + /** + * @internal + * @param $name + * @param $defaultValue + * @param $type + * @param $configureCallback + * @return MeasurableProperty + * @throws Exception + */ + protected function makeProperty($name, $defaultValue, $type, $configureCallback) + { + $setting = new MeasurableProperty($name, $defaultValue, $type, $this->pluginName, $this->idSite); + $setting->setConfigureCallback($configureCallback); + + $this->addSetting($setting); + + return $setting; + } + + /** + * Saves (persists) the current measurable setting values in the database. + * + * Will trigger an event to notify plugins that a value has been changed. + */ + public function save() + { + parent::save(); + + /** + * Triggered after a plugin settings have been updated. + * + * **Example** + * + * Piwik::addAction('MeasurableSettings.updated', function (MeasurableSettings $settings) { + * $value = $settings->someSetting->getValue(); + * // Do something with the new setting value + * }); + * + * @param Settings $settings The plugin settings object. + */ + Piwik::postEvent('MeasurableSettings.updated', array($this, $this->idSite)); + } +} diff --git a/core/Settings/SystemSetting.php b/core/Settings/Plugin/SystemSetting.php index 68e780a3e8..3c10a4c5f4 100644 --- a/core/Settings/SystemSetting.php +++ b/core/Settings/Plugin/SystemSetting.php @@ -7,48 +7,38 @@ * */ -namespace Piwik\Settings; +namespace Piwik\Settings\Plugin; use Piwik\Config; +use Piwik\Container\StaticContainer; use Piwik\Piwik; +use Piwik\Settings\Setting; +use Piwik\Settings\Storage; /** - * Describes a system wide setting. Only the Super User can change this type of setting and + * Describes a system wide setting. Only the Super User can change this type of setting by d efault and * the value of this setting will affect all users. * - * See {@link \Piwik\Plugin\Settings}. - * + * See {@link \Piwik\Settings\Setting}. * * @api */ class SystemSetting extends Setting { /** - * By default the value of the system setting is only readable by SuperUsers but someone the value should be - * readable by everyone. - * - * @var bool - * @since 2.4.0 - */ - public $readableByCurrentUser = false; - - /** - * @var bool - */ - private $writableByCurrentUser = false; - - /** * Constructor. * - * @param string $name The persisted name of the setting. - * @param string $title The display name of the setting. + * @param string $name The setting's persisted name. + * @param mixed $defaultValue Default value for this setting if no value was specified. + * @param string $type Eg an array, int, ... see TYPE_* constants + * @param string $pluginName The name of the plugin the system setting belongs to. */ - public function __construct($name, $title) + public function __construct($name, $defaultValue, $type, $pluginName) { - parent::__construct($name, $title); + parent::__construct($name, $defaultValue, $type, $pluginName); - $this->writableByCurrentUser = Piwik::hasUserSuperUserAccess(); - $this->readableByCurrentUser = $this->writableByCurrentUser; + $factory = StaticContainer::get('Piwik\Settings\Storage\Factory'); + $this->storage = $factory->getPluginStorage($this->pluginName, $userLogin = ''); } /** @@ -63,39 +53,19 @@ class SystemSetting extends Setting return false; } - return $this->writableByCurrentUser; - } + if (isset($this->hasWritePermission)) { + return $this->hasWritePermission; + } - /** - * Set whether setting is writable or not. For example to hide setting from the UI set it to false. - * - * @param bool $isWritable - */ - public function setIsWritableByCurrentUser($isWritable) - { - $this->writableByCurrentUser = (bool) $isWritable; - } + // performance improvement, do not detect this in __construct otherwise likely rather "big" query to DB. + $this->hasWritePermission = Piwik::hasUserSuperUserAccess(); - /** - * Returns `true` if this setting can be displayed for the current user, `false` if otherwise. - * - * @return bool - */ - public function isReadableByCurrentUser() - { - return $this->readableByCurrentUser; + return $this->hasWritePermission; } /** - * Returns the display order. System settings are displayed before user settings. - * - * @return int + * @inheritdoc */ - public function getOrder() - { - return 30; - } - public function getValue() { $defaultValue = parent::getValue(); // we access value first to make sure permissions are checked diff --git a/core/Settings/Plugin/SystemSettings.php b/core/Settings/Plugin/SystemSettings.php new file mode 100644 index 0000000000..1b2fc413ff --- /dev/null +++ b/core/Settings/Plugin/SystemSettings.php @@ -0,0 +1,90 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Settings\Plugin; + +use Piwik\Db; +use Piwik\Piwik; +use Piwik\Settings\Settings; +use Piwik\Settings\Storage; + +/** + * Base class of all system settings providers. Plugins that define their own configuration settings + * can extend this class to easily make their system settings available to Piwik users. + * + * Descendants of this class should implement the {@link init()} method and call the + * {@link makeSetting()} method to create a system setting for this plugin. + * + * For an example, see {@link Piwik\Plugins\ExampleSettingsPlugin\SystemSettings}. + * + * $systemSettings = new Piwik\Plugins\ExampleSettingsPlugin\SystemSettings(); // get instance via dependency injection + * $systemSettings->yourSetting->getValue(); + * + * @api + */ +abstract class SystemSettings extends Settings +{ + /** + * Constructor. + */ + public function __construct() + { + parent::__construct(); + + $this->init(); + } + + /** + * Creates a new system setting. + * + * Settings will be displayed in the UI depending on the order of `makeSetting` calls. This means you can define + * the order of the displayed settings by calling makeSetting first for more important settings. + * + * @param string $name The name of the setting that shall be created + * @param mixed $defaultValue The default value for this setting. Note the value will not be converted to the + * specified type. + * @param string $type The PHP internal type the value of this setting should have. + * Use one of FieldConfig::TYPE_* constancts + * @param \Closure $fieldConfigCallback A callback method to configure the field that shall be displayed in the + * UI to define the value for this setting + * @return SystemSetting Returns an instance of the created measurable setting. + */ + protected function makeSetting($name, $defaultValue, $type, $fieldConfigCallback) + { + $setting = new SystemSetting($name, $defaultValue, $type, $this->pluginName); + $setting->setConfigureCallback($fieldConfigCallback); + $this->addSetting($setting); + return $setting; + } + + /** + * Saves (persists) the current setting values in the database. + * + * Will trigger an event to notify plugins that a value has been changed. + */ + public function save() + { + parent::save(); + + /** + * Triggered after system settings have been updated. + * + * **Example** + * + * Piwik::addAction('SystemSettings.updated', function (SystemSettings $settings) { + * if ($settings->getPluginName() === 'PluginName') { + * $value = $settings->someSetting->getValue(); + * // Do something with the new setting value + * } + * }); + * + * @param Settings $settings The plugin settings object. + */ + Piwik::postEvent('SystemSettings.updated', array($this)); + } +} diff --git a/core/Settings/Plugin/UserSetting.php b/core/Settings/Plugin/UserSetting.php new file mode 100644 index 0000000000..0044eef78f --- /dev/null +++ b/core/Settings/Plugin/UserSetting.php @@ -0,0 +1,73 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Plugin; + +use Piwik\Container\StaticContainer; +use Piwik\Db; +use Piwik\Piwik; +use Exception; +use Piwik\Settings\Setting; +use Piwik\Settings\Storage; + +/** + * Describes a per user setting. Each user will be able to change this setting for themselves, + * but not for other users. + * + * See {@link \Piwik\Settings\Setting}. + */ +class UserSetting extends Setting +{ + /** + * @var null|string + */ + private $userLogin = null; + + /** + * Constructor. + * + * @param string $name The setting's persisted name. + * @param mixed $defaultValue Default value for this setting if no value was specified. + * @param string $type Eg an array, int, ... see TYPE_* constants + * @param string $pluginName The name of the plugin the setting belongs to + * @param string $userLogin The name of the user the value should be set or get for + * @throws Exception + */ + public function __construct($name, $defaultValue, $type, $pluginName, $userLogin) + { + parent::__construct($name, $defaultValue, $type, $pluginName); + + if (empty($userLogin)) { + throw new Exception('No userLogin given to create setting ' . $name); + } + + $this->userLogin = $userLogin; + + $factory = StaticContainer::get('Piwik\Settings\Storage\Factory'); + $this->storage = $factory->getPluginStorage($this->pluginName, $this->userLogin); + } + + /** + * Returns `true` if this setting can be displayed for the current user, `false` if otherwise. + * + * @return bool + */ + public function isWritableByCurrentUser() + { + if (isset($this->hasWritePermission)) { + return $this->hasWritePermission; + } + + // performance improvement, do not detect this in __construct otherwise likely rather "big" query to DB. + $this->hasWritePermission = Piwik::isUserHasSomeViewAccess(); + + return $this->hasWritePermission; + } + +} diff --git a/core/Settings/Plugin/UserSettings.php b/core/Settings/Plugin/UserSettings.php new file mode 100644 index 0000000000..cda8438d73 --- /dev/null +++ b/core/Settings/Plugin/UserSettings.php @@ -0,0 +1,93 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Settings\Plugin; + +use Piwik\Db; +use Piwik\Piwik; +use Piwik\Settings\Settings; +use Piwik\Settings\Storage; + +/** + * Base class of all plugin settings providers. Plugins that define their own configuration settings + * can extend this class to easily make their settings available to Piwik users. + * + * Descendants of this class should implement the {@link init()} method and call the + * {@link addSetting()} method for each of the plugin's settings. + * + * For an example, see {@link Piwik\Plugins\ExampleSettingsPlugin\UserSettings}. + * + * $userSettings = new Piwik\Plugins\ExampleSettingsPlugin\UserSettings(); // get instance via dependency injection + * $userSettings->yourSetting->getValue(); + * + * @api + */ +abstract class UserSettings extends Settings +{ + /** + * Constructor. + */ + public function __construct() + { + parent::__construct(); + + $this->init(); + } + + /** + * Creates a new user setting. + * + * Settings will be displayed in the UI depending on the order of `makeSetting` calls. This means you can define + * the order of the displayed settings by calling makeSetting first for more important settings. + * + * @param string $name The name of the setting that shall be created + * @param mixed $defaultValue The default value for this setting. Note the value will not be converted to the + * specified type. + * @param string $type The PHP internal type the value of this setting should have. + * Use one of FieldConfig::TYPE_* constancts + * @param \Closure $fieldConfigCallback A callback method to configure the field that shall be displayed in the + * UI to define the value for this setting + * @return UserSetting Returns an instance of the created measurable setting. + */ + protected function makeSetting($name, $defaultValue, $type, $configureCallback) + { + $userLogin = Piwik::getCurrentUserLogin(); + + $setting = new UserSetting($name, $defaultValue, $type, $this->pluginName, $userLogin); + $setting->setConfigureCallback($configureCallback); + + $this->addSetting($setting); + return $setting; + } + + /** + * Saves (persists) the current setting values in the database. + * + * Will trigger an event to notify plugins that a value has been changed. + */ + public function save() + { + parent::save(); + + /** + * Triggered after user settings have been updated. + * + * **Example** + * + * Piwik::addAction('UserSettings.updated', function (UserSettings $settings) { + * if ($settings->getPluginName() === 'PluginName') { + * $value = $settings->someSetting->getValue(); + * // Do something with the new setting value + * } + * }); + * + * @param Settings $settings The plugin settings object. + */ + Piwik::postEvent('UserSettings.updated', array($this)); + } +} diff --git a/core/Settings/Setting.php b/core/Settings/Setting.php index bf8947b196..efd99c3802 100644 --- a/core/Settings/Setting.php +++ b/core/Settings/Setting.php @@ -10,168 +10,84 @@ namespace Piwik\Settings; use Piwik\Piwik; -use Piwik\SettingsServer; +use Piwik\Settings\Storage\Storage; +use Exception; /** * Base setting type class. * * @api */ -abstract class Setting +class Setting { - /** - * Describes the setting's PHP data type. When saved, setting values will always be casted to this - * type. - * - * See {@link Piwik\Plugin\Settings} for a list of supported data types. - * - * @var string - */ - public $type = null; /** - * Describes what HTML element should be used to manipulate the setting through Piwik's UI. - * - * See {@link Piwik\Plugin\Settings} for a list of supported control types. - * + * The name of the setting * @var string */ - public $uiControlType = null; - - /** - * Name-value mapping of HTML attributes that will be added HTML form control, eg, - * `array('size' => 3)`. Attributes will be escaped before outputting. - * - * @var array - */ - public $uiControlAttributes = array(); - - /** - * The list of all available values for this setting. If null, the setting can have any value. - * - * If supplied, this field should be an array mapping available values with their prettified - * display value. Eg, if set to `array('nb_visits' => 'Visits', 'nb_actions' => 'Actions')`, - * the UI will display **Visits** and **Actions**, and when the user selects one, Piwik will - * set the setting to **nb_visits** or **nb_actions** respectively. - * - * The setting value will be validated if this field is set. If the value is not one of the - * available values, an error will be triggered. - * - * _Note: If a custom validator is supplied (see {@link $validate}), the setting value will - * not be validated._ - * - * @var null|array - */ - public $availableValues = null; + protected $name; /** - * Text that will appear above this setting's section in the _Plugin Settings_ admin page. - * - * @var null|string + * Null while not initialized, bool otherwise. + * @var null|bool */ - public $introduction = null; + protected $hasWritePermission = null; /** - * Text that will appear directly underneath the setting title in the _Plugin Settings_ admin - * page. If set, should be a short description of the setting. - * - * @var null|string + * @var Storage */ - public $description = null; + protected $storage; /** - * Text that will appear next to the setting's section in the _Plugin Settings_ admin page. If set, - * it should contain information about the setting that is more specific than a general description, - * such as the format of the setting value if it has a special format. - * - * @var null|string + * @var string */ - public $inlineHelp = null; + protected $pluginName; /** - * A closure that does some custom validation on the setting before the setting is persisted. - * - * The closure should take two arguments: the setting value and the {@link Setting} instance being - * validated. If the value is found to be invalid, the closure should throw an exception with - * a message that describes the error. - * - * **Example** - * - * $setting->validate = function ($value, Setting $setting) { - * if ($value > 60) { - * throw new \Exception('The time limit is not allowed to be greater than 60 minutes.'); - * } - * } - * - * @var null|\Closure + * @var FieldConfig */ - public $validate = null; + protected $config; /** - * A closure that transforms the setting value. If supplied, this closure will be executed after - * the setting has been validated. - * - * _Note: If a transform is supplied, the setting's {@link $type} has no effect. This means the - * transformation function will be responsible for casting the setting value to the appropriate - * data type._ - * - * **Example** - * - * $setting->transform = function ($value, Setting $setting) { - * if ($value > 30) { - * $value = 30; - * } - * - * return (int) $value; - * } - * - * @var null|\Closure + * @var \Closure|null */ - public $transform = null; + protected $configureCallback; /** - * Default value of this setting. - * - * The default value is not casted to the appropriate data type. This means _**you**_ have to make - * sure the value is of the correct type. - * * @var mixed */ - public $defaultValue = null; + protected $defaultValue; /** - * This setting's display name, for example, `'Refresh Interval'`. - * * @var string */ - public $title = ''; - - protected $key; - protected $name; - - /** - * @var StorageInterface - */ - private $storage; - protected $pluginName; + protected $type; /** * Constructor. * * @param string $name The setting's persisted name. Only alphanumeric characters are allowed, eg, * `'refreshInterval'`. - * @param string $title The setting's display name, eg, `'Refresh Interval'`. + * @param mixed $defaultValue Default value for this setting if no value was specified. + * @param string $type Eg an array, int, ... see SettingConfig::TYPE_* constants + * @param string $pluginName The name of the plugin the setting belongs to + * @throws Exception */ - public function __construct($name, $title) + public function __construct($name, $defaultValue, $type, $pluginName) { - $this->key = $name; - $this->name = $name; - $this->title = $title; + if (!ctype_alnum(str_replace('_', '', $name))) { + $msg = sprintf('The setting name "%s" in plugin "%s" is invalid. Only underscores, alpha and numerical characters are allowed', $name, $pluginName); + throw new Exception($msg); + } + + $this->name = $name; + $this->type = $type; + $this->pluginName = $pluginName; + $this->setDefaultValue($defaultValue); } /** - * Returns the setting's persisted name, eg, `'refreshInterval'`. - * + * Get the name of the setting. * @return string */ public function getName() @@ -180,32 +96,47 @@ abstract class Setting } /** - * Returns `true` if this setting is writable for the current user, `false` if otherwise. In case it returns - * writable for the current user it will be visible in the Plugin settings UI. - * - * @return bool + * Get the PHP type of the setting. + * @return string */ - public function isWritableByCurrentUser() + public function getType() { - return false; + return $this->type; } /** - * Returns `true` if this setting can be displayed for the current user, `false` if otherwise. - * - * @return bool + * @internal + * @ignore + * @param $callback */ - public function isReadableByCurrentUser() + public function setConfigureCallback($callback) { - return false; + $this->configureCallback = $callback; + $this->config = null; } /** - * Sets the object used to persist settings. - * - * @param StorageInterface $storage + * @return mixed + */ + public function getDefaultValue() + { + return $this->defaultValue; + } + + /** + * Sets/overwrites the current default value + * @param string $defaultValue */ - public function setStorage(StorageInterface $storage) + public function setDefaultValue($defaultValue) + { + $this->defaultValue = $defaultValue; + } + + /** + * @internal + * @param Storage $storage + */ + public function setStorage(Storage $storage) { $this->storage = $storage; } @@ -213,35 +144,52 @@ abstract class Setting /** * @internal * @ignore - * @return StorageInterface + * @return FieldConfig + * @throws Exception */ - public function getStorage() + public function configureField() { - return $this->storage; + if (!$this->config) { + $this->config = new FieldConfig(); + + if ($this->configureCallback) { + call_user_func($this->configureCallback, $this->config); + } + + $this->setUiControlIfNeeded($this->config); + $this->checkType($this->config); + } + + return $this->config; } /** - * Sets th name of the plugin the setting belongs to + * Set whether setting is writable or not. For example to hide setting from the UI set it to false. * - * @param string $pluginName + * @param bool $isWritable */ - public function setPluginName($pluginName) + public function setIsWritableByCurrentUser($isWritable) { - $this->pluginName = $pluginName; + $this->hasWritePermission = (bool) $isWritable; } /** - * Returns the previously persisted setting value. If no value was set, the default value - * is returned. + * Returns `true` if this setting is writable for the current user, `false` if otherwise. In case it returns + * writable for the current user it will be visible in the Plugin settings UI. * - * @return mixed - * @throws \Exception If the current user is not allowed to change the value of this setting. + * @return bool */ - public function getValue() + public function isWritableByCurrentUser() { - $this->checkHasEnoughReadPermission(); + return (bool) $this->hasWritePermission; + } - return $this->storage->getValue($this); + /** + * Saves (persists) the value for this setting in the database if a value has been actually set. + */ + public function save() + { + $this->storage->save(); } /** @@ -249,40 +197,85 @@ abstract class Setting * is returned. * * @return mixed - * @throws \Exception If the current user is not allowed to change the value of this setting. */ - public function removeValue() + public function getValue() { - $this->checkHasEnoughWritePermission(); - - return $this->storage->deleteValue($this); + return $this->storage->getValue($this->name, $this->defaultValue, $this->type); } /** * Sets and persists this setting's value overwriting any existing value. * + * Before a value is actually set it will be made sure the current user is allowed to change the value. The value + * will be first validated either via a system built-in validate method or via a set {@link FieldConfig::$validate} + * custom method. Afterwards the value will be transformed via a possibly specified {@link FieldConfig::$transform} + * method. Before storing the actual value, the value will be converted to the actually specified {@link $type}. + * * @param mixed $value * @throws \Exception If the current user is not allowed to change the value of this setting. */ public function setValue($value) { + $this->checkHasEnoughWritePermission(); + + $config = $this->configureField(); + $this->validateValue($value); - if ($this->transform && $this->transform instanceof \Closure) { - $value = call_user_func($this->transform, $value, $this); - } elseif (isset($this->type)) { + if ($config->transform && $config->transform instanceof \Closure) { + $value = call_user_func($config->transform, $value, $this); + } + + if (isset($this->type)) { settype($value, $this->type); } - return $this->storage->setValue($this, $value); + $this->storage->setValue($this->name, $value); } private function validateValue($value) { - $this->checkHasEnoughWritePermission(); - - if ($this->validate && $this->validate instanceof \Closure) { - call_user_func($this->validate, $value, $this); + $config = $this->configureField(); + + if ($config->validate && $config->validate instanceof \Closure) { + call_user_func($config->validate, $value, $this); + } elseif (is_array($config->availableValues)) { + if (is_bool($value) && $value) { + $value = '1'; + } elseif (is_bool($value)) { + $value = '0'; + } + + // TODO move error message creation to a subclass, eg in MeasurableSettings we do not want to mention plugin name + $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed', + array(strip_tags($config->title), $this->pluginName)); + + if (is_array($value) && $this->type === FieldConfig::TYPE_ARRAY) { + foreach ($value as $val) { + if (!array_key_exists($val, $config->availableValues)) { + throw new \Exception($errorMsg); + } + } + } else { + if (!array_key_exists($value, $config->availableValues)) { + throw new \Exception($errorMsg); + } + } + } elseif ($this->type === FieldConfig::TYPE_INT || $this->type === FieldConfig::TYPE_FLOAT) { + + if (!is_numeric($value)) { + $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed', + array(strip_tags($config->title), $this->pluginName)); + throw new \Exception($errorMsg); + } + + } elseif ($this->type === FieldConfig::TYPE_BOOL) { + + if (!in_array($value, array(true, false, '0', '1', 0, 1), true)) { + $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed', + array(strip_tags($config->title), $this->pluginName)); + throw new \Exception($errorMsg); + } } } @@ -291,50 +284,49 @@ abstract class Setting */ private function checkHasEnoughWritePermission() { - // When the request is a Tracker request, allow plugins to write settings - if (SettingsServer::isTrackerApiRequest()) { - return; - } - if (!$this->isWritableByCurrentUser()) { - $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingChangeNotAllowed', array($this->getName(), $this->pluginName)); + $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingChangeNotAllowed', array($this->name, $this->pluginName)); throw new \Exception($errorMsg); } } - /** - * @throws \Exception - */ - private function checkHasEnoughReadPermission() + private function setUiControlIfNeeded(FieldConfig $field) { - // When the request is a Tracker request, allow plugins to read settings - if (SettingsServer::isTrackerApiRequest()) { - return; - } - - if (!$this->isReadableByCurrentUser()) { - $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingReadNotAllowed', array($this->getName(), $this->pluginName)); - throw new \Exception($errorMsg); + if (!isset($field->uiControl)) { + $defaultControlTypes = array( + FieldConfig::TYPE_INT => FieldConfig::UI_CONTROL_TEXT, + FieldConfig::TYPE_FLOAT => FieldConfig::UI_CONTROL_TEXT, + FieldConfig::TYPE_STRING => FieldConfig::UI_CONTROL_TEXT, + FieldConfig::TYPE_BOOL => FieldConfig::UI_CONTROL_CHECKBOX, + FieldConfig::TYPE_ARRAY => FieldConfig::UI_CONTROL_MULTI_SELECT, + ); + + if (isset($defaultControlTypes[$this->type])) { + $field->uiControl = $defaultControlTypes[$this->type]; + } else { + $field->uiControl = FieldConfig::UI_CONTROL_TEXT; + } } } - /** - * Returns the unique string key used to store this setting. - * - * @return string - */ - public function getKey() + private function checkType(FieldConfig $field) { - return $this->key; - } + if ($field->uiControl === FieldConfig::UI_CONTROL_MULTI_SELECT && + $this->type !== FieldConfig::TYPE_ARRAY) { + throw new Exception('Type must be an array when using a multi select'); + } - /** - * Returns the display order. The lower the return value, the earlier the setting will be displayed. - * - * @return int - */ - public function getOrder() - { - return 100; + $types = array( + FieldConfig::TYPE_INT, + FieldConfig::TYPE_FLOAT, + FieldConfig::TYPE_STRING, + FieldConfig::TYPE_BOOL, + FieldConfig::TYPE_ARRAY + ); + + if (!in_array($this->type, $types)) { + throw new Exception('Type does not exist'); + } } + } diff --git a/core/Settings/Settings.php b/core/Settings/Settings.php new file mode 100644 index 0000000000..6c076f8cac --- /dev/null +++ b/core/Settings/Settings.php @@ -0,0 +1,107 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Settings; + +/** + * Base class of all settings providers. + * + * @api + */ +abstract class Settings +{ + /** + * An array containing all available settings: Array ( [setting-name] => [setting] ) + * + * @var Setting[] + */ + private $settings = array(); + + protected $pluginName; + + public function __construct() + { + if (!isset($this->pluginName)) { + $classname = get_class($this); + $parts = explode('\\', $classname); + + if (count($parts) >= 3) { + $this->pluginName = $parts[2]; + } else { + throw new \Exception(sprintf('Plugin Settings must have a plugin name specified in %s, could not detect plugin name', $classname)); + } + } + } + + /** + * @ignore + */ + public function getPluginName() + { + return $this->pluginName; + } + + /** + * @ignore + * @return Setting + */ + public function getSetting($name) + { + if (array_key_exists($name, $this->settings)) { + return $this->settings[$name]; + } + } + + /** + * Implemented by descendants. This method should define plugin settings (via the + * {@link addSetting()}) method and set the introduction text (via the + * {@link setIntroduction()}). + */ + abstract protected function init(); + + /** + * Returns the settings that can be displayed for the current user. + * + * @return Setting[] + */ + public function getSettingsWritableByCurrentUser() + { + return array_filter($this->settings, function (Setting $setting) { + return $setting->isWritableByCurrentUser(); + }); + } + + /** + * Makes a new plugin setting available. + * + * @param Setting $setting + * @throws \Exception If there is a setting with the same name that already exists. + * If the name contains non-alphanumeric characters. + */ + protected function addSetting(Setting $setting) + { + $name = $setting->getName(); + + if (isset($this->settings[$name])) { + throw new \Exception(sprintf('A setting with name "%s" does already exist for plugin "%s"', $name, $this->pluginName)); + } + + $this->settings[$name] = $setting; + } + + /** + * Saves (persists) the current setting values in the database. + */ + public function save() + { + foreach ($this->settings as $setting) { + $setting->save(); + } + } + +} diff --git a/core/Settings/Storage.php b/core/Settings/Storage.php deleted file mode 100644 index 131c01b111..0000000000 --- a/core/Settings/Storage.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -namespace Piwik\Settings; - -use Piwik\Option; - -/** - * Base setting type class. - * - * @api - */ -class Storage implements StorageInterface -{ - - /** - * Array containing all plugin settings values: Array( [setting-key] => [setting-value] ). - * - * @var array - */ - protected $settingsValues = array(); - - // for lazy loading of setting values - private $settingValuesLoaded = false; - - private $pluginName; - - public function __construct($pluginName) - { - $this->pluginName = $pluginName; - } - - /** - * Saves (persists) the current setting values in the database. - */ - public function save() - { - $this->loadSettingsIfNotDoneYet(); - - Option::set($this->getOptionKey(), serialize($this->settingsValues)); - } - - /** - * Removes all settings for this plugin from the database. Useful when uninstalling - * a plugin. - */ - public function deleteAllValues() - { - $this->deleteSettingsFromStorage(); - - $this->settingsValues = array(); - $this->settingValuesLoaded = false; - } - - protected function deleteSettingsFromStorage() - { - Option::delete($this->getOptionKey()); - } - - /** - * Returns the current value for a setting. If no value is stored, the default value - * is be returned. - * - * @param Setting $setting - * @return mixed - * @throws \Exception If the setting does not exist or if the current user is not allowed to change the value - * of this setting. - */ - public function getValue(Setting $setting) - { - $this->loadSettingsIfNotDoneYet(); - - if (array_key_exists($setting->getKey(), $this->settingsValues)) { - return $this->settingsValues[$setting->getKey()]; - } - - return $setting->defaultValue; - } - - /** - * Sets (overwrites) the value of a setting in memory. To persist the change, {@link save()} must be - * called afterwards, otherwise the change has no effect. - * - * Before the setting is changed, the {@link Piwik\Settings\Setting::$validate} and - * {@link Piwik\Settings\Setting::$transform} closures will be invoked (if defined). If there is no validation - * filter, the setting value will be casted to the appropriate data type. - * - * @param Setting $setting - * @param string $value - * @throws \Exception If the setting does not exist or if the current user is not allowed to change the value - * of this setting. - */ - public function setValue(Setting $setting, $value) - { - $this->loadSettingsIfNotDoneYet(); - - $this->settingsValues[$setting->getKey()] = $value; - } - - /** - * Unsets a setting value in memory. To persist the change, {@link save()} must be - * called afterwards, otherwise the change has no effect. - * - * @param Setting $setting - */ - public function deleteValue(Setting $setting) - { - $this->loadSettingsIfNotDoneYet(); - - $key = $setting->getKey(); - - if (array_key_exists($key, $this->settingsValues)) { - unset($this->settingsValues[$key]); - } - } - - public function getOptionKey() - { - return 'Plugin_' . $this->pluginName . '_Settings'; - } - - private function loadSettingsIfNotDoneYet() - { - if ($this->settingValuesLoaded) { - return; - } - - $this->settingValuesLoaded = true; - $this->settingsValues = $this->loadSettings(); - } - - protected function loadSettings() - { - $values = Option::get($this->getOptionKey()); - - if (!empty($values)) { - return unserialize($values); - } - - return array(); - } -} diff --git a/core/Settings/Storage/Backend/BackendInterface.php b/core/Settings/Storage/Backend/BackendInterface.php new file mode 100644 index 0000000000..624493460e --- /dev/null +++ b/core/Settings/Storage/Backend/BackendInterface.php @@ -0,0 +1,47 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Settings\Storage\Backend; + +/** + * Interface for a storage backend. Any new storage backend must implement this interface. + */ +interface BackendInterface +{ + + /** + * Get an id that identifies the current storage. Eg `Plugin_$pluginName_Settings` could be a storage id + * for plugin settings. It's kind of like a cache key and the value will be actually used for this by a cache + * decorator. + * + * @return string + */ + public function getStorageId(); + + /** + * Saves (persists) the current setting values in the database. Always all values that belong to a group of + * settings or backend needs to be passed. Usually existing values will be deleted and new values will be saved + * @param array $values An array of key value pairs where $settingName => $settingValue. + * Eg array('settingName1' > 'settingValue1') + */ + public function save($values); + + /** + * Deletes all saved settings. + * @return void + */ + public function delete(); + + /** + * Loads previously saved setting values and returns them (if some were saved) + * + * @return array An array of key value pairs where $settingName => $settingValue. + * Eg array('settingName1' > 'settingValue1') + */ + public function load(); +} diff --git a/core/Settings/Storage/Backend/Cache.php b/core/Settings/Storage/Backend/Cache.php new file mode 100644 index 0000000000..f3150d77ee --- /dev/null +++ b/core/Settings/Storage/Backend/Cache.php @@ -0,0 +1,78 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Storage\Backend; + +use Piwik\Settings\Storage; +use Piwik\Tracker; +use Piwik\Cache as PiwikCache; + +/** + * Loads settings from tracker cache instead of database. If not yet present in tracker cache will cache it. + * + * Can be used as a decorator in combination with any other storage backend. + */ +class Cache implements BackendInterface +{ + /** + * @var BackendInterface + */ + private $backend; + + public function __construct(BackendInterface $backend) + { + $this->backend = $backend; + } + + /** + * Saves (persists) the current setting values in the database. + */ + public function save($values) + { + $this->backend->save($values); + self::clearCache(); + } + + public function getStorageId() + { + return $this->backend->getStorageId(); + } + + public function delete() + { + $this->backend->delete(); + self::clearCache(); + } + + public function load() + { + $cacheId = $this->getStorageId(); + $cache = self::buildCache(); + + if ($cache->contains($cacheId)) { + return $cache->fetch($cacheId); + } + + $settings = $this->backend->load(); + $cache->save($cacheId, $settings); + + return $settings; + } + + public static function clearCache() + { + Tracker\Cache::deleteTrackerCache(); + self::buildCache()->flushAll(); + } + + public static function buildCache() + { + return PiwikCache::getEagerCache(); + } +} diff --git a/core/Settings/Storage/Backend/MeasurableSettingsTable.php b/core/Settings/Storage/Backend/MeasurableSettingsTable.php new file mode 100644 index 0000000000..22452288c8 --- /dev/null +++ b/core/Settings/Storage/Backend/MeasurableSettingsTable.php @@ -0,0 +1,167 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Storage\Backend; + +use Piwik\Common; +use Piwik\Db; +use Exception; + +/** + * Measurable settings backend. Stores all settings in a "site_setting" database table. + * + * If a value that needs to be stored is an array, will insert a new row for each value of this array. + */ +class MeasurableSettingsTable implements BackendInterface +{ + /** + * @var int + */ + private $idSite; + + /** + * @var string + */ + private $pluginName; + + /** + * @var Db\AdapterInterface + */ + private $db; + + public function __construct($idSite, $pluginName) + { + if (empty($pluginName)) { + throw new Exception('No plugin name given for MeasurableSettingsTable backend'); + } + + if (empty($idSite)) { + throw new Exception('No idSite given for MeasurableSettingsTable backend'); + } + + $this->idSite = (int) $idSite; + $this->pluginName = $pluginName; + } + + private function initDbIfNeeded() + { + if (!isset($this->db)) { + // we need to avoid db creation on instance creation, especially important in tracker mode + // the db might be never actually used when values are eg fetched from a cache + $this->db = Db::get(); + } + } + + /** + * @inheritdoc + */ + public function getStorageId() + { + return 'MeasurableSettings_' . $this->idSite . '_' . $this->pluginName; + } + + /** + * Saves (persists) the current setting values in the database. + */ + public function save($values) + { + $this->initDbIfNeeded(); + + $table = $this->getTableName(); + + $this->delete(); + + foreach ($values as $name => $value) { + if (!is_array($value)) { + $value = array($value); + } + + foreach ($value as $val) { + if (!isset($val)) { + continue; + } + + if (is_bool($val)) { + $val = (int) $val; + } + + $sql = "INSERT INTO $table (`idsite`, `plugin_name`, `setting_name`, `setting_value`) VALUES (?, ?, ?, ?)"; + $bind = array($this->idSite, $this->pluginName, $name, $val); + + $this->db->query($sql, $bind); + } + } + } + + public function load() + { + $this->initDbIfNeeded(); + + $table = $this->getTableName(); + + $sql = "SELECT `setting_name`, `setting_value` FROM " . $table . " WHERE idsite = ? and plugin_name = ?"; + $bind = array($this->idSite, $this->pluginName); + + $settings = $this->db->fetchAll($sql, $bind); + + $flat = array(); + foreach ($settings as $setting) { + $name = $setting['setting_name']; + + if (array_key_exists($name, $flat)) { + if (!is_array($flat[$name])) { + $flat[$name] = array($flat[$name]); + } + $flat[$name][] = $setting['setting_value']; + } else { + $flat[$name] = $setting['setting_value']; + } + } + + return $flat; + } + + private function getTableName() + { + return Common::prefixTable('site_setting'); + } + + public function delete() + { + $this->initDbIfNeeded(); + + $table = $this->getTableName(); + $sql = "DELETE FROM $table WHERE `idsite` = ? and plugin_name = ?"; + $bind = array($this->idSite, $this->pluginName); + + $this->db->query($sql, $bind); + } + + /** + * @internal + * @param int $idSite + * @throws \Exception + */ + public static function removeAllSettingsForSite($idSite) + { + $query = sprintf('DELETE FROM %s WHERE idsite = ?', Common::prefixTable('site_setting')); + Db::query($query, array($idSite)); + } + + /** + * @internal + * @param string $pluginName + * @throws \Exception + */ + public static function removeAllSettingsForPlugin($pluginName) + { + $query = sprintf('DELETE FROM %s WHERE plugin_name = ?', Common::prefixTable('site_setting')); + Db::query($query, array($pluginName)); + } +} diff --git a/core/Settings/Storage/Backend/Null.php b/core/Settings/Storage/Backend/Null.php new file mode 100644 index 0000000000..cc64c55654 --- /dev/null +++ b/core/Settings/Storage/Backend/Null.php @@ -0,0 +1,44 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Storage\Backend; + +use Piwik\Settings\Storage; + +/** + * Static / temporary storage where a value shall never be persisted. Meaning it will use the default value + * for each request until configured differently. Useful for tests etc. + */ +class Null implements BackendInterface +{ + private $storageId; + + public function __construct($storageId) + { + $this->storageId = $storageId; + } + + public function load() + { + return array(); + } + + public function getStorageId() + { + return $this->storageId; + } + + public function delete() + { + } + + public function save($values) + { + } +} diff --git a/core/Settings/Storage/Backend/PluginSettingsTable.php b/core/Settings/Storage/Backend/PluginSettingsTable.php new file mode 100644 index 0000000000..87476ff8fb --- /dev/null +++ b/core/Settings/Storage/Backend/PluginSettingsTable.php @@ -0,0 +1,175 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Storage\Backend; + +use Piwik\Common; +use Piwik\Db; +use Exception; + +/** + * Plugin settings backend. Stores all settings in a "plugin_setting" database table. + * + * If a value that needs to be stored is an array, will insert a new row for each value of this array. + */ +class PluginSettingsTable implements BackendInterface +{ + /** + * @var string + */ + private $pluginName; + + /** + * @var string + */ + private $userLogin; + + /** + * @var Db\AdapterInterface + */ + private $db; + + public function __construct($pluginName, $userLogin) + { + if (empty($pluginName)) { + throw new Exception('No plugin name given for PluginSettingsTable backend'); + } + + if ($userLogin === false || $userLogin === null) { + throw new Exception('Invalid user login name given for PluginSettingsTable backend'); + } + + $this->pluginName = $pluginName; + $this->userLogin = $userLogin; + } + + private function initDbIfNeeded() + { + if (!isset($this->db)) { + // we do not want to create a db connection on backend creation + $this->db = Db::get(); + } + } + + /** + * @inheritdoc + */ + public function getStorageId() + { + return 'PluginSettings_' . $this->pluginName . '_User_' . $this->userLogin; + } + + /** + * Saves (persists) the current setting values in the database. + */ + public function save($values) + { + $this->initDbIfNeeded(); + + $table = $this->getTableName(); + + $this->delete(); + + foreach ($values as $name => $value) { + + if (!is_array($value)) { + $value = array($value); + } + + foreach ($value as $val) { + if (!isset($val)) { + continue; + } + + if (is_bool($val)) { + $val = (int) $val; + } + + $sql = "INSERT INTO $table (`plugin_name`, `user_login`, `setting_name`, `setting_value`) VALUES (?, ?, ?, ?)"; + $bind = array($this->pluginName, $this->userLogin, $name, $val); + + $this->db->query($sql, $bind); + } + } + } + + public function load() + { + $this->initDbIfNeeded(); + + $sql = "SELECT `setting_name`, `setting_value` FROM " . $this->getTableName() . " WHERE plugin_name = ? and user_login = ?"; + $bind = array($this->pluginName, $this->userLogin); + + $settings = $this->db->fetchAll($sql, $bind); + + $flat = array(); + foreach ($settings as $setting) { + $name = $setting['setting_name']; + + if (array_key_exists($name, $flat)) { + if (!is_array($flat[$name])) { + $flat[$name] = array($flat[$name]); + } + $flat[$name][] = $setting['setting_value']; + } else { + $flat[$name] = $setting['setting_value']; + } + } + + return $flat; + } + + private function getTableName() + { + return Common::prefixTable('plugin_setting'); + } + + public function delete() + { + $this->initDbIfNeeded(); + + $table = $this->getTableName(); + $sql = "DELETE FROM $table WHERE `plugin_name` = ? and `user_login` = ?"; + $bind = array($this->pluginName, $this->userLogin); + + $this->db->query($sql, $bind); + } + + /** + * Unsets all settings for a user. The settings will be removed from the database. Used when + * a user is deleted. + * + * @internal + * @param string $userLogin + * @throws \Exception If the `$userLogin` is empty. Otherwise we would delete most plugin settings + */ + public static function removeAllUserSettingsForUser($userLogin) + { + if (empty($userLogin)) { + throw new Exception('No userLogin specified. Cannot remove all settings for this user'); + } + + $table = Common::prefixTable('plugin_setting'); + Db::get()->query(sprintf('DELETE FROM %s WHERE user_login = ?', $table), array($userLogin)); + } + + /** + * Unsets all settings for a plugin. The settings will be removed from the database. Used when + * a plugin is uninstalled. + * + * @internal + * @param string $pluginName + * @throws \Exception If the `$userLogin` is empty. + */ + public static function removeAllSettingsForPlugin($pluginName) + { + $table = Common::prefixTable('plugin_setting'); + Db::get()->query(sprintf('DELETE FROM %s WHERE plugin_name = ?', $table), array($pluginName)); + } +} diff --git a/core/Settings/Storage/Backend/SitesTable.php b/core/Settings/Storage/Backend/SitesTable.php new file mode 100644 index 0000000000..81de89b100 --- /dev/null +++ b/core/Settings/Storage/Backend/SitesTable.php @@ -0,0 +1,122 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Storage\Backend; + +use Piwik\Plugins\SitesManager\Model; +use Piwik\Site; +use Exception; + +/** + * Backend for an existing site. Stores all settings in the "site" database table. + */ +class SitesTable implements BackendInterface +{ + /** + * @var int + */ + private $idSite; + + private $commaSeparatedArrayFields = array( + 'sitesearch_keyword_parameters', + 'sitesearch_category_parameters', + 'excluded_user_agents', + 'excluded_parameters', + 'excluded_ips' + ); + + // these fields are standard fields of a site and cannot be adjusted via a setting + private $allowedNames = array( + 'ecommerce', 'sitesearch', 'sitesearch_keyword_parameters', + 'sitesearch_category_parameters', 'exclude_unknown_urls', + 'excluded_ips', 'excluded_parameters', + 'excluded_user_agents', 'keep_url_fragment', 'urls' + ); + + public function __construct($idSite) + { + if (empty($idSite)) { + throw new Exception('No idSite given for Measurable backend'); + } + + $this->idSite = (int) $idSite; + } + + /** + * @inheritdoc + */ + public function getStorageId() + { + return 'SitesTable_' . $this->idSite; + } + + /** + * Saves (persists) the current setting values in the database. + */ + public function save($values) + { + $model = $this->getModel(); + + foreach ($values as $key => $value) { + if (!in_array($key, $this->allowedNames)) { + unset($values[$key]); + continue; + } + + if (is_array($value) && in_array($key, $this->commaSeparatedArrayFields)) { + $values[$key] = implode(',', $value); + } elseif (is_bool($value)) { + $values[$key] = (int) $value; + } + } + + if (!empty($values['urls'])) { + $urls = array_unique($values['urls']); + $values['main_url'] = array_shift($urls); + + $model->deleteSiteAliasUrls($this->idSite); + foreach ($urls as $url) { + $model->insertSiteUrl($this->idSite, $url); + } + } + + unset($values['urls']); + + $model->updateSite($values, $this->idSite); + Site::clearCacheForSite($this->idSite); + } + + public function load() + { + if (!empty($this->idSite)) { + $site = Site::getSite($this->idSite); + + $urls = $this->getModel(); + $site['urls'] = $urls->getSiteUrlsFromId($this->idSite); + + foreach ($this->commaSeparatedArrayFields as $field) { + if (!empty($site[$field]) && is_string($site[$field])) { + $site[$field] = explode(',', $site[$field]); + } + } + + return $site; + } + } + + private function getModel() + { + return new Model(); + } + + public function delete() + { + } + +} diff --git a/core/Settings/Storage/Factory.php b/core/Settings/Storage/Factory.php index 87d54ad3b1..5c1c6ca645 100644 --- a/core/Settings/Storage/Factory.php +++ b/core/Settings/Storage/Factory.php @@ -9,20 +9,113 @@ namespace Piwik\Settings\Storage; -use Piwik\Settings\Storage; +use Piwik\Settings\Storage\Backend\BackendInterface; use Piwik\SettingsServer; -use Piwik\Tracker\SettingsStorage; +/** + * Factory to create an instance of a storage. The storage can be created with different backends depending on the need. + * + * @package Piwik\Settings\Storage + */ class Factory { - public static function make($pluginName) + // cache prevents multiple loading of storage + private $cache = array(); + + /** + * Get a storage instance for plugin settings. + * + * The storage will hold values that belong to the given plugin name and user login. Be aware that instances + * for a specific plugin and login will be cached during one request for better performance. + * + * @param string $pluginName + * @param string $userLogin Use an empty string if settings should be not for a specific login + * @return Storage + */ + public function getPluginStorage($pluginName, $userLogin) + { + $id = $pluginName . '#' . $userLogin; + + if (!isset($this->cache[$id])) { + $backend = new Backend\PluginSettingsTable($pluginName, $userLogin); + $this->cache[$id] = $this->makeStorage($backend); + } + + return $this->cache[$id]; + } + + /** + * Get a storage instance for measurable settings. + * + * The storage will hold values that belong to the given idSite and plugin name. Be aware that a storage instance + * for a specific site and plugin will be cached during one request for better performance. + * + * @param int $idSite If idSite is empty it will use a backend that never actually persists any value. Pass + * $idSite = 0 to create a storage for a site that is about to be created. + * @param string $pluginName + * @return Storage + */ + public function getMeasurableSettingsStorage($idSite, $pluginName) + { + $id = 'measurableSettings' . (int) $idSite . '#' . $pluginName; + + if (empty($idSite)) { + return $this->getNonPersistentStorage($id . '#nonpersistent'); + } + + if (!isset($this->cache[$id])) { + $backend = new Backend\MeasurableSettingsTable($idSite, $pluginName); + $this->cache[$id] = $this->makeStorage($backend); + } + + return $this->cache[$id]; + } + + /** + * Get a storage instance for settings that will be saved in the "site" table. + * + * The storage will hold values that belong to the given idSite. Be aware that a storage instance for a specific + * site will be cached during one request for better performance. + * + * @param int $idSite If idSite is empty it will use a backend that never actually persists any value. Pass + * $idSite = 0 to create a storage for a site that is about to be created. + * + * @param int $idSite + * @return Storage + */ + public function getSitesTable($idSite) + { + $id = 'sitesTable#' . $idSite; + + if (empty($idSite)) { + return $this->getNonPersistentStorage($id . '#nonpersistent'); + } + + if (!isset($this->cache[$id])) { + $backend = new Backend\SitesTable($idSite); + $this->cache[$id] = $this->makeStorage($backend); + } + + return $this->cache[$id]; + } + + /** + * Get a storage with a backend that will never persist or load any value. + * + * @param string $key + * @return Storage + */ + public function getNonPersistentStorage($key) + { + return new Storage(new Backend\Null($key)); + } + + private function makeStorage(BackendInterface $backend) { if (SettingsServer::isTrackerApiRequest()) { - $storage = new SettingsStorage($pluginName); - } else { - $storage = new Storage($pluginName); + $backend = new Backend\Cache($backend); } - return $storage; + return new Storage($backend); } } diff --git a/core/Settings/Storage/StaticStorage.php b/core/Settings/Storage/StaticStorage.php deleted file mode 100644 index d8a43b6c9a..0000000000 --- a/core/Settings/Storage/StaticStorage.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -namespace Piwik\Settings\Storage; - -use Piwik\Settings\Storage; - -/** - * Static / temporary storage where a value will never be persisted meaning it will use the default value - * for each request until configured differently. Useful for tests. - * - * @api - */ -class StaticStorage extends Storage -{ - - protected function loadSettings() - { - return array(); - } - - /** - * Saves (persists) the current setting values in the database. - */ - public function save() - { - } -} diff --git a/core/Settings/Storage/Storage.php b/core/Settings/Storage/Storage.php new file mode 100644 index 0000000000..b24282068d --- /dev/null +++ b/core/Settings/Storage/Storage.php @@ -0,0 +1,117 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Settings\Storage; + +use Piwik\Settings\Storage\Backend; + +/** + * A storage stores values for multiple settings. Storing multiple settings here saves having to do + * a "get" for each individual setting. A storage is usually stared between all individual setting instances + * within a plugin. + */ +class Storage +{ + /** + * Array containing all plugin settings values: Array( [setting-key] => [setting-value] ). + * + * @var array + */ + protected $settingsValues = array(); + + // for lazy loading of setting values + private $settingValuesLoaded = false; + + /** + * @var Backend\BackendInterface + */ + private $backend; + + /** + * Defines whether a value has changed since the settings were loaded or not. + * @var bool + */ + private $isDirty = false; + + public function __construct(Backend\BackendInterface $backend) + { + $this->backend = $backend; + } + + /** + * Get the currently used backend for this storage. + * @return Backend\BackendInterface + */ + public function getBackend() + { + return $this->backend; + } + + /** + * Saves (persists) the current setting values in the database if a value has actually changed. + */ + public function save() + { + if ($this->isDirty) { + $this->backend->save($this->settingsValues); + + $this->isDirty = false; + + Backend\Cache::clearCache(); + } + } + + /** + * Returns the current value for a setting. If no value is stored, the default value + * is be returned. + * + * @param string $key The name / key of a setting + * @param mixed $defaultValue Default value that will be used in case no value for this setting exists yet + * @param string $type The PHP internal type the value of the setting should have, see FieldConfig::TYPE_* + * constants. Only an actual value of the setting will be converted to the given type, the + * default value will not be converted. + * @return mixed + */ + public function getValue($key, $defaultValue, $type) + { + $this->loadSettingsIfNotDoneYet(); + + if (array_key_exists($key, $this->settingsValues)) { + settype($this->settingsValues[$key], $type); + return $this->settingsValues[$key]; + } + + return $defaultValue; + } + + /** + * Sets (overwrites) the value of a setting in memory. To persist the change across requests, {@link save()} must be + * called. + * + * @param string $key The name / key of a setting + * @param mixed $value The value that shall be set for the given setting. + */ + public function setValue($key, $value) + { + $this->loadSettingsIfNotDoneYet(); + + $this->isDirty = true; + $this->settingsValues[$key] = $value; + } + + private function loadSettingsIfNotDoneYet() + { + if ($this->settingValuesLoaded) { + return; + } + + $this->settingValuesLoaded = true; + $this->settingsValues = $this->backend->load(); + } +} diff --git a/core/Settings/StorageInterface.php b/core/Settings/StorageInterface.php deleted file mode 100644 index 6a4a76c5ac..0000000000 --- a/core/Settings/StorageInterface.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Settings; - -/** - * Base type of all Setting storage implementations. - */ -interface StorageInterface -{ - /** - * Gets the current value for this setting. If no value is specified, the default value will be returned. - * - * @param Setting $setting - * - * @return mixed - * - * @throws \Exception In case the setting does not exist or if the current user is not allowed to change the value - * of this setting. - */ - public function getValue(Setting $setting); - - /** - * Removes the value for the given setting. Make sure to call `save()` afterwards, otherwise the removal has no - * effect. - * - * @param Setting $setting - */ - public function deleteValue(Setting $setting); - - /** - * Sets (overwrites) the value for the given setting. Make sure to call `save()` afterwards, otherwise the change - * has no effect. Before the value is saved a possibly define `validate` closure and `filter` closure will be - * called. Alternatively the value will be casted to the specfied setting type. - * - * @param Setting $setting - * @param string $value - * - * @throws \Exception In case the setting does not exist or if the current user is not allowed to change the value - * of this setting. - */ - public function setValue(Setting $setting, $value); - - /** - * Removes all settings for this plugin from the database. Useful when uninstalling - * a plugin. - */ - public function deleteAllValues(); - - /** - * Saves (persists) the current setting values in the database. - */ - public function save(); -} diff --git a/core/Settings/UserSetting.php b/core/Settings/UserSetting.php deleted file mode 100644 index aa3a08c400..0000000000 --- a/core/Settings/UserSetting.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -namespace Piwik\Settings; - -use Piwik\Common; -use Piwik\Piwik; - -/** - * Describes a per user setting. Each user will be able to change this setting for themselves, - * but not for other users. - * - * - * @api - */ -class UserSetting extends Setting -{ - private $userLogin = null; - - /** - * Null while not initialized, bool otherwise. - * @var null|bool - */ - private $hasReadAndWritePermission = null; - - /** - * Constructor. - * - * @param string $name The setting's persisted name. - * @param string $title The setting's display name. - * @param null|string $userLogin The user this setting applies to. Will default to the current user login. - */ - public function __construct($name, $title, $userLogin = null) - { - parent::__construct($name, $title); - - $this->setUserLogin($userLogin); - } - - /** - * Returns `true` if this setting can be displayed for the current user, `false` if otherwise. - * - * @return bool - */ - public function isReadableByCurrentUser() - { - return $this->isWritableByCurrentUser(); - } - - /** - * Returns `true` if this setting can be displayed for the current user, `false` if otherwise. - * - * @return bool - */ - public function isWritableByCurrentUser() - { - if (isset($this->hasReadAndWritePermission)) { - return $this->hasReadAndWritePermission; - } - - $this->hasReadAndWritePermission = Piwik::isUserHasSomeViewAccess(); - - return $this->hasReadAndWritePermission; - } - - /** - * Returns the display order. User settings are displayed after system settings. - * - * @return int - */ - public function getOrder() - { - return 60; - } - - private function buildUserSettingName($name, $userLogin = null) - { - if (empty($userLogin)) { - $userLogin = Piwik::getCurrentUserLogin(); - } - - // the asterisk tag is indeed important here and better than an underscore. Imagine a plugin has the settings - // "api_password" and "api". A user having the login "_password" could otherwise under circumstances change the - // setting for "api" although he is not allowed to. It is not so important at the moment because only alNum is - // currently allowed as a name this might change in the future. - $appendix = '#' . $userLogin . '#'; - - if (Common::stringEndsWith($name, $appendix)) { - return $name; - } - - return $name . $appendix; - } - - /** - * Sets the name of the user this setting will be set for. - * - * @param $userLogin - * @throws \Exception If the current user does not have permission to set the setting value - * of `$userLogin`. - */ - public function setUserLogin($userLogin) - { - if (!empty($userLogin) && !Piwik::hasUserSuperUserAccessOrIsTheUser($userLogin)) { - throw new \Exception('You do not have the permission to read the settings of a different user'); - } - - $this->userLogin = $userLogin; - $this->key = $this->buildUserSettingName($this->name, $userLogin); - } - - /** - * Unsets all settings for a user. The settings will be removed from the database. Used when - * a user is deleted. - * - * @param string $userLogin - * @throws \Exception If the `$userLogin` is empty. - */ - public static function removeAllUserSettingsForUser($userLogin) - { - if (empty($userLogin)) { - throw new \Exception('No userLogin specified'); - } - - $pluginsSettings = Manager::getAllPluginSettings(); - - foreach ($pluginsSettings as $pluginSettings) { - $settings = $pluginSettings->getSettings(); - - foreach ($settings as $setting) { - if ($setting instanceof UserSetting) { - $setting->setUserLogin($userLogin); - $setting->removeValue(); - } - } - - $pluginSettings->save(); - } - } -} diff --git a/core/Site.php b/core/Site.php index 3bc535fc2d..a45a4a5e2e 100644 --- a/core/Site.php +++ b/core/Site.php @@ -419,6 +419,17 @@ class Site } /** + * Clears the site data cache. + * + * See also {@link setSites()} and {@link setSitesFromArray()}. + */ + public static function clearCacheForSite($idSite) + { + $idSite = (int)$idSite; + unset(self::$infoSites[$idSite]); + } + + /** * Utility function. Returns the value of the specified field for the * site with the specified ID. * diff --git a/core/Tracker/SettingsStorage.php b/core/Tracker/SettingsStorage.php deleted file mode 100644 index a69e03553f..0000000000 --- a/core/Tracker/SettingsStorage.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -namespace Piwik\Tracker; - -use Piwik\Settings\Storage; -use Piwik\Tracker; -use Piwik\Cache as PiwikCache; - -/** - * Loads settings from tracker cache instead of database. If not yet present in tracker cache will cache it. - */ -class SettingsStorage extends Storage -{ - protected function loadSettings() - { - $cacheId = $this->getOptionKey(); - $cache = $this->getCache(); - - if ($cache->contains($cacheId)) { - $settings = $cache->fetch($cacheId); - } else { - $settings = parent::loadSettings(); - - $cache->save($cacheId, $settings); - } - - return $settings; - } - - public function save() - { - parent::save(); - self::clearCache(); - } - - private function getCache() - { - return self::buildCache($this->getOptionKey()); - } - - public static function clearCache() - { - Cache::deleteTrackerCache(); - self::buildCache()->flushAll(); - } - - private static function buildCache() - { - return PiwikCache::getEagerCache(); - } -} diff --git a/core/Updates/3.0.0-b1.php b/core/Updates/3.0.0-b1.php index 301bc44a1b..b7fbd2d027 100644 --- a/core/Updates/3.0.0-b1.php +++ b/core/Updates/3.0.0-b1.php @@ -11,6 +11,7 @@ namespace Piwik\Updates; use Piwik\Common; use Piwik\Db; +use Piwik\DbHelper; use Piwik\Updater; use Piwik\Updates; use Piwik\Plugins\Dashboard; @@ -30,7 +31,11 @@ class Updates_3_0_0_b1 extends Updates $allGoals = $db->fetchAll(sprintf("SELECT DISTINCT idgoal FROM %s", Common::prefixTable('goal'))); $allDashboards = $db->fetchAll(sprintf("SELECT * FROM %s", Common::prefixTable('user_dashboard'))); - return $this->getDashboardMigrationSqls($allDashboards, $allGoals); + $queries = $this->getDashboardMigrationSqls($allDashboards, $allGoals); + $queries = $this->getPluginSettingsMigrationQueries($queries, $db); + $queries = $this->getSiteSettingsMigrationQueries($queries); + + return $queries; } public function doUpdate(Updater $updater) @@ -38,11 +43,104 @@ class Updates_3_0_0_b1 extends Updates $updater->executeMigrationQueries(__FILE__, $this->getMigrationQueries($updater)); } + /** + * @param $queries + * @param Db $db + * @return mixed + */ + private function getPluginSettingsMigrationQueries($queries, $db) + { + $pluginSettingsTableName = $this->getPluginSettingsTableName(); + $dbSettings = new Db\Settings(); + $engine = $dbSettings->getEngine(); + + $pluginSettingsTable = "CREATE TABLE $pluginSettingsTableName ( + `plugin_name` VARCHAR(60) NOT NULL, + `setting_name` VARCHAR(255) NOT NULL, + `setting_value` LONGTEXT NOT NULL, + `user_login` VARCHAR(100) NOT NULL DEFAULT '', + INDEX(plugin_name, user_login) + ) ENGINE=$engine DEFAULT CHARSET=utf8 + "; + $queries[$pluginSettingsTable] = 1050; + + $optionTable = Common::prefixTable('option'); + $query = 'SELECT `option_name`, `option_value` FROM `' . $optionTable . '` WHERE `option_name` LIKE "Plugin_%_Settings"'; + $options = Db::get()->fetchAll($query); + + foreach ($options as $option) { + $name = $option['option_name']; + $pluginName = str_replace(array('Plugin_', '_Settings'), '', $name); + $values = @unserialize($option['option_value']); + + if (empty($values)) { + continue; + } + + foreach ($values as $settingName => $settingValue) { + if (!is_array($settingValue)) { + $settingValue = array($settingValue); + } + + foreach ($settingValue as $val) { + $queries[$this->createPluginSettingQuery($pluginName, $settingName, $val)] = 1062; + } + } + } + + $queries[$query = sprintf('DELETE FROM `%s` WHERE `option_name` like "Plugin_%%_Settings"', $optionTable)] = false; + + return $queries; + } + + /** + * @param $queries + * @param Db $db + * @return mixed + */ + private function getSiteSettingsMigrationQueries($queries) + { + $table = Common::prefixTable('site_setting'); + + $pluginSettingsTable = "ALTER TABLE $table ADD COLUMN `plugin_name` VARCHAR(60) NOT NULL AFTER `idsite`"; + $queries[$pluginSettingsTable] = 1060; + $queries["ALTER TABLE $table DROP PRIMARY KEY, ADD INDEX(idsite, plugin_name);"] = false; + + // we cannot migrate existing settings as we do not know the related plugin name, but this feature + // (measurablesettings) was not really used anyway. If a migration is somewhere really needed it has to be + // handled in the plugin + $queries[sprintf('DELETE FROM `%s`', $table)] = false; + + return $queries; + } + + private function createPluginSettingQuery($pluginName, $settingName, $settingValue) + { + $table = $this->getPluginSettingsTableName(); + + $login = ''; + if (preg_match('/^.+#(.+)#$/', $settingName, $matches)) { + $login = $matches[1]; + $settingName = str_replace('#' . $login . '#', '', $settingName); + } + + $db = Db::get(); + + $query = sprintf("INSERT INTO %s (plugin_name, setting_name, setting_value, user_login) VALUES ", $table); + $query .= sprintf("(%s, %s, %s, %s)", $db->quote($pluginName), $db->quote($settingName), $db->quote($settingValue), $db->quote($login)); + + return $query; + } + + private function getPluginSettingsTableName() + { + return Common::prefixTable('plugin_setting'); + } + private function getDashboardMigrationSqls($allDashboards, $allGoals) { $sqls = array(); - // update dashboard to use new widgets $oldWidgets = array( array ( diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php index f275aebe58..14a6e13ed5 100644 --- a/core/ViewDataTable/Config.php +++ b/core/ViewDataTable/Config.php @@ -15,7 +15,7 @@ use Piwik\DataTable; use Piwik\DataTable\Filter\PivotByDimension; use Piwik\Metrics; use Piwik\Plugins\API\API; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; /** * Contains base display properties for {@link Piwik\Plugin\ViewDataTable}s. Manipulating these @@ -717,7 +717,7 @@ class Config private function setShouldShowPivotBySubtable() { - $report = Reports::factory($this->controllerName, $this->controllerAction); + $report = ReportsProvider::factory($this->controllerName, $this->controllerAction); if (empty($report)) { $this->show_pivot_by_subtable = false; diff --git a/core/ViewDataTable/Factory.php b/core/ViewDataTable/Factory.php index 543f1280cf..846de225c2 100644 --- a/core/ViewDataTable/Factory.php +++ b/core/ViewDataTable/Factory.php @@ -12,7 +12,7 @@ use Piwik\Common; use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; /** * Provides a means of creating {@link Piwik\Plugin\ViewDataTable} instances by ID. @@ -168,7 +168,7 @@ class Factory private static function getReport($apiAction) { list($module, $action) = explode('.', $apiAction); - $report = Reports::factory($module, $action); + $report = ReportsProvider::factory($module, $action); return $report; } diff --git a/core/Widget/WidgetsList.php b/core/Widget/WidgetsList.php index dfeb004277..14c626b0a9 100644 --- a/core/Widget/WidgetsList.php +++ b/core/Widget/WidgetsList.php @@ -175,7 +175,7 @@ class WidgetsList { $list = new static; - $widgets = StaticContainer::get('Piwik\Plugin\Widgets'); + $widgets = StaticContainer::get('Piwik\Plugin\WidgetsProvider'); $widgetContainerConfigs = $widgets->getWidgetContainerConfigs(); foreach ($widgetContainerConfigs as $config) { @@ -191,7 +191,7 @@ class WidgetsList } } - $reports = StaticContainer::get('Piwik\Plugin\Reports'); + $reports = StaticContainer::get('Piwik\Plugin\ReportsProvider'); $reports = $reports->getAllReports(); foreach ($reports as $report) { if ($report->isEnabled()) { diff --git a/plugins/API/API.php b/plugins/API/API.php index 528d17e8cf..67a86ff386 100644 --- a/plugins/API/API.php +++ b/plugins/API/API.php @@ -25,8 +25,10 @@ use Piwik\Metrics; use Piwik\Period; use Piwik\Period\Range; use Piwik\Piwik; +use Piwik\Plugin\SettingsProvider; use Piwik\Plugins\API\DataTable\MergeDataTables; use Piwik\Plugins\CoreAdminHome\CustomLogo; +use Piwik\Plugins\CorePluginsAdmin\SettingsMetadata; use Piwik\Translation\Translator; use Piwik\Measurable\Type\TypeManager; use Piwik\Version; @@ -55,6 +57,16 @@ require_once PIWIK_INCLUDE_PATH . '/core/Config.php'; class API extends \Piwik\Plugin\API { /** + * @var SettingsProvider + */ + private $settingsProvider; + + public function __construct(SettingsProvider $settingsProvider) + { + $this->settingsProvider = $settingsProvider; + } + + /** * Get Piwik version * @return string */ @@ -113,11 +125,16 @@ class API extends \Piwik\Plugin\API $available = array(); foreach ($types as $type) { + $measurableSettings = $this->settingsProvider->getAllMeasurableSettings($idSite = 0, $type->getId()); + + $settingsMetadata = new SettingsMetadata(); + $available[] = array( 'id' => $type->getId(), 'name' => Piwik::translate($type->getName()), 'description' => Piwik::translate($type->getDescription()), - 'howToSetupUrl' => $type->getHowToSetupUrl() + 'howToSetupUrl' => $type->getHowToSetupUrl(), + 'settings' => $settingsMetadata->formatSettings($measurableSettings) ); } diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php index 8a9a96b537..f5c5cc1a91 100644 --- a/plugins/API/ProcessedReport.php +++ b/plugins/API/ProcessedReport.php @@ -23,7 +23,7 @@ use Piwik\Metrics\Formatter; use Piwik\Period; use Piwik\Piwik; use Piwik\Plugin\Report; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Site; use Piwik\Timer; use Piwik\Url; @@ -168,7 +168,7 @@ class ProcessedReport $availableReports = array(); - $reports = new Reports(); + $reports = new ReportsProvider(); foreach ($reports->getAllReports() as $report) { $report->configureReportMetadata($availableReports, $parameters); } @@ -291,7 +291,7 @@ class ProcessedReport */ private static function sortReports($a, $b) { - $reports = new Reports(); + $reports = new ReportsProvider(); return $reports->compareCategories($a['category'], $a['subcategory'], $a['order'], $b['category'], $b['subcategory'], $b['order']); } diff --git a/plugins/API/Reports/Get.php b/plugins/API/Reports/Get.php index 61cded3856..8adc67b4ed 100644 --- a/plugins/API/Reports/Get.php +++ b/plugins/API/Reports/Get.php @@ -10,7 +10,7 @@ namespace Piwik\Plugins\API\Reports; use Piwik\Piwik; use Piwik\Plugin\Report; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class Get extends Report { @@ -81,7 +81,7 @@ class Get extends Report */ private function getReportsToMerge() { - $reports = new Reports(); + $reports = new ReportsProvider(); $result = array(); foreach ($reports->getAllReportClasses() as $reportClass) { if ($reportClass == 'Piwik\\Plugins\\API\\Reports\\Get') { diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php index e475f4240c..4be588f9fd 100644 --- a/plugins/Actions/API.php +++ b/plugins/Actions/API.php @@ -22,7 +22,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; use Piwik\Plugins\CustomVariables\API as APICustomVariables; use Piwik\Plugins\Actions\Actions\ActionSiteSearch; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Tracker\Action; use Piwik\Tracker\PageUrl; @@ -56,7 +56,7 @@ class API extends \Piwik\Plugin\API { Piwik::checkUserHasViewAccess($idSite); - $report = Reports::factory("Actions", "get"); + $report = ReportsProvider::factory("Actions", "get"); $archive = Archive::build($idSite, $period, $date, $segment); $requestedColumns = Piwik::getArrayFromApiParameter($columns); diff --git a/plugins/Actions/Reports/GetEntryPageTitles.php b/plugins/Actions/Reports/GetEntryPageTitles.php index 9df1d74450..5f81112116 100644 --- a/plugins/Actions/Reports/GetEntryPageTitles.php +++ b/plugins/Actions/Reports/GetEntryPageTitles.php @@ -15,7 +15,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Report\ReportWidgetFactory; use Piwik\Widget\WidgetsList; @@ -85,8 +85,8 @@ class GetEntryPageTitles extends Base public function getRelatedReports() { return array( - Reports::factory('Actions', 'getPageTitles'), - Reports::factory('Actions', 'getEntryPageUrls') + ReportsProvider::factory('Actions', 'getPageTitles'), + ReportsProvider::factory('Actions', 'getEntryPageUrls') ); } } diff --git a/plugins/Actions/Reports/GetEntryPageUrls.php b/plugins/Actions/Reports/GetEntryPageUrls.php index dfeeb8cf04..4714b31a6b 100644 --- a/plugins/Actions/Reports/GetEntryPageUrls.php +++ b/plugins/Actions/Reports/GetEntryPageUrls.php @@ -15,7 +15,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Report\ReportWidgetFactory; use Piwik\Widget\WidgetsList; @@ -83,7 +83,7 @@ class GetEntryPageUrls extends Base public function getRelatedReports() { return array( - Reports::factory('Actions', 'getEntryPageTitles'), + ReportsProvider::factory('Actions', 'getEntryPageTitles'), ); } } diff --git a/plugins/Actions/Reports/GetExitPageTitles.php b/plugins/Actions/Reports/GetExitPageTitles.php index 53f235209d..74ceaf6b17 100644 --- a/plugins/Actions/Reports/GetExitPageTitles.php +++ b/plugins/Actions/Reports/GetExitPageTitles.php @@ -15,7 +15,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Report\ReportWidgetFactory; use Piwik\Widget\WidgetsList; @@ -94,8 +94,8 @@ class GetExitPageTitles extends Base public function getRelatedReports() { return array( - Reports::factory('Actions', 'getPageTitles'), - Reports::factory('Actions', 'getExitPageUrls'), + ReportsProvider::factory('Actions', 'getPageTitles'), + ReportsProvider::factory('Actions', 'getExitPageUrls'), ); } } diff --git a/plugins/Actions/Reports/GetExitPageUrls.php b/plugins/Actions/Reports/GetExitPageUrls.php index bc4d29782d..6ad6865c6c 100644 --- a/plugins/Actions/Reports/GetExitPageUrls.php +++ b/plugins/Actions/Reports/GetExitPageUrls.php @@ -16,7 +16,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Report\ReportWidgetFactory; use Piwik\Widget\WidgetsList; @@ -98,7 +98,7 @@ class GetExitPageUrls extends Base public function getRelatedReports() { return array( - Reports::factory('Actions', 'getExitPageTitles'), + ReportsProvider::factory('Actions', 'getExitPageTitles'), ); } diff --git a/plugins/Actions/Reports/GetPageTitles.php b/plugins/Actions/Reports/GetPageTitles.php index ed1846ddd6..5a5e0581e9 100644 --- a/plugins/Actions/Reports/GetPageTitles.php +++ b/plugins/Actions/Reports/GetPageTitles.php @@ -16,7 +16,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Report\ReportWidgetFactory; use Piwik\Widget\WidgetsList; @@ -82,8 +82,8 @@ class GetPageTitles extends Base public function getRelatedReports() { return array( - Reports::factory('Actions', 'getEntryPageTitles'), - Reports::factory('Actions', 'getExitPageTitles'), + ReportsProvider::factory('Actions', 'getEntryPageTitles'), + ReportsProvider::factory('Actions', 'getExitPageTitles'), ); } } diff --git a/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php b/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php index 3d50ba4f95..4b3911780a 100644 --- a/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php +++ b/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php @@ -15,7 +15,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetPageTitlesFollowingSiteSearch extends SiteSearchBase { @@ -82,7 +82,7 @@ class GetPageTitlesFollowingSiteSearch extends SiteSearchBase public function getRelatedReports() { return array( - Reports::factory('Actions', 'getPageUrlsFollowingSiteSearch'), + ReportsProvider::factory('Actions', 'getPageUrlsFollowingSiteSearch'), ); } } diff --git a/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php b/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php index 063d3855b5..ae1bb8927d 100644 --- a/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php +++ b/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\Actions\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\Actions\Columns\DestinationPage; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetPageUrlsFollowingSiteSearch extends GetPageTitlesFollowingSiteSearch { @@ -36,7 +36,7 @@ class GetPageUrlsFollowingSiteSearch extends GetPageTitlesFollowingSiteSearch public function getRelatedReports() { return array( - Reports::factory('Actions', 'getPageTitlesFollowingSiteSearch'), + ReportsProvider::factory('Actions', 'getPageTitlesFollowingSiteSearch'), ); } } diff --git a/plugins/AnonymousPiwikUsageMeasurement b/plugins/AnonymousPiwikUsageMeasurement -Subproject 3d1160c0980377b140c3ebb987cef4b01ce1b3a +Subproject eb8a1669250ee8d7f889d7cf2e29efcefa7bde8 diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php index 19e0f1c9c1..f8031c76e4 100644 --- a/plugins/CoreAdminHome/Controller.php +++ b/plugins/CoreAdminHome/Controller.php @@ -14,7 +14,6 @@ use Piwik\ArchiveProcessor\Rules; use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; -use Piwik\DataTable\Renderer\Json; use Piwik\Menu\MenuTop; use Piwik\Nonce; use Piwik\Piwik; @@ -24,9 +23,6 @@ use Piwik\Plugins\CustomVariables\CustomVariables; use Piwik\Plugins\LanguagesManager\LanguagesManager; use Piwik\Plugins\PrivacyManager\DoNotTrackHeaderChecker; use Piwik\Plugins\SitesManager\API as APISitesManager; -use Piwik\Settings\Manager as SettingsManager; -use Piwik\Settings\SystemSetting; -use Piwik\Settings\UserSetting; use Piwik\Site; use Piwik\Translation\Translator; use Piwik\UpdateCheck; @@ -35,8 +31,6 @@ use Piwik\View; class Controller extends ControllerAdmin { - const SET_PLUGIN_SETTINGS_NONCE = 'CoreAdminHome.setPluginSettings'; - /** * @var Translator */ @@ -82,175 +76,6 @@ class Controller extends ControllerAdmin return $view->render(); } - public function adminPluginSettings() - { - Piwik::checkUserHasSuperUserAccess(); - - $settings = $this->getPluginSettings(); - - $vars = array( - 'nonce' => Nonce::getNonce(static::SET_PLUGIN_SETTINGS_NONCE), - 'pluginsSettings' => $this->getSettingsByType($settings, 'admin'), - 'firstSuperUserSettingNames' => $this->getFirstSuperUserSettingNames($settings), - 'mode' => 'admin' - ); - - return $this->renderTemplate('pluginSettings', $vars); - } - - /** - * @param \Piwik\Plugin\Settings[] $pluginsSettings - * @return array array([pluginName] => []) - */ - private function getSettingsByType($pluginsSettings, $mode) - { - $byType = array(); - - foreach ($pluginsSettings as $pluginName => $pluginSettings) { - $settings = array(); - - foreach ($pluginSettings->getSettingsForCurrentUser() as $setting) { - if ('admin' === $mode && $setting instanceof SystemSetting) { - $settings[] = $setting; - } elseif ('user' === $mode && $setting instanceof UserSetting) { - $settings[] = $setting; - } - } - - if (!empty($settings)) { - $byType[$pluginName] = array( - 'introduction' => $pluginSettings->getIntroduction(), - 'settings' => $settings - ); - } - } - - return $byType; - } - - public function userPluginSettings() - { - Piwik::checkUserIsNotAnonymous(); - - $settings = $this->getPluginSettings(); - - $vars = array( - 'nonce' => Nonce::getNonce(static::SET_PLUGIN_SETTINGS_NONCE), - 'pluginsSettings' => $this->getSettingsByType($settings, 'user'), - 'firstSuperUserSettingNames' => $this->getFirstSuperUserSettingNames($settings), - 'mode' => 'user' - ); - - return $this->renderTemplate('pluginSettings', $vars); - } - - private function getPluginSettings() - { - $pluginsSettings = SettingsManager::getPluginSettingsForCurrentUser(); - - ksort($pluginsSettings); - - return $pluginsSettings; - } - - /** - * @param \Piwik\Plugin\Settings[] $pluginsSettings - * @return array array([pluginName] => []) - */ - private function getFirstSuperUserSettingNames($pluginsSettings) - { - $names = array(); - foreach ($pluginsSettings as $pluginName => $pluginSettings) { - - foreach ($pluginSettings->getSettingsForCurrentUser() as $setting) { - if ($setting instanceof \Piwik\Settings\SystemSetting) { - $names[$pluginName] = $setting->getName(); - break; - } - } - } - - return $names; - } - - public function setPluginSettings() - { - Piwik::checkUserIsNotAnonymous(); - Json::sendHeaderJSON(); - - $nonce = Common::getRequestVar('nonce', null, 'string'); - - if (!Nonce::verifyNonce(static::SET_PLUGIN_SETTINGS_NONCE, $nonce)) { - return json_encode(array( - 'result' => 'error', - 'message' => $this->translator->translate('General_ExceptionNonceMismatch') - )); - } - - $pluginsSettings = SettingsManager::getPluginSettingsForCurrentUser(); - - try { - - foreach ($pluginsSettings as $pluginName => $pluginSetting) { - foreach ($pluginSetting->getSettingsForCurrentUser() as $setting) { - - $value = $this->findSettingValueFromRequest($pluginName, $setting->getKey()); - - if (!is_null($value)) { - $setting->setValue($value); - } - } - } - - } catch (Exception $e) { - $message = $e->getMessage(); - - if (!empty($setting)) { - $message = $setting->title . ': ' . $message; - } - - $message = html_entity_decode($message, ENT_QUOTES, 'UTF-8'); - return json_encode(array('result' => 'error', 'message' => $message)); - } - - try { - foreach ($pluginsSettings as $pluginSetting) { - $pluginSetting->save(); - } - } catch (Exception $e) { - return json_encode(array( - 'result' => 'error', - 'message' => $this->translator->translate('CoreAdminHome_PluginSettingsSaveFailed')) - ); - } - - Nonce::discardNonce(static::SET_PLUGIN_SETTINGS_NONCE); - return json_encode(array('result' => 'success')); - } - - private function findSettingValueFromRequest($pluginName, $settingKey) - { - $changedPluginSettings = Common::getRequestVar('settings', null, 'array'); - - if (!array_key_exists($pluginName, $changedPluginSettings)) { - return; - } - - $settings = $changedPluginSettings[$pluginName]; - - foreach ($settings as $setting) { - if ($setting['name'] == $settingKey) { - $value = $setting['value']; - - if (is_string($value)) { - return Common::unsanitizeInputValue($value); - } - - return $value; - } - } - } - public function setGeneralSettings() { Piwik::checkUserHasSuperUserAccess(); diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php index 0f9bf60702..6010391fdf 100644 --- a/plugins/CoreAdminHome/CoreAdminHome.php +++ b/plugins/CoreAdminHome/CoreAdminHome.php @@ -11,7 +11,8 @@ namespace Piwik\Plugins\CoreAdminHome; use Piwik\Db; use Piwik\Piwik; use Piwik\ProxyHttp; -use Piwik\Settings\UserSetting; +use Piwik\Settings\Plugin\UserSetting; +use Piwik\Settings\Storage\Backend\PluginSettingsTable; /** * @@ -35,7 +36,7 @@ class CoreAdminHome extends \Piwik\Plugin public function cleanupUser($userLogin) { - UserSetting::removeAllUserSettingsForUser($userLogin); + PluginSettingsTable::removeAllUserSettingsForUser($userLogin); } public function getStylesheetFiles(&$stylesheets) @@ -59,7 +60,6 @@ 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"; $jsFiles[] = "plugins/CoreAdminHome/javascripts/protocolCheck.js"; } diff --git a/plugins/CoreAdminHome/Menu.php b/plugins/CoreAdminHome/Menu.php index b0dd0ee258..9dcb9b9e66 100644 --- a/plugins/CoreAdminHome/Menu.php +++ b/plugins/CoreAdminHome/Menu.php @@ -13,26 +13,9 @@ use Piwik\Menu\MenuAdmin; use Piwik\Menu\MenuTop; use Piwik\Piwik; use Piwik\Plugin; -use Piwik\Settings\Manager as SettingsManager; class Menu extends \Piwik\Plugin\Menu { - public function configureTopMenu(MenuTop $menu) - { - if (Piwik::isUserIsAnonymous()) { - if (Plugin\Manager::getInstance()->isPluginActivated('ScheduledReports')) { - $url = $this->urlForModuleAction('ScheduledReports', 'index'); - } else { - $url = $this->urlForModuleAction('API', 'listAllAPI'); - } - } else { - $url = $this->urlForModuleAction('UsersManager', 'userSettings'); - } - - $menu->registerMenuIcon('CoreAdminHome_Administration', 'icon-configure'); - $menu->addItem('CoreAdminHome_Administration', null, $url, 980, Piwik::translate('CoreAdminHome_Administration')); - } - public function configureAdminMenu(MenuAdmin $menu) { $menu->addDevelopmentItem(null, array(), $order = 40); @@ -43,25 +26,29 @@ class Menu extends \Piwik\Plugin\Menu $menu->addManageItem('General_GeneralSettings', $this->urlForAction('generalSettings'), $order = 6); - - if (SettingsManager::hasSystemPluginsSettingsForCurrentUser()) { - $menu->addManageItem('CoreAdminHome_PluginSettings', - $this->urlForAction('adminPluginSettings'), - $order = 7); - } } if (!Piwik::isUserIsAnonymous()) { $menu->addManageItem('CoreAdminHome_TrackingCode', $this->urlForAction('trackingCodeGenerator'), $order = 25); + } + } - if (SettingsManager::hasUserPluginsSettingsForCurrentUser()) { - $menu->addPersonalItem('CoreAdminHome_PluginSettings', - $this->urlForAction('userPluginSettings'), - $order = 15); + public function configureTopMenu(MenuTop $menu) + { + if (Piwik::isUserIsAnonymous()) { + if (Plugin\Manager::getInstance()->isPluginActivated('ScheduledReports')) { + $url = $this->urlForModuleAction('ScheduledReports', 'index'); + } else { + $url = $this->urlForModuleAction('API', 'listAllAPI'); } + } else { + $url = $this->urlForModuleAction('UsersManager', 'userSettings'); } + + $menu->registerMenuIcon('CoreAdminHome_Administration', 'icon-configure'); + $menu->addItem('CoreAdminHome_Administration', null, $url, 980, Piwik::translate('CoreAdminHome_Administration')); } -} +}
\ No newline at end of file diff --git a/plugins/CoreAdminHome/javascripts/pluginSettings.js b/plugins/CoreAdminHome/javascripts/pluginSettings.js deleted file mode 100644 index d6e49de450..0000000000 --- a/plugins/CoreAdminHome/javascripts/pluginSettings.js +++ /dev/null @@ -1,87 +0,0 @@ -/*! - * Piwik - free/libre analytics platform - * - * @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() - { - $submit.prop('disabled', true); - - var $nonce = $('[name="setpluginsettingsnonce"]'); - var nonceValue = ''; - - if ($nonce) { - nonceValue = $nonce.val(); - } - - var ajaxHandler = new ajaxHelper(); - ajaxHandler.addParams({ - module: 'CoreAdminHome', - action: 'setPluginSettings', - nonce: nonceValue - }, 'GET'); - ajaxHandler.addParams({settings: getSettings()}, 'POST'); - ajaxHandler.redirectOnSuccess(); - ajaxHandler.setLoadingElement(getLoadingElement()); - ajaxHandler.setErrorElement(getErrorElement()); - ajaxHandler.setCompleteCallback(function () { - $submit.prop('disabled', false); - }); - ajaxHandler.send(); - } - - 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:not([multiple])', $pluginSection).serializeArray(); - - // by default, it does not generate an array - var $multiSelects = $('select[multiple]', $pluginSection); - $multiSelects.each(function (index, multiSelect) { - var name = $(multiSelect).attr('name'); - serialized.push({name: name, value: $(multiSelect).val()}); - }); - - // by default, values of unchecked checkboxes are not send - var $uncheckedNodes = $('input[type=checkbox]:not(:checked)', $pluginSection); - $uncheckedNodes.each(function (index, uncheckedNode) { - var name = $(uncheckedNode).attr('name'); - serialized.push({name: name, value: 0}); - }); - - values[pluginName] = serialized; - }); - - return values; - } - - function getErrorElement() - { - return $('#ajaxErrorPluginSettings'); - } - - function getLoadingElement() - { - return $('#ajaxLoadingPluginSettings'); - } - -});
\ No newline at end of file diff --git a/plugins/CoreAdminHome/lang/en.json b/plugins/CoreAdminHome/lang/en.json index 4a87945661..306fe77fd7 100644 --- a/plugins/CoreAdminHome/lang/en.json +++ b/plugins/CoreAdminHome/lang/en.json @@ -71,6 +71,7 @@ "PluginSettingsIntro": "Here you can change the settings for the following 3rd party plugins:", "PluginSettingsValueNotAllowed": "The value for field \"%1$s\" in plugin \"%2$s\" is not allowed", "PluginSettingsSaveFailed": "Failed to save plugin settings", + "PluginSettingsSaveSuccess": "Plugin settings updated.", "SendPluginUpdateCommunication": "Send an email when a plugin update is available", "SendPluginUpdateCommunicationHelp": "An email will be sent to Super Users when there is a new version available for a plugin.", "StableReleases": "If Piwik is a critical part of your business, we recommend you use the latest stable release. If you use the latest beta and you find a bug or have a suggestion, please %1$ssee here%2$s.", diff --git a/plugins/CoreAdminHome/templates/generalSettings.twig b/plugins/CoreAdminHome/templates/generalSettings.twig index e70d537472..34f09d9d13 100644 --- a/plugins/CoreAdminHome/templates/generalSettings.twig +++ b/plugins/CoreAdminHome/templates/generalSettings.twig @@ -284,4 +284,9 @@ </p> {% endif %} + <h2 piwik-enriched-headline>{{ 'CoreAdminHome_SystemPluginSettings'|translate }}</h2> + + <div piwik-plugin-settings mode="admin"></div> + + {% endblock %} diff --git a/plugins/CoreAdminHome/templates/pluginSettings.twig b/plugins/CoreAdminHome/templates/pluginSettings.twig deleted file mode 100644 index b160fe847e..0000000000 --- a/plugins/CoreAdminHome/templates/pluginSettings.twig +++ /dev/null @@ -1,56 +0,0 @@ - -{% extends "admin.twig" %} - -{% set title %} -{% if mode == 'user' -%} - {{ 'CoreAdminHome_PersonalPluginSettings'|translate }} -{%- else -%} - {{ 'CoreAdminHome_SystemPluginSettings'|translate }} -{% endif %} -{% endset %} - -{% block content %} - - {% import 'macros.twig' as piwik %} - {% import 'ajaxMacros.twig' as ajax %} - {% import 'settingsMacros.twig' as settingsMacro %} - - <h2 piwik-enriched-headline>{{ title }}</h2> - - <input type="hidden" name="setpluginsettingsnonce" value="{{ nonce }}"> - - <p> - {{ 'CoreAdminHome_PluginSettingsIntro'|translate }} - {% for pluginName, settings in pluginsSettings %} - <a href="#{{ pluginName|e('html_attr') }}">{{ pluginName }}</a>{% if not loop.last %}, {% endif %} - {% endfor %} - </p> - - {% for pluginName, pluginSettings in pluginsSettings %} - - <h2 id="{{ pluginName|e('html_attr') }}">{{ pluginName }}</h2> - - {% if pluginSettings.introduction %} - <p class="pluginIntroduction"> - {{ pluginSettings.introduction }} - </p> - {% endif %} - - <div id="pluginSettings" data-pluginname="{{ pluginName|e('html_attr') }}"> - - {% for name, setting in pluginSettings.settings %} - {{ settingsMacro.singleSetting(setting, loop.index) }} - {% endfor %} - - </div> - - {% endfor %} - - <hr/> - - {{ ajax.errorDiv('ajaxErrorPluginSettings') }} - {{ ajax.loadingDiv('ajaxLoadingPluginSettings') }} - - <input type="submit" value="{{ 'General_Save'|translate }}" class="pluginsSettingsSubmit submit"/> - -{% endblock %}
\ No newline at end of file diff --git a/plugins/CoreConsole/Commands/GenerateReport.php b/plugins/CoreConsole/Commands/GenerateReport.php index 3f9fda4939..1faa1c9dc6 100644 --- a/plugins/CoreConsole/Commands/GenerateReport.php +++ b/plugins/CoreConsole/Commands/GenerateReport.php @@ -13,7 +13,7 @@ use Piwik\Columns\Dimension; use Piwik\Piwik; use Piwik\Plugin\Manager; use Piwik\Plugin\Report; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Translate; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -80,7 +80,7 @@ class GenerateReport extends GeneratePluginBase { $order = 1; - $reports = new Reports(); + $reports = new ReportsProvider(); foreach ($reports->getAllReports() as $report) { if ($report->getCategoryId() === $category) { @@ -193,7 +193,7 @@ class GenerateReport extends GeneratePluginBase $category = $input->getOption('category'); - $reports = new Reports(); + $reports = new ReportsProvider(); $categories = array(); foreach ($reports->getAllReports() as $report) { @@ -232,7 +232,7 @@ class GenerateReport extends GeneratePluginBase $dimensions = array(); $dimensionNames = array(); - $reports = new Reports(); + $reports = new ReportsProvider(); foreach ($reports->getAllReports() as $report) { $dimension = $report->getDimension(); diff --git a/plugins/CoreConsole/Commands/GenerateSettings.php b/plugins/CoreConsole/Commands/GenerateSettings.php index 314b98e7a9..a5b0c4e077 100644 --- a/plugins/CoreConsole/Commands/GenerateSettings.php +++ b/plugins/CoreConsole/Commands/GenerateSettings.php @@ -20,38 +20,72 @@ class GenerateSettings extends GeneratePluginBase protected function configure() { $this->setName('generate:settings') - ->setDescription('Adds a plugin setting class to an existing plugin') - ->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have settings yet'); + ->setDescription('Adds a SystemSetting, UserSetting or MeasurableSetting class to an existing plugin') + ->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have settings yet') + ->addOption('settingstype', null, InputOption::VALUE_REQUIRED, 'The type of settings you want to create. Should be one of these values: ' . implode(', ', $this->getSettingTypes())); } protected function execute(InputInterface $input, OutputInterface $output) { - $pluginName = $this->getPluginName($input, $output); + $settingsType = $this->getSettingsType($input, $output); + $settingsFilename = $settingsType . '.php'; + + $pluginName = $this->getPluginName($input, $output, $settingsType, $settingsFilename); $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleSettingsPlugin'; $replace = array('ExampleSettingsPlugin' => $pluginName); - $whitelistFiles = array('/Settings.php'); + $whitelistFiles = array('/' . $settingsFilename); $this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles); $this->writeSuccessMessage($output, array( - sprintf('Settings.php for %s generated.', $pluginName), - 'You can now start defining your plugin settings', + sprintf('%s for %s generated.', $settingsFilename, $pluginName), + 'You can now start defining your ' . $settingsType, 'Enjoy!' )); } + private function getSettingTypes() + { + return array('system', 'user', 'measurable'); + } + + private function getSettingsType(InputInterface $input, OutputInterface $output) + { + $availableTypes = $this->getSettingTypes(); + + $validate = function ($type) use ($availableTypes) { + if (empty($type) || !in_array($type, $availableTypes)) { + throw new \InvalidArgumentException('Please enter a valid settings type (' . implode(', ', $availableTypes) . '). '); + } + + return $type; + }; + + $settingsType = $input->getOption('settingstype'); + + if (empty($settingsType)) { + $dialog = $this->getHelperSet()->get('dialog'); + $settingsType = $dialog->askAndValidate($output, 'Please choose the type of settings you want to create (' . implode(', ', $availableTypes) . '): ', $validate, false, null, $availableTypes); + } else { + $validate($settingsType); + } + + return ucfirst($settingsType) . 'Settings'; + } + /** * @param InputInterface $input * @param OutputInterface $output + * @param string $settingsType * @return array * @throws \RuntimeException */ - protected function getPluginName(InputInterface $input, OutputInterface $output) + protected function getPluginName(InputInterface $input, OutputInterface $output, $settingsType, $settingsFile) { - $pluginNames = $this->getPluginNamesHavingNotSpecificFile('Settings.php'); - $invalidName = 'You have to enter the name of an existing plugin which does not already have settings'; + $pluginNames = $this->getPluginNamesHavingNotSpecificFile($settingsFile); + $invalidName = 'You have to enter the name of an existing plugin which does not already have ' . $settingsType; return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName); } diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php index 9e6afdb107..e7b844db39 100644 --- a/plugins/CoreHome/CoreHome.php +++ b/plugins/CoreHome/CoreHome.php @@ -147,6 +147,7 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/ucfirst.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.module.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/attributes.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/ignore-click.js"; @@ -200,6 +201,14 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/quick-access/quick-access.directive.js"; $jsFiles[] = "plugins/CoreHome/angularjs/selector/selector.directive.js"; + + + // we have to load these CorePluginsAdmin files here. If we loaded them in CorePluginsAdmin, + // there would be JS errors as CorePluginsAdmin is loaded first. Meaning it is loaded before + // any angular JS file is loaded etc. + $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js"; + $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.js"; + $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js"; } public function getClientSideTranslationKeys(&$translationKeys) diff --git a/plugins/CoreHome/angularjs/common/directives/attributes.js b/plugins/CoreHome/angularjs/common/directives/attributes.js new file mode 100644 index 0000000000..c8285d9e8e --- /dev/null +++ b/plugins/CoreHome/angularjs/common/directives/attributes.js @@ -0,0 +1,37 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * If the given text or resolved expression matches any text within the element, the matching text will be wrapped + * with a class. + * + * Example: + * <div piwik-autocomplete-matched="'text'">My text</div> ==> <div>My <span class="autocompleteMatched">text</span></div> + * + * <div piwik-autocomplete-matched="searchTerm">{{ name }}</div> + * <input type="text" ng-model="searchTerm"> + */ +(function () { + angular.module('piwikApp.directive').directive('piwikAttributes', piwikAttributes); + + piwikAttributes.$inject = ['$sanitize']; + + function piwikAttributes(piwik, $sanitize) { + + return { + link: function (scope, element, attrs) { + attrs.piwikAttributes = JSON.parse(attrs.piwikAttributes); + + if (angular.isObject(attrs.piwikAttributes)) { + angular.forEach(attrs.piwikAttributes, function (value, key) { + element.attr(key, value); + }); + } + } + }; + } +})(); diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js index 7b999aab08..83a714ceb7 100644 --- a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js +++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js @@ -7,9 +7,9 @@ (function () { angular.module('piwikApp').factory('reportingMenuModel', reportingMenuModelService); - reportingMenuModelService.$inject = ['$filter', '$q', 'piwikApi', 'reportingPagesModel', '$location']; + reportingMenuModelService.$inject = ['$filter', '$q', 'reportingPagesModel', '$location']; - function reportingMenuModelService ($filter, $q, piwikApi, reportingPagesModel, $location) { + function reportingMenuModelService ($filter, $q, reportingPagesModel, $location) { // those sites are going to be displayed var model = { diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js index 4018fcee8c..9683228b4f 100644 --- a/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js +++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js @@ -7,9 +7,9 @@ (function () { angular.module('piwikApp').factory('reportingPageModel', reportingPageModelService); - reportingPageModelService.$inject = ['$filter', 'piwikApi', 'reportingPagesModel', 'reportMetadataModel']; + reportingPageModelService.$inject = ['$filter', 'reportingPagesModel', 'reportMetadataModel']; - function reportingPageModelService ($filter, piwikApi, reportingPagesModel, reportMetadataModel) { + function reportingPageModelService ($filter, reportingPagesModel, reportMetadataModel) { var init = false; // those sites are going to be displayed diff --git a/plugins/CorePluginsAdmin/API.php b/plugins/CorePluginsAdmin/API.php new file mode 100644 index 0000000000..5ebd6dddbb --- /dev/null +++ b/plugins/CorePluginsAdmin/API.php @@ -0,0 +1,109 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CorePluginsAdmin; +use Piwik\Piwik; +use Piwik\Plugin\SettingsProvider; +use Exception; + +/** + * API for plugin CorePluginsAdmin + * + * @method static \Piwik\Plugins\CorePluginsAdmin\API getInstance() + */ +class API extends \Piwik\Plugin\API +{ + /** + * @var SettingsMetadata + */ + private $settingsMetadata; + + /** + * @var SettingsProvider + */ + private $settingsProvider; + + public function __construct(SettingsProvider $settingsProvider, SettingsMetadata $settingsMetadata) + { + $this->settingsProvider = $settingsProvider; + $this->settingsMetadata = $settingsMetadata; + } + + /** + * @ignore + * @param array $settingValues Format: array('PluginName' => array(array('name' => 'SettingName1', 'value' => 'SettingValue1), ..)) + * @throws Exception + */ + public function setSystemSettings($settingValues) + { + Piwik::checkUserHasSuperUserAccess(); + + $pluginsSettings = $this->settingsProvider->getAllSystemSettings(); + + $this->settingsMetadata->setPluginSettings($pluginsSettings, $settingValues); + + try { + foreach ($pluginsSettings as $pluginSetting) { + $pluginSetting->save(); + } + } catch (Exception $e) { + throw new Exception(Piwik::translate('CoreAdminHome_PluginSettingsSaveFailed')); + } + } + + /** + * @ignore + * @param array $settingValues Format: array('PluginName' => array(array('name' => 'SettingName1', 'value' => 'SettingValue1), ..)) + * @throws Exception + */ + public function setUserSettings($settingValues) + { + Piwik::checkUserIsNotAnonymous(); + + $pluginsSettings = $this->settingsProvider->getAllUserSettings(); + + $this->settingsMetadata->setPluginSettings($pluginsSettings, $settingValues); + + try { + foreach ($pluginsSettings as $pluginSetting) { + $pluginSetting->save(); + } + } catch (Exception $e) { + throw new Exception(Piwik::translate('CoreAdminHome_PluginSettingsSaveFailed')); + } + } + + /** + * @ignore + * @return array + * @throws \Piwik\NoAccessException + */ + public function getSystemSettings() + { + Piwik::checkUserHasSuperUserAccess(); + + $systemSettings = $this->settingsProvider->getAllSystemSettings(); + + return $this->settingsMetadata->formatSettings($systemSettings); + } + + /** + * @ignore + * @return array + * @throws \Piwik\NoAccessException + */ + public function getUserSettings() + { + Piwik::checkUserIsNotAnonymous(); + + $userSettings = $this->settingsProvider->getAllUserSettings(); + + return $this->settingsMetadata->formatSettings($userSettings); + } + +} diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php index f4e171c158..4af63c245d 100644 --- a/plugins/CorePluginsAdmin/Controller.php +++ b/plugins/CorePluginsAdmin/Controller.php @@ -18,7 +18,6 @@ use Piwik\Nonce; use Piwik\Notification; use Piwik\Piwik; use Piwik\Plugin; -use Piwik\Settings\Manager as SettingsManager; use Piwik\Translation\Translator; use Piwik\Url; use Piwik\Version; @@ -40,9 +39,15 @@ class Controller extends Plugin\ControllerAdmin */ private $translator; - public function __construct(Translator $translator) + /** + * @var Plugin\SettingsProvider + */ + private $settingsProvider; + + public function __construct(Translator $translator, Plugin\SettingsProvider $settingsProvider) { $this->translator = $translator; + $this->settingsProvider = $settingsProvider; parent::__construct(); } @@ -246,7 +251,7 @@ class Controller extends Plugin\ControllerAdmin $view->otherUsersCount = count($users) - 1; $view->themeEnabled = \Piwik\Plugin\Manager::getInstance()->getThemeEnabled()->getPluginName(); - $view->pluginNamesHavingSettings = $this->getPluginNamesHavingSettingsForCurrentUser(); + $view->pluginNamesHavingSettings = array_keys($this->settingsProvider->getAllSystemSettings()); $view->isMarketplaceEnabled = CorePluginsAdmin::isMarketplaceEnabled(); $view->isPluginsAdminEnabled = CorePluginsAdmin::isPluginsAdminEnabled(); @@ -427,9 +432,9 @@ class Controller extends Plugin\ControllerAdmin } $message = $this->translator->translate('CorePluginsAdmin_SuccessfullyActicated', array($pluginName)); - if (SettingsManager::hasSystemPluginSettingsForCurrentUser($pluginName)) { + if ($this->settingsProvider->getSystemSettings($pluginName)) { $target = sprintf('<a href="index.php%s#%s">', - Url::getCurrentQueryStringWithParametersModified(array('module' => 'CoreAdminHome', 'action' => 'adminPluginSettings')), + Url::getCurrentQueryStringWithParametersModified(array('module' => 'CoreAdminHome', 'action' => 'generalSettings')), $pluginName); $message .= ' ' . $this->translator->translate('CorePluginsAdmin_ChangeSettingsPossible', array($target, '</a>')); } @@ -502,11 +507,6 @@ class Controller extends Plugin\ControllerAdmin } } - private function getPluginNamesHavingSettingsForCurrentUser() - { - return SettingsManager::getPluginNamesHavingSystemSettings(); - } - private function tryToRepairPiwik() { // in case any opcaches etc were not cleared after an update for instance. Might prevent from getting the diff --git a/plugins/CorePluginsAdmin/CorePluginsAdmin.php b/plugins/CorePluginsAdmin/CorePluginsAdmin.php index 6e03b3b46a..dc550eead3 100644 --- a/plugins/CorePluginsAdmin/CorePluginsAdmin.php +++ b/plugins/CorePluginsAdmin/CorePluginsAdmin.php @@ -30,6 +30,7 @@ class CorePluginsAdmin extends \Piwik\Plugin $stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/marketplace.less"; $stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/plugins_admin.less"; $stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/plugin-details.less"; + $stylesheets[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less"; } public static function isMarketplaceEnabled() @@ -55,6 +56,10 @@ class CorePluginsAdmin extends \Piwik\Plugin public function getClientSideTranslationKeys(&$translations) { $translations[] = 'CorePluginsAdmin_NoZipFileSelected'; + $translations[] = 'CorePluginsAdmin_NoPluginSettings'; + $translations[] = 'CoreAdminHome_PluginSettingsIntro'; + $translations[] = 'CoreAdminHome_PluginSettingsSaveSuccess'; + $translations[] = 'General_Save'; } } diff --git a/plugins/CorePluginsAdmin/Menu.php b/plugins/CorePluginsAdmin/Menu.php index fe3be3d841..8095870ac8 100644 --- a/plugins/CorePluginsAdmin/Menu.php +++ b/plugins/CorePluginsAdmin/Menu.php @@ -41,8 +41,8 @@ class Menu extends \Piwik\Plugin\Menu if ($hasSuperUserAcess) { $menu->addManageItem(Piwik::translate('General_Plugins') . $pluginsUpdateMessage, - $this->urlForAction('plugins', array('activated' => '')), - $order = 4); + $this->urlForAction('plugins', array('activated' => '')), + $order = 4); } if ($this->isAllowedToSeeMarketPlace()) { @@ -56,8 +56,6 @@ class Menu extends \Piwik\Plugin\Menu { $isAnonymous = Piwik::isUserIsAnonymous(); $isMarketplaceEnabled = CorePluginsAdmin::isMarketplaceEnabled(); - return $isMarketplaceEnabled && !$isAnonymous; } - } diff --git a/plugins/CorePluginsAdmin/SettingsMetadata.php b/plugins/CorePluginsAdmin/SettingsMetadata.php new file mode 100644 index 0000000000..fa62d12b11 --- /dev/null +++ b/plugins/CorePluginsAdmin/SettingsMetadata.php @@ -0,0 +1,127 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CorePluginsAdmin; + +use Piwik\Common; +use Piwik\Piwik; +use Piwik\Settings\Setting; +use Piwik\Settings\Settings; +use Exception; + +class SettingsMetadata +{ + + /** + * @param Settings[] $settingsInstances + * @param array $settingValues array('pluginName' => array('settingName' => 'settingValue')) + * @throws Exception; + */ + public function setPluginSettings($settingsInstances, $settingValues) + { + try { + foreach ($settingsInstances as $pluginName => $pluginSetting) { + foreach ($pluginSetting->getSettingsWritableByCurrentUser() as $setting) { + + $value = $this->findSettingValueFromRequest($settingValues, $pluginName, $setting->getName()); + + if (isset($value)) { + $setting->setValue($value); + } + } + } + + } catch (Exception $e) { + $message = $e->getMessage(); + + if (!empty($setting)) { + $title = Piwik::translate(strip_tags($setting->configureField()->title)); + throw new Exception($title . ': ' . $message); + } + } + } + + private function findSettingValueFromRequest($settingValues, $pluginName, $settingName) + { + if (!array_key_exists($pluginName, $settingValues)) { + return; + } + + foreach ($settingValues[$pluginName] as $setting) { + if ($setting['name'] === $settingName) { + $value = null; + if (array_key_exists('value', $setting)) { + $value = $setting['value']; + } + + if (is_string($value)) { + return Common::unsanitizeInputValue($value); + } + + return $value; + } + } + } + + + /** + * @param Settings[] $allSettings A list of Settings instead by pluginname + * @return array + */ + public function formatSettings($allSettings) + { + $metadata = array(); + foreach ($allSettings as $pluginName => $settings) { + $writableSettings = $settings->getSettingsWritableByCurrentUser(); + + if (empty($writableSettings)) { + continue; + } + + $plugin = array( + 'pluginName' => $pluginName, + 'settings' => array() + ); + + foreach ($writableSettings as $writableSetting) { + $plugin['settings'][] = $this->formatMetadata($writableSetting); + } + + $metadata[] = $plugin; + } + + return $metadata; + } + + private function formatMetadata(Setting $setting) + { + $config = $setting->configureField(); + + $availableValues = $config->availableValues; + + if (is_array($availableValues)) { + $availableValues = (object) $availableValues; + } + + return array( + 'name' => $setting->getName(), + 'title' => $config->title, + 'value' => $setting->getValue(), + 'defaultValue' => $setting->getDefaultValue(), + 'type' => $setting->getType(), + 'uiControl' => $config->uiControl, + 'uiControlAttributes' => $config->uiControlAttributes, + 'availableValues' => $availableValues, + 'description' => $config->description, + 'inlineHelp' => $config->inlineHelp, + 'introduction' => $config->introduction, + 'condition' => $config->condition, + ); + } + +}
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html new file mode 100644 index 0000000000..3a3cf49605 --- /dev/null +++ b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html @@ -0,0 +1,89 @@ +<div class="form-group" ng-show="formField.showField"> + <p ng-if="formField.introduction" class="settingIntroduction">{{ formField.introduction }}</p> + + <label ng-if="formField.uiControl != 'checkbox'" ng-bind-html="formField.title"></label> + + <div class="form-help" ng-show="formField.inlineHelp"> + <span ng-bind-html="formField.inlineHelp"></span> + + <span ng-show="formField.defaultValue && formField.uiControl != 'checkbox' && formField.uiControl != 'radio'"> + <br /> + {{ 'General_Default'|translate }}: + <span>{{formField.defaultValue|limitTo:50}}</span> + </span> + </div> + + <select ng-if="formField.uiControl == 'select'" + name="{{ formField.name }}" + ng-model="formField.value" + ng-options="t.key as t.value group by t.group for t in formField.availableValues" + piwik-attributes="{{formField.uiControlAttributes}}"> + </select> + + <select ng-if="formField.uiControl == 'multiselect'" + multiple + name="{{ formField.name }}" + ng-model="formField.value" + ng-options="t.key as t.value for t in formField.availableValues" + piwik-attributes="{{formField.uiControlAttributes}}"> + </select> + + <textarea ng-if="formField.uiControl == 'textarea' && formField.type !== 'array'" + name="{{ formField.name }}" + piwik-attributes="{{formField.uiControlAttributes}}" + ng-model="formField.value" + ></textarea> + + <textarea ng-if="formField.uiControl == 'textarea' && formField.type === 'array'" + name="{{ formField.name }}" + ng-list=" " ng-trim="false" + piwik-attributes="{{formField.uiControlAttributes}}" + ng-model="formField.value" + ></textarea> + + <label ng-if="formField.uiControl == 'radio'" + ng-repeat="(key,value) in formField.availableValues" + class="radio"> + <input ng-model="formField.value" + ng-value="key" + type="radio" + name="{{ formField.name }}" + piwik-attributes="{{formField.uiControlAttributes}}" + > + {{ value }} + <span ng-show="formField.description" class='form-description'>{{ formField.description }}</span> + </label> + + <label ng-if="formField.uiControl == 'checkbox'" class="checkbox"> + <input ng-model="formField.value" + piwik-attributes="{{formField.uiControlAttributes}}" + ng-value="1" + type="checkbox" + name="{{ formField.name }}"> + + <span ng-bind-html="formField.title"></span> + + <span ng-show="formField.description" class='form-description'>{{ formField.description }}</span> + </label> + + <input ng-if="(formField.uiControl == 'text' || formField.uiControl == 'password') && formField.type !== 'array'" + class="control_{{ formField.uiControl }}" + type="{{ formField.uiControl }}" + name="{{ formField.name }}" + ng-model="formField.value" + piwik-attributes="{{formField.uiControlAttributes}}" + > + + <input ng-if="(formField.uiControl == 'text' || formField.uiControl == 'password') && formField.type === 'array'" + class="control_{{ formField.uiControl }}" + type="{{ formField.uiControl }}" + name="{{ formField.name }}" + ng-list + ng-model="formField.value" + piwik-attributes="{{formField.uiControlAttributes}}" + > + + <span ng-show="formField.uiControl != 'checkbox' && formField.uiControl != 'radio'" + class='form-description'>{{ formField.description }}</span> + +</div>
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js new file mode 100644 index 0000000000..b46067bf9d --- /dev/null +++ b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js @@ -0,0 +1,128 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Usage: + * <div piwik-form-field="{...}"> + */ +(function () { + angular.module('piwikApp').directive('piwikFormField', piwikFormField); + + piwikFormField.$inject = ['piwik']; + + function piwikFormField(piwik){ + + return { + restrict: 'A', + scope: { + piwikFormField: '=', + allSettings: '=' + }, + templateUrl: 'plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html?cb=' + piwik.cacheBuster, + compile: function (element, attrs) { + + function evaluateConditionalExpression(scope, field) + { + if (!field.condition) { + return; + } + + var values = {}; + angular.forEach(scope.allSettings, function (setting) { + if (setting.value === '0') { + values[setting.name] = 0; + } else { + values[setting.name] = setting.value; + } + }); + + field.showField = scope.$eval(field.condition, values); + } + + function hasUiControl(field, uiControlType) + { + return field.uiControl === uiControlType; + } + + function isSelectControl(field) + { + return hasUiControl(field, 'select') || hasUiControl(field, 'multiselect'); + } + + function hasGroupedValues(availableValues) + { + if (!angular.isObject(availableValues) + || angular.isArray(availableValues)) { + return false; + } + + var key; + for (key in availableValues) { + if (Object.prototype.hasOwnProperty.call(availableValues, key)) { + if (angular.isObject(availableValues[key])) { + return true; + } else { + return false; + } + } + } + + return false; + } + + return function (scope, element, attrs) { + var field = scope.piwikFormField; + + if (angular.isArray(field.defaultValue)) { + field.defaultValue = field.defaultValue.join(','); + } + + if (isSelectControl(field) && field.availableValues) { + var availableValues = field.availableValues; + + if (!hasGroupedValues(availableValues)) { + availableValues = {'': availableValues}; + } + + var flatValues = []; + angular.forEach(availableValues, function (values, group) { + angular.forEach(values, function (value, key) { + + if (field.type === 'integer' && angular.isString(key)) { + key = parseInt(key, 10); + } + + flatValues.push({group: group, key: key, value: value}); + }); + }); + + field.availableValues = flatValues; + } + + field.showField = true; + + if (field.condition && scope.allSettings) { + evaluateConditionalExpression(scope, field); + + for (var key in scope.allSettings) { + if(scope.allSettings.hasOwnProperty(key)) { + scope.$watchCollection('allSettings[' + key + '].value', function (val, oldVal) { + if (val !== oldVal) { + evaluateConditionalExpression(scope, field); + } + }); + } + } + + } + + scope.formField = field; + }; + } + }; + } +})();
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js new file mode 100644 index 0000000000..bbe3c02eb0 --- /dev/null +++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js @@ -0,0 +1,72 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').controller('PluginSettingsController', PluginSettingsController); + + PluginSettingsController.$inject = ['$scope', 'piwikApi']; + + function PluginSettingsController($scope, piwikApi) { + // remember to keep controller very simple. Create a service/factory (model) if needed + + var self = this; + + this.isLoading = true; + + var apiMethod = 'CorePluginsAdmin.getUserSettings'; + + if ($scope.mode === 'admin') { + apiMethod = 'CorePluginsAdmin.getSystemSettings'; + } + + piwikApi.fetch({method: apiMethod}).then(function (settings) { + self.isLoading = false; + self.settingsPerPlugin = settings; + }, function () { + self.isLoading = false; + }); + + this.save = function () { + var apiMethod = 'CorePluginsAdmin.setUserSettings'; + if ($scope.mode === 'admin') { + apiMethod = 'CorePluginsAdmin.setSystemSettings'; + } + + this.isLoading = true; + + var values = {}; + angular.forEach(this.settingsPerPlugin, function (settings) { + if (!values[settings.pluginName]) { + values[settings.pluginName] = []; + } + + angular.forEach(settings.settings, function (setting) { + var value = setting.value; + if (value === false) { + value = '0'; + } else if (value === true) { + value = '1'; + } + values[settings.pluginName].push({ + name: setting.name, + value: value + }); + }); + }); + + piwikApi.post({method: apiMethod}, {settingValues: values}).then(function (success) { + self.isLoading = false; + + var UI = require('piwik/UI'); + var notification = new UI.Notification(); + notification.show(_pk_translate('CoreAdminHome_PluginSettingsSaveSuccess'), {context: 'success'}); + + }, function () { + self.isLoading = false; + }); + }; + } +})();
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html new file mode 100644 index 0000000000..3748c3d458 --- /dev/null +++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html @@ -0,0 +1,31 @@ +<div class="pluginSettings"> + + <p> + {{ 'CoreAdminHome_PluginSettingsIntro'|translate }} + + <span ng-repeat="settings in pluginSettings.settingsPerPlugin"> + <a href="#{{ settings.pluginName }}">{{ settings.pluginName }}</a><span ng-hide="$last">, </span> + </span> + </p> + + <p ng-if="!pluginSettings.isLoading && pluginSettings.settingsPerPlugin.length === 0"> + {{ 'CorePluginsAdmin_NoPluginSettings'|translate }} + </p> + + <div piwik-activity-indicator loading="pluginSettings.isLoading"></div> + + <div ng-repeat="settings in pluginSettings.settingsPerPlugin" + id="pluginSettings"> + + <h2 id="{{ settings.pluginName }}" class="secondary">{{ settings.pluginName }}</h2> + + <div ng-repeat="setting in settings.settings"> + <div piwik-form-field="setting" all-settings="settings.settings"></div> + </div> + </div> + + <input type="button" ng-click="pluginSettings.save()" + value="{{ 'General_Save'|translate }}" + class="pluginsSettingsSubmit submit"/> + +</div>
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.js b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.js new file mode 100644 index 0000000000..927868325f --- /dev/null +++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.js @@ -0,0 +1,44 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Usage: + * <div piwik-plugin-settings> + */ +(function () { + angular.module('piwikApp').directive('piwikPluginSettings', piwikPluginSettings); + + piwikPluginSettings.$inject = ['piwik']; + + function piwikPluginSettings(piwik){ + var defaults = { + mode: '' + }; + + return { + restrict: 'A', + scope: { + mode: '@' + }, + templateUrl: 'plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html?cb=' + piwik.cacheBuster, + controller: 'PluginSettingsController', + controllerAs: 'pluginSettings', + compile: function (element, attrs) { + + for (var index in defaults) { + if (defaults.hasOwnProperty(index) && attrs[index] === undefined) { + attrs[index] = defaults[index]; + } + } + + return function (scope, element, attrs) { + + }; + } + }; + } +})();
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less new file mode 100644 index 0000000000..cd747097ed --- /dev/null +++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less @@ -0,0 +1,6 @@ +.pluginSettings { + textarea { + width: 376px; + height: 250px; + } +}
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/lang/en.json b/plugins/CorePluginsAdmin/lang/en.json index d3b5a1d685..10aa1f8f00 100644 --- a/plugins/CorePluginsAdmin/lang/en.json +++ b/plugins/CorePluginsAdmin/lang/en.json @@ -51,6 +51,7 @@ "MissingRequirementsPleaseInstallNotice": "Please install %1$s %2$s as it is required by %3$s.", "NewVersion": "new version", "NoPluginsFound": "No plugins found", + "NoPluginSettings": "No plugin settings that can be configured", "NotAllowedToBrowseMarketplacePlugins": "You can browse the list of plugins that can be installed to customize or extend your Piwik platform. Please contact your administrator if you need any of these installed.", "NotAllowedToBrowseMarketplaceThemes": "You can browse the list of themes that can be installed to customize the appearance of the Piwik platform. Please contact your administrator to get any of these installed for you.", "NoThemesFound": "No themes found", diff --git a/plugins/CorePluginsAdmin/templates/macros.twig b/plugins/CorePluginsAdmin/templates/macros.twig index f0b66123e5..c64500c518 100644 --- a/plugins/CorePluginsAdmin/templates/macros.twig +++ b/plugins/CorePluginsAdmin/templates/macros.twig @@ -166,7 +166,7 @@ {% if name in pluginNamesHavingSettings %} <br /><br /> - <a href="{{ linkTo({'module':'CoreAdminHome', 'action': 'adminPluginSettings'}) }}#{{ name|e('html_attr') }}" class="settingsLink">{{ 'General_Settings'|translate }}</a> + <a href="{{ linkTo({'module':'CoreAdminHome', 'action': 'generalSettings'}) }}#{{ name|e('html_attr') }}" class="settingsLink">{{ 'General_Settings'|translate }}</a> {% endif %} </td> <td class="desc"> diff --git a/plugins/CustomAlerts b/plugins/CustomAlerts -Subproject dd33d73ee4117d3c3e7f342e135e8fcd1da8dba +Subproject 1282ff93f053af06d84661af2b6e4ce5e8ffbe0 diff --git a/plugins/CustomDimensions b/plugins/CustomDimensions -Subproject d18b214313b8fde350f7092659af3e92de5a11a +Subproject 072bd2b3d901c0dc45b43683dd3385686d61900 diff --git a/plugins/DBStats/Reports/GetMetricDataSummary.php b/plugins/DBStats/Reports/GetMetricDataSummary.php index 94735c4e03..34b773531d 100644 --- a/plugins/DBStats/Reports/GetMetricDataSummary.php +++ b/plugins/DBStats/Reports/GetMetricDataSummary.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; /** * Shows a datatable that displays the amount of space each numeric archive table @@ -35,7 +35,7 @@ class GetMetricDataSummary extends Base public function getRelatedReports() { return array( - Reports::factory('DBStats', 'getMetricDataSummaryByYear'), + ReportsProvider::factory('DBStats', 'getMetricDataSummaryByYear'), ); } diff --git a/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php b/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php index 81b89d5d99..55bc6f90e2 100644 --- a/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php +++ b/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; /** * Shows a datatable that displays the amount of space each numeric archive table @@ -36,7 +36,7 @@ class GetMetricDataSummaryByYear extends Base public function getRelatedReports() { return array( - Reports::factory('DBStats', 'getMetricDataSummary'), + ReportsProvider::factory('DBStats', 'getMetricDataSummary'), ); } diff --git a/plugins/DBStats/Reports/GetReportDataSummary.php b/plugins/DBStats/Reports/GetReportDataSummary.php index 58c1b065c5..781b3988b9 100644 --- a/plugins/DBStats/Reports/GetReportDataSummary.php +++ b/plugins/DBStats/Reports/GetReportDataSummary.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; /** * Shows a datatable that displays the amount of space each blob archive table @@ -35,7 +35,7 @@ class GetReportDataSummary extends Base public function getRelatedReports() { return array( - Reports::factory('DBStats', 'getReportDataSummaryByYear'), + ReportsProvider::factory('DBStats', 'getReportDataSummaryByYear'), ); } } diff --git a/plugins/DBStats/Reports/GetReportDataSummaryByYear.php b/plugins/DBStats/Reports/GetReportDataSummaryByYear.php index 7ff78a638b..228d4281b0 100644 --- a/plugins/DBStats/Reports/GetReportDataSummaryByYear.php +++ b/plugins/DBStats/Reports/GetReportDataSummaryByYear.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; /** * Shows a datatable that displays the amount of space each blob archive table @@ -36,7 +36,7 @@ class GetReportDataSummaryByYear extends Base public function getRelatedReports() { return array( - Reports::factory('DBStats', 'getReportDataSummary'), + ReportsProvider::factory('DBStats', 'getReportDataSummary'), ); } diff --git a/plugins/DevicesDetection/Reports/GetBrowserVersions.php b/plugins/DevicesDetection/Reports/GetBrowserVersions.php index 66c7f91f55..0c71c602f2 100644 --- a/plugins/DevicesDetection/Reports/GetBrowserVersions.php +++ b/plugins/DevicesDetection/Reports/GetBrowserVersions.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\DevicesDetection\Columns\BrowserVersion; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetBrowserVersions extends Base { @@ -36,7 +36,7 @@ class GetBrowserVersions extends Base public function getRelatedReports() { return array( - Reports::factory('DevicesDetection', 'getBrowsers'), + ReportsProvider::factory('DevicesDetection', 'getBrowsers'), ); } } diff --git a/plugins/DevicesDetection/Reports/GetBrowsers.php b/plugins/DevicesDetection/Reports/GetBrowsers.php index 5c39a275ee..b1a2f936d0 100644 --- a/plugins/DevicesDetection/Reports/GetBrowsers.php +++ b/plugins/DevicesDetection/Reports/GetBrowsers.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\DevicesDetection\Columns\BrowserName; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetBrowsers extends Base { @@ -37,7 +37,7 @@ class GetBrowsers extends Base public function getRelatedReports() { return array( - Reports::factory('DevicesDetection', 'getBrowserVersions'), + ReportsProvider::factory('DevicesDetection', 'getBrowserVersions'), ); } } diff --git a/plugins/DevicesDetection/Reports/GetOsFamilies.php b/plugins/DevicesDetection/Reports/GetOsFamilies.php index a283a83f7b..ef0d056b9a 100644 --- a/plugins/DevicesDetection/Reports/GetOsFamilies.php +++ b/plugins/DevicesDetection/Reports/GetOsFamilies.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\DevicesDetection\Columns\Os; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetOsFamilies extends Base { @@ -37,7 +37,7 @@ class GetOsFamilies extends Base public function getRelatedReports() { return array( - Reports::factory('DevicesDetection', 'getOsVersions'), + ReportsProvider::factory('DevicesDetection', 'getOsVersions'), ); } diff --git a/plugins/DevicesDetection/Reports/GetOsVersions.php b/plugins/DevicesDetection/Reports/GetOsVersions.php index b330054c6f..c100b2e39f 100644 --- a/plugins/DevicesDetection/Reports/GetOsVersions.php +++ b/plugins/DevicesDetection/Reports/GetOsVersions.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\DevicesDetection\Columns\OsVersion; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetOsVersions extends Base { @@ -37,7 +37,7 @@ class GetOsVersions extends Base public function getRelatedReports() { return array( - Reports::factory('DevicesDetection', 'getOsFamilies'), + ReportsProvider::factory('DevicesDetection', 'getOsFamilies'), ); } } diff --git a/plugins/Diagnostics/ConfigReader.php b/plugins/Diagnostics/ConfigReader.php index 7d0c90938d..d1f57be422 100644 --- a/plugins/Diagnostics/ConfigReader.php +++ b/plugins/Diagnostics/ConfigReader.php @@ -136,12 +136,12 @@ class ConfigReader * for already existing configured config values that overwrite a plugin system setting. * * @param array $configValues - * @param \Piwik\Plugin\Settings[] $pluginSettings + * @param \Piwik\Settings\Plugin\SystemSettings[] $systemSettings * @return array */ - public function addConfigValuesFromPluginSettings($configValues, $pluginSettings) + public function addConfigValuesFromSystemSettings($configValues, $systemSettings) { - foreach ($pluginSettings as $pluginSetting) { + foreach ($systemSettings as $pluginSetting) { $pluginName = $pluginSetting->getPluginName(); if (empty($pluginName)) { @@ -150,36 +150,34 @@ class ConfigReader $configs[$pluginName] = array(); - foreach ($pluginSetting->getSettings() as $setting) { - if ($setting instanceof PiwikSettings\SystemSetting && $setting->isReadableByCurrentUser()) { - $name = $setting->getName(); + foreach ($pluginSetting->getSettingsWritableByCurrentUser() as $setting) { + $name = $setting->getName(); + $config = $setting->configureField(); - $description = ''; - if (!empty($setting->description)) { - $description .= $setting->description . ' '; - } + $description = ''; + if (!empty($config->description)) { + $description .= $config->description . ' '; + } - if (!empty($setting->inlineHelp)) { - $description .= $setting->inlineHelp; - } + if (!empty($config->inlineHelp)) { + $description .= $config->inlineHelp; + } + + if (isset($configValues[$pluginName][$name])) { + $configValues[$pluginName][$name]['defaultValue'] = $setting->getDefaultValue(); + $configValues[$pluginName][$name]['description'] = trim($description); - if (isset($configValues[$pluginName][$name])) { - $configValues[$pluginName][$name]['defaultValue'] = $setting->defaultValue; - $configValues[$pluginName][$name]['description'] = trim($description); - - if ($setting->uiControlType === PluginSettings::CONTROL_PASSWORD) { - $value = $configValues[$pluginName][$name]['value']; - $configValues[$pluginName][$name]['value'] = $this->getMaskedPassword(); - } - } else { - $defaultValue = $setting->getValue(); - $configValues[$pluginName][$name] = array( - 'value' => null, - 'description' => trim($description), - 'isCustomValue' => false, - 'defaultValue' => $defaultValue - ); + if ($config->uiControl === PiwikSettings\FieldConfig::UI_CONTROL_PASSWORD) { + $configValues[$pluginName][$name]['value'] = $this->getMaskedPassword(); } + } else { + $defaultValue = $setting->getValue(); + $configValues[$pluginName][$name] = array( + 'value' => null, + 'description' => trim($description), + 'isCustomValue' => false, + 'defaultValue' => $defaultValue + ); } } diff --git a/plugins/Diagnostics/Controller.php b/plugins/Diagnostics/Controller.php index 7d2341a195..a350ff47a3 100644 --- a/plugins/Diagnostics/Controller.php +++ b/plugins/Diagnostics/Controller.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\Diagnostics; use Piwik\Config; use Piwik\Piwik; +use Piwik\Plugin\SettingsProvider; use Piwik\View; use Piwik\Settings; @@ -30,10 +31,11 @@ class Controller extends \Piwik\Plugin\ControllerAdmin { Piwik::checkUserHasSuperUserAccess(); - $allSettings = Settings\Manager::getAllPluginSettings(); + $settings = new SettingsProvider(\Piwik\Plugin\Manager::getInstance()); + $allSettings = $settings->getAllSystemSettings(); $configValues = $this->configReader->getConfigValuesFromFiles(); - $configValues = $this->configReader->addConfigValuesFromPluginSettings($configValues, $allSettings); + $configValues = $this->configReader->addConfigValuesFromSystemSettings($configValues, $allSettings); $configValues = $this->sortConfigValues($configValues); return $this->renderTemplate('configfile', array( diff --git a/plugins/Diagnostics/Test/Integration/ConfigReaderTest.php b/plugins/Diagnostics/Test/Integration/ConfigReaderTest.php index bb05cb61e7..6634c4a38a 100644 --- a/plugins/Diagnostics/Test/Integration/ConfigReaderTest.php +++ b/plugins/Diagnostics/Test/Integration/ConfigReaderTest.php @@ -10,9 +10,12 @@ namespace Piwik\Plugins\Diagnostics\Test\Integration\Commands; use Piwik\Application\Kernel\GlobalSettingsProvider; use Piwik\Ini\IniReader; +use Piwik\Piwik; use Piwik\Plugins\Diagnostics\ConfigReader; -use Piwik\Plugins\ExampleSettingsPlugin\Settings; +use Piwik\Plugins\ExampleSettingsPlugin\SystemSettings; +use Piwik\Settings\FieldConfig; use Piwik\Tests\Fixtures\OneVisitorTwoVisits; +use Piwik\Tests\Framework\Mock\FakeAccess; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; /** @@ -29,8 +32,12 @@ class ConfigReaderTest extends IntegrationTestCase public function setUp() { + parent::setUp(); + $settings = new GlobalSettingsProvider($this->configPath('global.ini.php'), $this->configPath('config.ini.php'), $this->configPath('common.config.ini.php')); $this->configReader = new ConfigReader($settings, new IniReader()); + + FakeAccess::clearAccess($superUser = true); } public function test_getConfigValuesFromFiles() @@ -160,9 +167,9 @@ with multiple lines', public function test_addConfigValuesFromPluginSettings() { - $settings = new Settings(); + $settings = new SystemSettings(); - $configValues = $this->configReader->addConfigValuesFromPluginSettings(array(), array($settings)); + $configValues = $this->configReader->addConfigValuesFromSystemSettings(array(), array($settings)); $expected = array ( 'ExampleSettingsPlugin' => @@ -208,7 +215,7 @@ Another line', public function test_addConfigValuesFromPluginSettings_shouldAddDescriptionAndDefaultValueForExistingConfigValues() { - $settings = new Settings(); + $settings = new SystemSettings(); $existing = array( 'ExampleSettingsPlugin' => @@ -223,7 +230,7 @@ Another line', ) ); - $configValues = $this->configReader->addConfigValuesFromPluginSettings($existing, array($settings)); + $configValues = $this->configReader->addConfigValuesFromSystemSettings($existing, array($settings)); $this->assertSame('Choose the metric that should be displayed in the browser tab', $configValues['ExampleSettingsPlugin']['metric']['description']); $this->assertSame('nb_visits', $configValues['ExampleSettingsPlugin']['metric']['defaultValue']); @@ -231,8 +238,8 @@ Another line', public function test_addConfigValuesFromPluginSettings_shouldMaskValueIfTypeIsPassword() { - $settings = new Settings(); - $settings->metric->uiControlType = Settings::CONTROL_PASSWORD; + $settings = new SystemSettings(); + $settings->metric->configureField()->uiControl = FieldConfig::UI_CONTROL_PASSWORD; $existing = array( 'ExampleSettingsPlugin' => @@ -247,11 +254,18 @@ Another line', ) ); - $configValues = $this->configReader->addConfigValuesFromPluginSettings($existing, array($settings)); + $configValues = $this->configReader->addConfigValuesFromSystemSettings($existing, array($settings)); $this->assertSame('******', $configValues['ExampleSettingsPlugin']['metric']['value']); } + public function provideContainerConfig() + { + return array( + 'Piwik\Access' => new FakeAccess(), + ); + } + private function configPath($file) { return PIWIK_INCLUDE_PATH . '/tests/resources/Config/' . $file; diff --git a/plugins/Events/Events.php b/plugins/Events/Events.php index 15c49e87f1..8ae0f5cb2e 100644 --- a/plugins/Events/Events.php +++ b/plugins/Events/Events.php @@ -13,7 +13,7 @@ use Piwik\DataTable; use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugin\ViewDataTable; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable\AllColumns; class Events extends \Piwik\Plugin @@ -180,7 +180,7 @@ class Events extends \Piwik\Plugin $this->addRelatedReports($view, $secondaryDimension); $this->addTooltipEventValue($view); - $subtableReport = Reports::factory('Events', $view->config->subtable_controller_action); + $subtableReport = ReportsProvider::factory('Events', $view->config->subtable_controller_action); $view->config->pivot_by_dimension = $subtableReport->getDimension()->getId(); $view->config->pivot_by_column = 'nb_events'; } diff --git a/plugins/ExampleSettingsPlugin/MeasurableSettings.php b/plugins/ExampleSettingsPlugin/MeasurableSettings.php new file mode 100644 index 0000000000..be1879750b --- /dev/null +++ b/plugins/ExampleSettingsPlugin/MeasurableSettings.php @@ -0,0 +1,65 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\ExampleSettingsPlugin; + +use Piwik\Plugins\MobileAppMeasurable\Type as MobileAppType; +use Piwik\Settings\Setting; +use Piwik\Settings\FieldConfig; + +/** + * Defines Settings for ExampleSettingsPlugin. + * + * Usage like this: + * // require Piwik\Plugin\SettingsProvider via Dependency Injection eg in constructor of your class + * $settings = $settingsProvider->getMeasurableSettings('ExampleSettingsPlugin', $idSite); + * $settings->appId->getValue(); + * $settings->contactEmails->getValue(); + */ +class MeasurableSettings extends \Piwik\Settings\Measurable\MeasurableSettings +{ + /** @var Setting|null */ + public $appId; + + /** @var Setting */ + public $contactEmails; + + protected function init() + { + if ($this->hasMeasurableType(MobileAppType::ID)) { + // this setting will be only shown for mobile apps + $this->appId = $this->makeAppIdSetting(); + } + + $this->contactEmails = $this->makeContactEmailsSetting(); + } + + private function makeAppIdSetting() + { + $defaultValue = ''; + $type = FieldConfig::TYPE_STRING; + + return $this->makeSetting('mobile_app_id', $defaultValue, $type, function (FieldConfig $field) { + $field->title = 'App ID'; + $field->inlineHelp = 'Enter the id of the mobile app eg "org.domain.example"'; + $field->uiControl = FieldConfig::UI_CONTROL_TEXT; + }); + } + + private function makeContactEmailsSetting() + { + $defaultValue = array(); + $type = FieldConfig::TYPE_ARRAY; + + return $this->makeSetting('contact_email', $defaultValue, $type, function (FieldConfig $field) { + $field->title = 'Contact email addresses'; + $field->uiControl = FieldConfig::UI_CONTROL_TEXTAREA; + }); + } + +} diff --git a/plugins/ExampleSettingsPlugin/Settings.php b/plugins/ExampleSettingsPlugin/Settings.php deleted file mode 100644 index 1b110d2871..0000000000 --- a/plugins/ExampleSettingsPlugin/Settings.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Plugins\ExampleSettingsPlugin; - -use Piwik\Settings\SystemSetting; -use Piwik\Settings\UserSetting; - -/** - * Defines Settings for ExampleSettingsPlugin. - * - * Usage like this: - * $settings = new Settings('ExampleSettingsPlugin'); - * $settings->autoRefresh->getValue(); - * $settings->metric->getValue(); - */ -class Settings extends \Piwik\Plugin\Settings -{ - /** @var UserSetting */ - public $autoRefresh; - - /** @var UserSetting */ - public $refreshInterval; - - /** @var UserSetting */ - public $color; - - /** @var SystemSetting */ - public $metric; - - /** @var SystemSetting */ - public $browsers; - - /** @var SystemSetting */ - public $description; - - /** @var SystemSetting */ - public $password; - - protected function init() - { - $this->setIntroduction('Here you can specify the settings for this plugin.'); - - // User setting --> checkbox converted to bool - $this->createAutoRefreshSetting(); - - // User setting --> textbox converted to int defining a validator and filter - $this->createRefreshIntervalSetting(); - - // User setting --> radio - $this->createColorSetting(); - - // System setting --> allows selection of a single value - $this->createMetricSetting(); - - // System setting --> allows selection of multiple values - $this->createBrowsersSetting(); - - // System setting --> textarea - $this->createDescriptionSetting(); - - // System setting --> textarea - $this->createPasswordSetting(); - } - - private function createAutoRefreshSetting() - { - $this->autoRefresh = new UserSetting('autoRefresh', 'Auto refresh'); - $this->autoRefresh->type = static::TYPE_BOOL; - $this->autoRefresh->uiControlType = static::CONTROL_CHECKBOX; - $this->autoRefresh->description = 'If enabled, the value will be automatically refreshed depending on the specified interval'; - $this->autoRefresh->defaultValue = false; - - $this->addSetting($this->autoRefresh); - } - - private function createRefreshIntervalSetting() - { - $this->refreshInterval = new UserSetting('refreshInterval', 'Refresh Interval'); - $this->refreshInterval->type = static::TYPE_INT; - $this->refreshInterval->uiControlType = static::CONTROL_TEXT; - $this->refreshInterval->uiControlAttributes = array('size' => 3); - $this->refreshInterval->description = 'Defines how often the value should be updated'; - $this->refreshInterval->inlineHelp = 'Enter a number which is >= 15'; - $this->refreshInterval->defaultValue = '30'; - $this->refreshInterval->validate = function ($value, $setting) { - if ($value < 15) { - throw new \Exception('Value is invalid'); - } - }; - - $this->addSetting($this->refreshInterval); - } - - private function createColorSetting() - { - $this->color = new UserSetting('color', 'Color'); - $this->color->uiControlType = static::CONTROL_RADIO; - $this->color->description = 'Pick your favourite color'; - $this->color->availableValues = array('red' => 'Red', 'blue' => 'Blue', 'green' => 'Green'); - - $this->addSetting($this->color); - } - - private function createMetricSetting() - { - $this->metric = new SystemSetting('metric', 'Metric to display'); - $this->metric->type = static::TYPE_STRING; - $this->metric->uiControlType = static::CONTROL_SINGLE_SELECT; - $this->metric->availableValues = array('nb_visits' => 'Visits', 'nb_actions' => 'Actions', 'visitors' => 'Visitors'); - $this->metric->introduction = 'Only Super Users can change the following settings:'; - $this->metric->description = 'Choose the metric that should be displayed in the browser tab'; - $this->metric->defaultValue = 'nb_visits'; - $this->metric->readableByCurrentUser = true; - - $this->addSetting($this->metric); - } - - private function createBrowsersSetting() - { - $this->browsers = new SystemSetting('browsers', 'Supported Browsers'); - $this->browsers->type = static::TYPE_ARRAY; - $this->browsers->uiControlType = static::CONTROL_MULTI_SELECT; - $this->browsers->availableValues = array('firefox' => 'Firefox', 'chromium' => 'Chromium', 'safari' => 'safari'); - $this->browsers->description = 'The value will be only displayed in the following browsers'; - $this->browsers->defaultValue = array('firefox', 'chromium', 'safari'); - $this->browsers->readableByCurrentUser = true; - - $this->addSetting($this->browsers); - } - - private function createDescriptionSetting() - { - $this->description = new SystemSetting('description', 'Description for value'); - $this->description->readableByCurrentUser = true; - $this->description->uiControlType = static::CONTROL_TEXTAREA; - $this->description->description = 'This description will be displayed next to the value'; - $this->description->defaultValue = "This is the value: \nAnother line"; - - $this->addSetting($this->description); - } - - private function createPasswordSetting() - { - $this->password = new SystemSetting('password', 'API password'); - $this->password->readableByCurrentUser = true; - $this->password->uiControlType = static::CONTROL_PASSWORD; - $this->password->description = 'Password for the 3rd API where we fetch the value'; - $this->password->transform = function ($value) { - return sha1($value . 'salt'); - }; - - $this->addSetting($this->password); - } -} diff --git a/plugins/ExampleSettingsPlugin/SystemSettings.php b/plugins/ExampleSettingsPlugin/SystemSettings.php new file mode 100644 index 0000000000..1d2082e949 --- /dev/null +++ b/plugins/ExampleSettingsPlugin/SystemSettings.php @@ -0,0 +1,96 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\ExampleSettingsPlugin; + +use Piwik\Settings\Setting; +use Piwik\Settings\FieldConfig; + +/** + * Defines Settings for ExampleSettingsPlugin. + * + * Usage like this: + * $settings = new SystemSettings(); + * $settings->metric->getValue(); + * $settings->description->getValue(); + */ +class SystemSettings extends \Piwik\Settings\Plugin\SystemSettings +{ + /** @var Setting */ + public $metric; + + /** @var Setting */ + public $browsers; + + /** @var Setting */ + public $description; + + /** @var Setting */ + public $password; + + protected function init() + { + // System setting --> allows selection of a single value + $this->metric = $this->createMetricSetting(); + + // System setting --> allows selection of multiple values + $this->browsers = $this->createBrowsersSetting(); + + // System setting --> textarea + $this->description = $this->createDescriptionSetting(); + + // System setting --> textarea + $this->password = $this->createPasswordSetting(); + } + + private function createMetricSetting() + { + return $this->makeSetting('metric', $default = 'nb_visits', FieldConfig::TYPE_STRING, function (FieldConfig $field) { + $field->title = 'Metric to display'; + $field->uiControl = FieldConfig::UI_CONTROL_SINGLE_SELECT; + $field->availableValues = array('nb_visits' => 'Visits', 'nb_actions' => 'Actions', 'visitors' => 'Visitors'); + $field->introduction = 'Only Super Users can change the following settings:'; + $field->description = 'Choose the metric that should be displayed in the browser tab'; + }); + } + + private function createBrowsersSetting() + { + $default = array('firefox', 'chromium', 'safari'); + + return $this->makeSetting('browsers', $default, FieldConfig::TYPE_ARRAY, function (FieldConfig $field) { + $field->title = 'Supported Browsers'; + $field->uiControl = FieldConfig::UI_CONTROL_MULTI_SELECT; + $field->availableValues = array('firefox' => 'Firefox', 'chromium' => 'Chromium', 'safari' => 'safari'); + $field->description = 'The value will be only displayed in the following browsers'; + }); + } + + private function createDescriptionSetting() + { + $default = "This is the value: \nAnother line"; + + return $this->makeSetting('description', $default, FieldConfig::TYPE_STRING, function (FieldConfig $field) { + $field->title = 'Description for value'; + $field->uiControl = FieldConfig::UI_CONTROL_TEXTAREA; + $field->description = 'This description will be displayed next to the value'; + }); + } + + private function createPasswordSetting() + { + return $this->makeSetting('password', $default = null, FieldConfig::TYPE_STRING, function (FieldConfig $field) { + $field->title = 'API password'; + $field->uiControl = FieldConfig::UI_CONTROL_PASSWORD; + $field->description = 'Password for the 3rd API where we fetch the value'; + $field->transform = function ($value) { + return sha1($value . 'salt'); + }; + }); + } +} diff --git a/plugins/ExampleSettingsPlugin/UserSettings.php b/plugins/ExampleSettingsPlugin/UserSettings.php new file mode 100644 index 0000000000..cffa88d16d --- /dev/null +++ b/plugins/ExampleSettingsPlugin/UserSettings.php @@ -0,0 +1,80 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\ExampleSettingsPlugin; + +use Piwik\Settings\Setting; +use Piwik\Settings\FieldConfig; + +/** + * Defines Settings for ExampleSettingsPlugin. + * + * Usage like this: + * $settings = new UserSettings(); + * $settings->autoRefresh->getValue(); + * $settings->color->getValue(); + */ +class UserSettings extends \Piwik\Settings\Plugin\UserSettings +{ + /** @var Setting */ + public $autoRefresh; + + /** @var Setting */ + public $refreshInterval; + + /** @var Setting */ + public $color; + + protected function init() + { + // User setting --> checkbox converted to bool + $this->autoRefresh = $this->createAutoRefreshSetting(); + + // User setting --> textbox converted to int defining a validator and filter + $this->refreshInterval = $this->createRefreshIntervalSetting(); + + // User setting --> radio + $this->color = $this->createColorSetting(); + } + + private function createAutoRefreshSetting() + { + return $this->makeSetting('autoRefresh', $default = false, FieldConfig::TYPE_BOOL, function (FieldConfig $field) { + $field->title = 'Auto refresh'; + $field->uiControl = FieldConfig::UI_CONTROL_CHECKBOX; + $field->description = 'If enabled, the value will be automatically refreshed depending on the specified interval'; + }); + } + + private function createRefreshIntervalSetting() + { + return $this->makeSetting('refreshInterval', $default = '30', FieldConfig::TYPE_INT, function (FieldConfig $field) { + $field->title = 'Refresh Interval'; + $field->uiControl = FieldConfig::UI_CONTROL_TEXT; + $field->uiControlAttributes = array('size' => 3); + $field->description = 'Defines how often the value should be updated'; + $field->inlineHelp = 'Enter a number which is >= 15'; + $field->validate = function ($value, $setting) { + if ($value < 15) { + throw new \Exception('Value is invalid'); + } + }; + }); + } + + private function createColorSetting() + { + return $this->makeSetting('color', $default = 'red', FieldConfig::TYPE_STRING, function (FieldConfig $field) { + $field->title = 'Color'; + $field->uiControl = FieldConfig::UI_CONTROL_RADIO; + $field->description = 'Pick your favourite color'; + $field->availableValues = array('red' => 'Red', 'blue' => 'Blue', 'green' => 'Green'); + }); + } + +} diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php index bb7da8cbae..e1db0d187c 100644 --- a/plugins/Goals/API.php +++ b/plugins/Goals/API.php @@ -22,7 +22,7 @@ use Piwik\Plugin\Report; use Piwik\Plugins\API\DataTable\MergeDataTables; use Piwik\Plugins\CoreHome\Columns\Metrics\ConversionRate; use Piwik\Plugins\Goals\Columns\Metrics\AverageOrderRevenue; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Segment\SegmentExpression; use Piwik\Site; use Piwik\Tracker\Cache; @@ -415,7 +415,7 @@ class API extends \Piwik\Plugin\API $requestedColumns = array_unique(array_merge($requestedColumns, $metricsToAdd)); } - $report = Reports::factory('Goals', 'getMetrics'); + $report = ReportsProvider::factory('Goals', 'getMetrics'); $columnsToGet = $report->getMetricsRequiredForReport($allMetrics, $requestedColumns); $inDbMetricNames = array_map(function ($name) use ($idGoal) { diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php index 03420aa8d7..1f9d229428 100644 --- a/plugins/Goals/Goals.php +++ b/plugins/Goals/Goals.php @@ -12,7 +12,7 @@ use Piwik\ArchiveProcessor; use Piwik\Common; use Piwik\Db; use Piwik\Piwik; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Tracker\GoalManager; use Piwik\Category\Subcategory; @@ -180,7 +180,7 @@ class Goals extends \Piwik\Plugin { $reportsWithGoals = array(); - $reports = new Reports(); + $reports = new ReportsProvider(); foreach ($reports->getAllReports() as $report) { if ($report->hasGoalMetrics()) { diff --git a/plugins/Goals/Pages.php b/plugins/Goals/Pages.php index 50e3f63e37..be55bff144 100644 --- a/plugins/Goals/Pages.php +++ b/plugins/Goals/Pages.php @@ -13,7 +13,7 @@ use Piwik\Common; use Piwik\Piwik; use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Widget\WidgetContainerConfig; use Piwik\Widget\WidgetConfig; use Piwik\Report\ReportWidgetFactory; @@ -335,7 +335,7 @@ class Pages private function createWidgetForReport($module, $action) { - $report = Reports::factory($module, $action); + $report = ReportsProvider::factory($module, $action); $factory = new ReportWidgetFactory($report); return $factory->createWidget(); } diff --git a/plugins/LogViewer b/plugins/LogViewer -Subproject 1703d7536e39240643204a1f334d8b8da6cd6fc +Subproject 15d48dd1788f71bcf0a7578fe5664af6c450362 diff --git a/plugins/MobileAppMeasurable/Type.php b/plugins/MobileAppMeasurable/Type.php index 6a6fd64c58..34ff4335b5 100644 --- a/plugins/MobileAppMeasurable/Type.php +++ b/plugins/MobileAppMeasurable/Type.php @@ -16,6 +16,5 @@ class Type extends \Piwik\Measurable\Type protected $description = 'MobileAppMeasurable_MobileAppDescription'; protected $howToSetupUrl = 'http://developer.piwik.org/guides/tracking-api-clients#mobile-sdks'; - } diff --git a/plugins/MobileAppMeasurable/config/test.php b/plugins/MobileAppMeasurable/config/test.php deleted file mode 100644 index 08a3359557..0000000000 --- a/plugins/MobileAppMeasurable/config/test.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -return array( - - 'Piwik\Plugins\MobileAppMeasurable\Type' => DI\object('Piwik\Plugins\MobileAppMeasurable\tests\Framework\Mock\Type'), - -);
\ No newline at end of file diff --git a/plugins/MobileAppMeasurable/tests/Framework/Mock/Type.php b/plugins/MobileAppMeasurable/tests/Framework/Mock/Type.php deleted file mode 100644 index c573f5b101..0000000000 --- a/plugins/MobileAppMeasurable/tests/Framework/Mock/Type.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php -/** -* Piwik - free/libre analytics platform -* -* @link http://piwik.org -* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later -*/ - -namespace Piwik\Plugins\MobileAppMeasurable\tests\Framework\Mock; - -use Piwik\Measurable\MeasurableSetting; -use Piwik\Measurable\MeasurableSettings; -use Piwik\Tracker; - -class Type extends \Piwik\Plugins\MobileAppMeasurable\Type -{ - - public function configureMeasurableSettings(MeasurableSettings $settings) - { - $appId = new MeasurableSetting('app_id', 'App-ID'); - $appId->validate = function ($value) { - if (strlen($value) > 100) { - throw new \Exception('Only 100 characters are allowed'); - } - }; - - $settings->addSetting($appId); - } -} diff --git a/plugins/Morpheus/Menu.php b/plugins/Morpheus/Menu.php index 5fc5f5c6ed..a45b35c0b6 100644 --- a/plugins/Morpheus/Menu.php +++ b/plugins/Morpheus/Menu.php @@ -28,9 +28,5 @@ class Menu extends \Piwik\Plugin\Menu if (Development::isEnabled() && Piwik::isUserHasSomeAdminAccess()) { $menu->addDevelopmentItem('UI Demo', $this->urlForAction('demo')); } - - if (Development::isEnabled() && Piwik::isUserHasSomeAdminAccess()) { - $menu->addPlatformItem('UI Demo', $this->urlForAction('demo'), $order = 15); - } } } diff --git a/plugins/Morpheus/templates/settingsMacros.twig b/plugins/Morpheus/templates/settingsMacros.twig index 19a88bfa28..c09aa608eb 100644 --- a/plugins/Morpheus/templates/settingsMacros.twig +++ b/plugins/Morpheus/templates/settingsMacros.twig @@ -22,14 +22,14 @@ {% set settingValue = setting.getValue %} - {% if setting.uiControlType != 'checkbox' %} + {% if setting.uiControl != 'checkbox' %} <label>{{ setting.title }}</label> {% endif %} {% if setting.inlineHelp %} <div class="form-help"> {{ setting.inlineHelp }} - {% if setting.defaultValue and setting.uiControlType != 'checkbox' and setting.uiControlType != 'radio' %} + {% if setting.defaultValue and setting.uiControl != 'checkbox' and setting.uiControl != 'radio' %} <br/> {{ 'General_Default'|translate }}: {% if setting.defaultValue is iterable %} @@ -41,13 +41,13 @@ </div> {% endif %} - {% if setting.uiControlType == 'select' or setting.uiControlType == 'multiselect' %} + {% if setting.uiControl == 'select' or setting.uiControl == 'multiselect' %} <select {% for attr, val in setting.uiControlAttributes %} {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}" {% endfor %} name="{{ setting.getKey|e('html_attr') }}" - {% if setting.uiControlType == 'multiselect' %}multiple{% endif %}> + {% if setting.uiControl == 'multiselect' %}multiple{% endif %}> {% for key, value in setting.availableValues %} <option value='{{ key }}' @@ -61,7 +61,7 @@ {% endfor %} </select> - {% elseif setting.uiControlType == 'textarea' %} + {% elseif setting.uiControl == 'textarea' %} <textarea style="width: 376px; height: 250px;" {% for attr, val in setting.uiControlAttributes %} {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}" @@ -70,7 +70,7 @@ > {{- settingValue -}} </textarea> - {% elseif setting.uiControlType == 'radio' %} + {% elseif setting.uiControl == 'radio' %} {% for key, value in setting.availableValues %} <label class="radio"> @@ -95,7 +95,7 @@ </label> {% endfor %} - {% elseif setting.uiControlType == 'checkbox' %} + {% elseif setting.uiControl == 'checkbox' %} <label class="checkbox"> <input id="name-value-{{ index }}" @@ -122,14 +122,14 @@ {% for attr, val in setting.uiControlAttributes %} {{ attr|e('html_attr') }}="{{ val|e('html_attr') }}" {% endfor %} - class="control_{{ setting.uiControlType|e('html_attr') }}" - type="{{ setting.uiControlType|e('html_attr') }}" + class="control_{{ setting.uiControl|e('html_attr') }}" + type="{{ setting.uiControl|e('html_attr') }}" name="{{ setting.getKey|e('html_attr') }}" value="{{ settingValue|e('html_attr') }}"> {% endif %} - {% if setting.uiControlType != 'checkbox' and setting.uiControlType != 'radio' %} + {% if setting.uiControl != 'checkbox' and setting.uiControl != 'radio' %} <span class='form-description'>{{ setting.description }}</span> {% endif %} diff --git a/plugins/PiwikPro/Widgets.php b/plugins/PiwikPro/Widgets.php deleted file mode 100644 index f8e007aa07..0000000000 --- a/plugins/PiwikPro/Widgets.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\PiwikPro; - -use Piwik\Piwik; -use Piwik\PiwikPro\Advertising; -use Piwik\Plugins\ExampleRssWidget\RssRenderer; -use Piwik\View; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'About Piwik'; - - /** - * @var Advertising - */ - private $advertising; - - /** - * @var Promo - */ - private $promo; - - public function __construct(Advertising $advertising, Promo $promo) - { - $this->advertising = $advertising; - $this->promo = $promo; - } - - protected function init() - { - if ($this->advertising->arePiwikProAdsEnabled()) { - $this->addWidget('PiwikPro_WidgetBlogTitle', 'rssPiwikPro'); - $this->addWidget('PiwikPro_WidgetPiwikProAd', 'promoPiwikPro'); - } - } - - public function rssPiwikPro() - { - try { - $rss = new RssRenderer('https://piwik.pro/feed/'); - $rss->showDescription(true); - - return $rss->get(); - - } catch (\Exception $e) { - - return $this->error($e); - } - } - - public function promoPiwikPro() - { - $view = new View('@PiwikPro/promoPiwikProWidget'); - - $promo = $this->promo->getContent(); - - $view->ctaLinkUrl = $this->advertising->getPromoUrlForOnPremises('PromoWidget', $promo['campaignContent']); - $view->ctaText = $promo['text']; - $view->ctaLinkTitle = $this->promo->getLinkTitle(); - - return $view->render(); - } - - /** - * @param \Exception $e - * @return string - */ - private function error($e) - { - return '<div class="pk-emptyDataTable">' - . Piwik::translate('General_ErrorRequest', array('', '')) - . ' - ' . $e->getMessage() . '</div>'; - } - -} diff --git a/plugins/QueuedTracking b/plugins/QueuedTracking -Subproject a7137646fa4d738c6378686df6cd222fd291217 +Subproject be67d637924b3b84b989e8c89cc85619bec6325 diff --git a/plugins/Resolution/Reports/GetConfiguration.php b/plugins/Resolution/Reports/GetConfiguration.php index 600f07edd9..8e42b28a24 100644 --- a/plugins/Resolution/Reports/GetConfiguration.php +++ b/plugins/Resolution/Reports/GetConfiguration.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\Resolution\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\Resolution\Columns\Configuration; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetConfiguration extends Base { @@ -38,7 +38,7 @@ class GetConfiguration extends Base public function getRelatedReports() { return array( - Reports::factory('Resolution', 'getResolution'), + ReportsProvider::factory('Resolution', 'getResolution'), ); } } diff --git a/plugins/Resolution/Reports/GetResolution.php b/plugins/Resolution/Reports/GetResolution.php index 3b27236a86..2e18ec95aa 100644 --- a/plugins/Resolution/Reports/GetResolution.php +++ b/plugins/Resolution/Reports/GetResolution.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\Resolution\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\Resolution\Columns\Resolution; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetResolution extends Base { @@ -36,7 +36,7 @@ class GetResolution extends Base public function getRelatedReports() { return array( - Reports::factory('Resolution', 'getConfiguration'), + ReportsProvider::factory('Resolution', 'getConfiguration'), ); } } diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index d6b969f9ad..70711c2f0b 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -18,7 +18,11 @@ use Piwik\Metrics\Formatter; use Piwik\Network\IPUtils; use Piwik\Option; use Piwik\Piwik; -use Piwik\Measurable\MeasurableSettings; +use Piwik\Plugin\SettingsProvider; +use Piwik\Plugins\CorePluginsAdmin\SettingsMetadata; +use Piwik\Plugins\WebsiteMeasurable\Settings\Urls; +use Piwik\Settings\Measurable\MeasurableProperty; +use Piwik\Settings\Measurable\MeasurableSettings; use Piwik\ProxyHttp; use Piwik\Scheduler\Scheduler; use Piwik\SettingsPiwik; @@ -61,6 +65,22 @@ class API extends \Piwik\Plugin\API const OPTION_KEEP_URL_FRAGMENTS_GLOBAL = 'SitesManager_KeepURLFragmentsGlobal'; /** + * @var SettingsProvider + */ + private $settingsProvider; + + /** + * @var SettingsMetadata + */ + private $settingsMetadata; + + public function __construct(SettingsProvider $provider, SettingsMetadata $settingsMetadata) + { + $this->settingsProvider = $provider; + $this->settingsMetadata = $settingsMetadata; + } + + /** * Returns the javascript tag for the given idSite. * This tag must be included on every page to be tracked by Piwik * @@ -505,7 +525,7 @@ class API extends \Piwik\Plugin\API * @param null|string $excludedUserAgents * @param int $keepURLFragments If 1, URL fragments will be kept when tracking. If 2, they * will be removed. If 0, the default global behavior will be used. - * @param array|null $settings JSON serialized settings eg {settingName: settingValue, ...} + * @param array|null $settingValues JSON serialized settings eg {settingName: settingValue, ...} * @see getKeepURLFragmentsGlobal. * @param string $type The website type, defaults to "website" if not set. * @param bool|null $excludeUnknownUrls Track only URL matching one of website URLs @@ -513,7 +533,7 @@ class API extends \Piwik\Plugin\API * @return int the website ID created */ public function addSite($siteName, - $urls, + $urls = null, $ecommerce = null, $siteSearch = null, $searchKeywordParameters = null, @@ -527,20 +547,47 @@ class API extends \Piwik\Plugin\API $excludedUserAgents = null, $keepURLFragments = null, $type = null, - $settings = null, + $settingValues = null, $excludeUnknownUrls = null) { Piwik::checkUserHasSuperUserAccess(); $this->checkName($siteName); - $urls = $this->cleanParameterUrls($urls); - $this->checkUrls($urls); - $this->checkAtLeastOneUrl($urls); - $siteSearch = $this->checkSiteSearch($siteSearch); - list($searchKeywordParameters, $searchCategoryParameters) = $this->checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters); - $keepURLFragments = (int)$keepURLFragments; - self::checkKeepURLFragmentsValue($keepURLFragments); + if (empty($settingValues)) { + $settingValues = array(); + } + + if (isset($urls)) { + $settingValues = $this->setSettingValue('urls', $urls, $settingValues); + } + if (isset($ecommerce)) { + $settingValues = $this->setSettingValue('ecommerce', $ecommerce, $settingValues); + } + if (isset($siteSearch)) { + $settingValues = $this->setSettingValue('sitesearch', $siteSearch, $settingValues); + } + if (isset($searchKeywordParameters)) { + $settingValues = $this->setSettingValue('sitesearch_keyword_parameters', explode(',', $searchKeywordParameters), $settingValues); + } + if (isset($searchCategoryParameters)) { + $settingValues = $this->setSettingValue('sitesearch_category_parameters', explode(',', $searchCategoryParameters), $settingValues); + } + if (isset($keepURLFragments)) { + $settingValues = $this->setSettingValue('keep_url_fragment', $keepURLFragments, $settingValues); + } + if (isset($excludeUnknownUrls)) { + $settingValues = $this->setSettingValue('exclude_unknown_urls', $excludeUnknownUrls, $settingValues); + } + if (isset($excludedIps)) { + $settingValues = $this->setSettingValue('excluded_ips', explode(',', $excludedIps), $settingValues); + } + if (isset($excludedQueryParameters)) { + $settingValues = $this->setSettingValue('excluded_parameters', explode(',', $excludedQueryParameters), $settingValues); + } + if (isset($excludedUserAgents)) { + $settingValues = $this->setSettingValue('excluded_user_agents', explode(',', $excludedUserAgents), $settingValues); + } $timezone = trim($timezone); if (empty($timezone)) { @@ -553,23 +600,10 @@ class API extends \Piwik\Plugin\API } $this->checkValidCurrency($currency); - $url = $urls[0]; - $urls = array_slice($urls, 1); - - $bind = array('name' => $siteName, - 'main_url' => $url); - - $bind['exclude_unknown_urls'] = (int)$excludeUnknownUrls; - $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps); - $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters); - $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents); - $bind['keep_url_fragment'] = $keepURLFragments; + $bind = array('name' => $siteName); $bind['timezone'] = $timezone; $bind['currency'] = $currency; - $bind['ecommerce'] = (int)$ecommerce; - $bind['sitesearch'] = $siteSearch; - $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters; - $bind['sitesearch_category_parameters'] = $searchCategoryParameters; + $bind['main_url'] = ''; if (is_null($startDate)) { $bind['ts_created'] = Date::now()->getDatetime(); @@ -585,21 +619,31 @@ class API extends \Piwik\Plugin\API $bind['group'] = ""; } - if (!empty($settings)) { - $this->validateMeasurableSettings(0, $bind['type'], $settings); + $allSettings = $this->setAndValidateMeasurableSettings(0, $bind['type'], $settingValues); + + foreach ($allSettings as $settings) { + foreach ($settings->getSettingsWritableByCurrentUser() as $setting) { + $name = $setting->getName(); + if ($setting instanceof MeasurableProperty && $name !== 'urls') { + $default = $setting->getDefaultValue(); + if (is_bool($default)) { + $default = (int) $default; + } elseif (is_array($default)) { + $default = implode(',', $default); + } + + $bind[$name] = $default; + } + } } $idSite = $this->getModel()->createSite($bind); - $this->insertSiteUrls($idSite, $urls); + $this->saveMeasurableSettings($idSite, $bind['type'], $settingValues); // we reload the access list which doesn't yet take in consideration this new website Access::getInstance()->reloadAccess(); - if (!empty($settings)) { - $this->updateMeasurableSettings($idSite, $settings); - } - $this->postUpdateWebsite($idSite); /** @@ -612,34 +656,57 @@ class API extends \Piwik\Plugin\API return (int) $idSite; } - private function validateMeasurableSettings($idSite, $idType, $settings) + private function setSettingValue($fieldName, $value, $settingValues) { - $measurableSettings = new MeasurableSettings($idSite, $idType); + $pluginName = 'WebsiteMeasurable'; + if (empty($settingValues[$pluginName])) { + $settingValues[$pluginName] = array(); + } - foreach ($measurableSettings->getSettingsForCurrentUser() as $measurableSetting) { - $name = $measurableSetting->getName(); - if (!empty($settings[$name])) { - $measurableSetting->setValue($settings[$name]); + $found = false; + foreach ($settingValues[$pluginName] as $key => $setting) { + if ($setting['name'] === $fieldName) { + $setting['value'] = $value; + $found = true; + break; } } + + if (!$found) { + $settingValues[$pluginName][] = array('name' => $fieldName, 'value' => $value); + } + + return $settingValues; } - private function updateMeasurableSettings($idSite, $settings) + public function getSiteSettings($idSite) { - $idType = Site::getTypeFor($idSite); + Piwik::checkUserHasAdminAccess($idSite); - $measurableSettings = new MeasurableSettings($idSite, $idType); + $measurableSettings = $this->settingsProvider->getAllMeasurableSettings($idSite, $idMeasurableType = false); - foreach ($measurableSettings->getSettingsForCurrentUser() as $measurableSetting) { - $name = $measurableSetting->getName(); - if (!empty($settings[$name])) { - $measurableSetting->setValue($settings[$name]); - } - // we do not clear existing settings if the value is missing. - // There can be so many settings added by random plugins one would always clear some settings. - } + return $this->settingsMetadata->formatSettings($measurableSettings); + } - $measurableSettings->save(); + private function setAndValidateMeasurableSettings($idSite, $idType, $settingValues) + { + $measurableSettings = $this->settingsProvider->getAllMeasurableSettings($idSite, $idType); + + $this->settingsMetadata->setPluginSettings($measurableSettings, $settingValues); + + return $measurableSettings; + } + + /** + * @param MeasurableSettings[] $measurableSettings + */ + private function saveMeasurableSettings($idSite, $idType, $settingValues) + { + $measurableSettings = $this->setAndValidateMeasurableSettings($idSite, $idType, $settingValues); + + foreach ($measurableSettings as $measurableSetting) { + $measurableSetting->save(); + } } private function postUpdateWebsite($idSite) @@ -685,21 +752,6 @@ class API extends \Piwik\Plugin\API Piwik::postEvent('SitesManager.deleteSite.end', array($idSite)); } - /** - * Checks that the array has at least one element - * - * @param array $urls - * @throws Exception - */ - private function checkAtLeastOneUrl($urls) - { - if (!is_array($urls) - || count($urls) == 0 - ) { - throw new Exception(Piwik::translate("SitesManager_ExceptionNoUrl")); - } - } - private function checkValidTimezone($timezone) { $timezones = $this->getTimezonesList(); @@ -775,15 +827,26 @@ class API extends \Piwik\Plugin\API { Piwik::checkUserHasAdminAccess($idSite); - $urls = $this->cleanParameterUrls($urls); - $this->checkUrls($urls); + if (empty($urls)) { + return 0; + } + + if (!is_array($urls)) { + $urls = array($urls); + } $urlsInit = $this->getSiteUrlsFromId($idSite); - $toInsert = array_diff($urls, $urlsInit); - $this->insertSiteUrls($idSite, $toInsert); + $toInsert = array_merge($urlsInit, $urls); + + $urlsProperty = new Urls($idSite); + $urlsProperty->setValue($toInsert); + $urlsProperty->save(); + + $inserted = array_diff($urlsProperty->getValue(), $urlsInit); + $this->postUpdateWebsite($idSite); - return count($toInsert); + return count($inserted); } /** @@ -798,14 +861,18 @@ class API extends \Piwik\Plugin\API { Piwik::checkUserHasAdminAccess($idSite); - $urls = $this->cleanParameterUrls($urls); - $this->checkUrls($urls); + $mainUrl = Site::getMainUrlFor($idSite); + array_unshift($urls, $mainUrl); + + $urlsProperty = new Urls($idSite); + $urlsProperty->setValue($urls); + $urlsProperty->save(); + + $inserted = array_diff($urlsProperty->getValue(), $urls); - $this->getModel()->deleteSiteAliasUrls($idSite); - $this->insertSiteUrls($idSite, $urls); $this->postUpdateWebsite($idSite); - return count($urls); + return count($inserted); } /** @@ -1093,7 +1160,7 @@ class API extends \Piwik\Plugin\API * @param int|null $keepURLFragments If 1, URL fragments will be kept when tracking. If 2, they * will be removed. If 0, the default global behavior will be used. * @param string $type The Website type, default value is "website" - * @param array|null $settings JSON serialized settings eg {settingName: settingValue, ...} + * @param array|null $settingValues JSON serialized settings eg {settingName: settingValue, ...} * @param bool|null $excludeUnknownUrls Track only URL matching one of website URLs * @throws Exception * @see getKeepURLFragmentsGlobal. If null, the existing value will @@ -1117,7 +1184,7 @@ class API extends \Piwik\Plugin\API $excludedUserAgents = null, $keepURLFragments = null, $type = null, - $settings = null, + $settingValues = null, $excludeUnknownUrls = null) { Piwik::checkUserHasAdminAccess($idSite); @@ -1136,71 +1203,74 @@ class API extends \Piwik\Plugin\API $bind['name'] = $siteName; } - if (!is_null($urls)) { - $urls = $this->cleanParameterUrls($urls); - $this->checkUrls($urls); - $this->checkAtLeastOneUrl($urls); - $url = $urls[0]; - $bind['main_url'] = $url; + if (empty($settingValues)) { + $settingValues = array(); } - if (!is_null($currency)) { + if (isset($urls)) { + $settingValues = $this->setSettingValue('urls', $urls, $settingValues); + } + if (isset($ecommerce)) { + $settingValues = $this->setSettingValue('ecommerce', $ecommerce, $settingValues); + } + if (isset($siteSearch)) { + $settingValues = $this->setSettingValue('sitesearch', $siteSearch, $settingValues); + } + if (isset($searchKeywordParameters)) { + $settingValues = $this->setSettingValue('sitesearch_keyword_parameters', explode(',', $searchKeywordParameters), $settingValues); + } + if (isset($searchCategoryParameters)) { + $settingValues = $this->setSettingValue('sitesearch_category_parameters', explode(',', $searchCategoryParameters), $settingValues); + } + if (isset($keepURLFragments)) { + $settingValues = $this->setSettingValue('keep_url_fragment', $keepURLFragments, $settingValues); + } + if (isset($excludeUnknownUrls)) { + $settingValues = $this->setSettingValue('exclude_unknown_urls', $excludeUnknownUrls, $settingValues); + } + if (isset($excludedIps)) { + $settingValues = $this->setSettingValue('excluded_ips', explode(',', $excludedIps), $settingValues); + } + if (isset($excludedQueryParameters)) { + $settingValues = $this->setSettingValue('excluded_parameters', explode(',', $excludedQueryParameters), $settingValues); + } + if (isset($excludedUserAgents)) { + $settingValues = $this->setSettingValue('excluded_user_agents', explode(',', $excludedUserAgents), $settingValues); + } + + if (isset($currency)) { $currency = trim($currency); $this->checkValidCurrency($currency); $bind['currency'] = $currency; } - if (!is_null($timezone)) { + if (isset($timezone)) { $timezone = trim($timezone); $this->checkValidTimezone($timezone); $bind['timezone'] = $timezone; } - if (!is_null($group) + if (isset($group) && Piwik::hasUserSuperUserAccess() ) { $bind['group'] = trim($group); } - if (!is_null($ecommerce)) { - $bind['ecommerce'] = (int)(bool)$ecommerce; - } - if (!is_null($startDate)) { + if (isset($startDate)) { $bind['ts_created'] = Date::factory($startDate)->getDatetime(); } - $bind['exclude_unknown_urls'] = (int)$excludeUnknownUrls; - $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps); - $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters); - $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents); - - if (!is_null($keepURLFragments)) { - $keepURLFragments = (int)$keepURLFragments; - self::checkKeepURLFragmentsValue($keepURLFragments); - - $bind['keep_url_fragment'] = $keepURLFragments; - } - $bind['sitesearch'] = $this->checkSiteSearch($siteSearch); - list($searchKeywordParameters, $searchCategoryParameters) = $this->checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters); - $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters; - $bind['sitesearch_category_parameters'] = $searchCategoryParameters; - - if (!is_null($type)) { + if (isset($type)) { $bind['type'] = $this->checkAndReturnType($type); } - if (!empty($settings)) { - $this->validateMeasurableSettings($idSite, Site::getTypeFor($idSite), $settings); + if (!empty($settingValues)) { + $this->setAndValidateMeasurableSettings($idSite, $idType = null, $settingValues); } - $this->getModel()->updateSite($bind, $idSite); - - if (!empty($settings)) { - $this->updateMeasurableSettings($idSite, $settings); + if (!empty($bind)) { + $this->getModel()->updateSite($bind, $idSite); } - // we now update the main + alias URLs - $this->getModel()->deleteSiteAliasUrls($idSite); - - if (count($urls) > 1) { - $this->addSiteAliasUrls($idSite, array_slice($urls, 1)); + if (!empty($settingValues)) { + $this->saveMeasurableSettings($idSite, $idType = null, $settingValues); } $this->postUpdateWebsite($idSite); @@ -1359,23 +1429,6 @@ class API extends \Piwik\Plugin\API } /** - * Insert the list of alias URLs for the website. - * The URLs must not exist already for this website! - */ - private function insertSiteUrls($idSite, $urls) - { - if (count($urls) != 0) { - foreach ($urls as $url) { - try { - $this->getModel()->insertSiteUrl($idSite, $url); - } catch(Exception $e) { - // See bug #4149 - } - } - } - } - - /** * Remove the final slash in the URLs if found * * @param string $url @@ -1429,77 +1482,6 @@ class API extends \Piwik\Plugin\API } } - private function checkSiteSearch($siteSearch) - { - if ($siteSearch === null) { - return "1"; - } - return $siteSearch == 1 ? "1" : "0"; - } - - private function checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters) - { - $searchKeywordParameters = trim($searchKeywordParameters); - $searchCategoryParameters = trim($searchCategoryParameters); - if (empty($searchKeywordParameters)) { - $searchKeywordParameters = ''; - } - - if (empty($searchCategoryParameters)) { - $searchCategoryParameters = ''; - } - - return array($searchKeywordParameters, $searchCategoryParameters); - } - - /** - * Check that the array of URLs are valid URLs - * - * @param array $urls - * @throws Exception if any of the urls is not valid - */ - private function checkUrls($urls) - { - foreach ($urls as $url) { - if (!$this->isValidUrl($url)) { - throw new Exception(sprintf(Piwik::translate("SitesManager_ExceptionInvalidUrl"), $url)); - } - } - } - - /** - * Clean the parameter URLs: - * - if the parameter is a string make it an array - * - remove the trailing slashes if found - * - * @param string|array urls - * @return array the array of cleaned URLs - */ - private function cleanParameterUrls($urls) - { - if (!is_array($urls)) { - $urls = array($urls); - } - - $urls = array_filter($urls); - $urls = array_map('urldecode', $urls); - - foreach ($urls as &$url) { - $url = $this->removeTrailingSlash($url); - $scheme = parse_url($url, PHP_URL_SCHEME); - if (empty($scheme) - && strpos($url, '://') === false - ) { - $url = 'http://' . $url; - } - $url = trim($url); - $url = Common::sanitizeInputValue($url); - } - - $urls = array_unique($urls); - return $urls; - } - public function renameGroup($oldGroupName, $newGroupName) { Piwik::checkUserHasSuperUserAccess(); @@ -1567,19 +1549,4 @@ class API extends \Piwik\Plugin\API return SettingsPiwik::getWebsitesCountToDisplay(); } - /** - * Utility function that throws if a value is not valid for the 'keep_url_fragment' - * column of the piwik_site table. - * - * @param int $keepURLFragments - * @throws Exception - */ - private static function checkKeepURLFragmentsValue($keepURLFragments) - { - // make sure value is between 0 & 2 - if (!in_array($keepURLFragments, array(0, 1, 2))) { - throw new Exception("Error in SitesManager.updateSite: keepURLFragments must be between 0 & 2" . - " (actual value: $keepURLFragments)."); - } - } } diff --git a/plugins/SitesManager/Controller.php b/plugins/SitesManager/Controller.php index d64fb45fa9..5eb1ecf3ed 100644 --- a/plugins/SitesManager/Controller.php +++ b/plugins/SitesManager/Controller.php @@ -13,7 +13,7 @@ use Piwik\API\ResponseBuilder; use Piwik\Common; use Piwik\Exception\UnexpectedWebsiteFoundException; use Piwik\Piwik; -use Piwik\Measurable\MeasurableSettings; +use Piwik\Settings\Measurable\MeasurableSettings; use Piwik\SettingsPiwik; use Piwik\Site; use Piwik\Tracker\TrackerCodeGenerator; @@ -50,8 +50,8 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $view = new View('@SitesManager/measurable_type_settings'); - $propSettings = new MeasurableSettings($idSite, $idType); - $view->settings = $propSettings->getSettingsForCurrentUser(); +// $propSettings = new MeasurableSettings($idSite, $idType); + $view->settings = array(); return $view->render(); } diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php index 5ebc5898f6..ee77874aa2 100644 --- a/plugins/SitesManager/SitesManager.php +++ b/plugins/SitesManager/SitesManager.php @@ -14,6 +14,7 @@ use Piwik\Container\StaticContainer; use Piwik\Db; use Piwik\Plugins\PrivacyManager\PrivacyManager; use Piwik\Measurable\Settings\Storage; +use Piwik\Settings\Storage\Backend\MeasurableSettingsTable; use Piwik\Tracker\Cache; use Piwik\Tracker\Model as TrackerModel; @@ -73,8 +74,7 @@ class SitesManager extends \Piwik\Plugin $archiveInvalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); $archiveInvalidator->forgetRememberedArchivedReportsToInvalidateForSite($idSite); - $measurableStorage = new Storage(Db::get(), $idSite); - $measurableStorage->deleteAllValues(); + MeasurableSettingsTable::removeAllSettingsForSite($idSite); } /** diff --git a/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js b/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js index 0fac17cc22..a88b34b969 100644 --- a/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js +++ b/plugins/SitesManager/angularjs/sites-manager/sites-manager-site.controller.js @@ -7,9 +7,9 @@ (function () { angular.module('piwikApp').controller('SitesManagerSiteController', SitesManagerSiteController); - SitesManagerSiteController.$inject = ['$scope', '$filter', 'sitesManagerApiHelper', 'sitesManagerTypeModel']; + SitesManagerSiteController.$inject = ['$scope', '$filter', 'sitesManagerApiHelper', 'sitesManagerTypeModel', 'piwikApi']; - function SitesManagerSiteController($scope, $filter, sitesManagerApiHelper, sitesManagerTypeModel) { + function SitesManagerSiteController($scope, $filter, sitesManagerApiHelper, sitesManagerTypeModel, piwikApi) { var translate = $filter('translate'); @@ -18,11 +18,19 @@ initModel(); initActions(); + $scope.site.isLoading = true; sitesManagerTypeModel.fetchTypeById($scope.site.type).then(function (type) { + $scope.site.isLoading = false; + if (type) { $scope.currentType = type; $scope.howToSetupUrl = type.howToSetupUrl; $scope.isInternalSetupUrl = '?' === ('' + type.howToSetupUrl).substr(0, 1); + $scope.typeSettings = type.settings; + + if (isSiteNew()) { + $scope.measurableSettings = angular.copy(type.settings); + } } else { $scope.currentType = {name: $scope.site.type}; } @@ -39,27 +47,30 @@ var initModel = function() { - if(siteIsNew()) + if (isSiteNew()) { initNewSite(); - else - initExistingSite(); + } else { + $scope.site.excluded_ips = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_ips); + $scope.site.excluded_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_parameters); + $scope.site.excluded_user_agents = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_user_agents); + $scope.site.sitesearch_keyword_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.sitesearch_keyword_parameters); + $scope.site.sitesearch_category_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.sitesearch_category_parameters); + } - $scope.site.editDialog = {}; $scope.site.removeDialog = {}; }; var editSite = function () { + $scope.site.editMode = true; - if ($scope.siteIsBeingEdited) { - - $scope.site.editDialog.show = true; - $scope.site.editDialog.title = translate('SitesManager_OnlyOneSiteAtTime', '"' + $scope.lookupCurrentEditSite().name + '"'); - - } else { - - $scope.site.editMode = true; - $scope.informSiteIsBeingEdited(); - } + $scope.measurableSettings = []; + $scope.site.isLoading = true; + piwikApi.fetch({method: 'SitesManager.getSiteSettings', idSite: $scope.site.idsite}).then(function (settings) { + $scope.measurableSettings = settings; + $scope.site.isLoading = false; + }, function () { + $scope.site.isLoading = false; + }); }; var saveSite = function() { @@ -67,106 +78,96 @@ var sendSiteSearchKeywordParams = $scope.site.sitesearch == '1' && !$scope.site.useDefaultSiteSearchParams; var sendSearchCategoryParameters = sendSiteSearchKeywordParams && $scope.customVariablesActivated; - var ajaxHandler = new ajaxHelper(); - ajaxHandler.addParams({ - module: 'API', - format: 'json' - }, 'GET'); - - if(siteIsNew()) { - - ajaxHandler.addParams({ - method: 'SitesManager.addSite' - }, 'GET'); + var values = { + siteName: $scope.site.name, + timezone: $scope.site.timezone, + currency: $scope.site.currency, + type: $scope.site.type, + settingValues: {} + }; - } else { + var isNewSite = isSiteNew(); - ajaxHandler.addParams({ - idSite: $scope.site.idsite, - method: 'SitesManager.updateSite' - }, 'GET'); + var apiMethod = 'SitesManager.addSite'; + if (!isNewSite) { + apiMethod = 'SitesManager.updateSite'; + values.idSite = $scope.site.idsite; } - var settings = $('.typeSettings fieldset').serializeArray(); + angular.forEach($scope.measurableSettings, function (settings) { + if (!values['settingValues'][settings.pluginName]) { + values['settingValues'][settings.pluginName] = []; + } - var flatSettings = ''; - if (settings.length) { - flatSettings = {}; - angular.forEach(settings, function (setting) { - flatSettings[setting.name] = setting.value; + angular.forEach(settings.settings, function (setting) { + var value = setting.value; + if (value === false) { + value = '0'; + } else if (value === true) { + value = '1'; + } + if (angular.isArray(value) && setting.uiControl == 'textarea') { + var newValue = []; + angular.forEach(value, function (val) { + // as they are line separated we cannot trim them in the view + if (val) { + newValue.push(val); + } + }); + value = newValue; + } + + values['settingValues'][settings.pluginName].push({ + name: setting.name, + value: value + }); }); - } + }); - ajaxHandler.addParams({ - siteName: $scope.site.name, - timezone: $scope.site.timezone, - currency: $scope.site.currency, - ecommerce: $scope.site.ecommerce, - excludedIps: $scope.site.excluded_ips.join(','), - excludedQueryParameters: $scope.site.excluded_parameters.join(','), - excludedUserAgents: $scope.site.excluded_user_agents.join(','), - keepURLFragments: $scope.site.keep_url_fragment, - siteSearch: $scope.site.sitesearch, - type: $scope.site.type, - searchKeywordParameters: sendSiteSearchKeywordParams ? $scope.site.sitesearch_keyword_parameters.join(',') : null, - searchCategoryParameters: sendSearchCategoryParameters ? $scope.site.sitesearch_category_parameters.join(',') : null, - urls: $scope.site.alias_urls, - excludeUnknownUrls: $scope.site.exclude_unknown_urls, - settings: flatSettings - }, 'POST'); + piwikApi.post({method: apiMethod}, values).then(function (response) { + $scope.site.editMode = false; - ajaxHandler.redirectOnSuccess($scope.redirectParams); - ajaxHandler.setLoadingElement(); - ajaxHandler.send(true); + var UI = require('piwik/UI'); + var notification = new UI.Notification(); + + var message = 'Website updated'; + if (isNewSite) { + message = 'Website created'; + } + + notification.show(message, {context: 'success', id: 'websitecreated'}); + notification.scrollToNotification(); + + if (!$scope.site.idsite && response && response.value) { + $scope.site.idsite = response.value; + } + + angular.forEach(values.settingValues, function (settings, pluginName) { + angular.forEach(settings, function (setting) { + if (setting.name === 'urls') { + $scope.site.alias_urls = setting.value; + } else { + $scope.site[setting.name] = setting.value; + } + }); + }); + }); }; - var siteIsNew = function() { + var isSiteNew = function() { return angular.isUndefined($scope.site.idsite); }; var initNewSite = function() { - - $scope.informSiteIsBeingEdited(); - $scope.site.editMode = true; $scope.site.name = "Name"; - $scope.site.alias_urls = [ - "http://siteUrl.com/", - "http://siteUrl2.com/" - ]; - $scope.site.exclude_unknown_urls = 0; - $scope.site.keep_url_fragment = 0; - $scope.site.excluded_ips = []; - $scope.site.excluded_parameters = []; - $scope.site.excluded_user_agents = []; - $scope.site.sitesearch_keyword_parameters = []; - $scope.site.sitesearch_category_parameters = []; - $scope.site.sitesearch = $scope.globalSettings.searchKeywordParametersGlobal.length ? 1 : 0; $scope.site.timezone = $scope.globalSettings.defaultTimezone; $scope.site.currency = $scope.globalSettings.defaultCurrency; - $scope.site.ecommerce = 0; - - updateSiteWithSiteSearchConfig(); - }; - var initExistingSite = function() { - - $scope.site.keep_url_fragment = parseInt($scope.site.keep_url_fragment, 10); - $scope.site.ecommerce = parseInt($scope.site.ecommerce, 10); - $scope.site.sitesearch = parseInt($scope.site.sitesearch, 10); - $scope.site.excluded_ips = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_ips); - $scope.site.excluded_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_parameters); - $scope.site.excluded_user_agents = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.excluded_user_agents); - $scope.site.sitesearch_keyword_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.sitesearch_keyword_parameters); - $scope.site.sitesearch_category_parameters = sitesManagerApiHelper.commaDelimitedFieldToArray($scope.site.sitesearch_category_parameters); - - updateSiteWithSiteSearchConfig(); - }; - - var updateSiteWithSiteSearchConfig = function() { - - $scope.site.useDefaultSiteSearchParams = - $scope.globalSettings.searchKeywordParametersGlobal.length && !$scope.site.sitesearch_keyword_parameters.length; + if ($scope.typeSettings) { + // we do not want to manipulate initial type settings + $scope.measurableSettings = angular.copy($scope.typeSettings); + } }; var openDeleteDialog = function() { diff --git a/plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js b/plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js index 66c27a7703..1236243d2a 100644 --- a/plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js +++ b/plugins/SitesManager/angularjs/sites-manager/sites-manager.controller.js @@ -20,7 +20,6 @@ $scope.adminSites = adminSites; $scope.hasSuperUserAccess = piwik.hasSuperUserAccess; $scope.redirectParams = {showaddsite: false}; - $scope.siteIsBeingEdited = false; $scope.cacheBuster = piwik.cacheBuster; $scope.totalNumberOfSites = '?'; @@ -40,8 +39,6 @@ $scope.addSite = addSite; $scope.addNewEntity = addNewEntity; $scope.saveGlobalSettings = saveGlobalSettings; - - $scope.informSiteIsBeingEdited = informSiteIsBeingEdited; $scope.lookupCurrentEditSite = lookupCurrentEditSite; $scope.closeAddMeasurableDialog = function () { @@ -62,15 +59,8 @@ }); }; - var informSiteIsBeingEdited = function() { - - $scope.siteIsBeingEdited = true; - }; - var initSelectLists = function() { - initSiteSearchSelectOptions(); - initEcommerceSelectOptions(); initCurrencyList(); initTimezones(); }; @@ -115,14 +105,6 @@ addNewEntity(); }; - var initEcommerceSelectOptions = function() { - - $scope.eCommerceptions = [ - {key: 0, value: translate('SitesManager_NotAnEcommerceSite')}, - {key: 1, value: translate('SitesManager_EnableEcommerce')} - ]; - }; - var initUtcTime = function() { var currentDate = new Date(); @@ -186,14 +168,6 @@ }); }; - var initSiteSearchSelectOptions = function() { - - $scope.siteSearchOptions = [ - {key: 1, value: translate('SitesManager_EnableSiteSearch')}, - {key: 0, value: translate('SitesManager_DisableSiteSearch')} - ]; - }; - var initKeepURLFragmentsList = function() { $scope.keepURLFragmentsOptions = [ {key: 0, value: ($scope.globalSettings.keepURLFragmentsGlobal ? translate('General_Yes') : translate('General_No')) + ' (' + translate('General_Default') + ')'}, @@ -248,9 +222,17 @@ ajaxHandler.send(true); }; - var cancelEditSite = function ($event) { - $event.stopPropagation(); - piwik.helper.redirect($scope.redirectParams); + var cancelEditSite = function (site) { + site.editMode = false; + + var idSite = site.idsite; + if (idSite) { + var siteElement = $('.site[idsite=' + idSite + ']'); + if (siteElement[0]) { + // todo move this into a directive + siteElement[0].scrollIntoView(); + } + } }; var lookupCurrentEditSite = function () { diff --git a/plugins/SitesManager/templates/dialogs/dialogs.html b/plugins/SitesManager/templates/dialogs/dialogs.html index 2f3d5d8072..e50578ebbc 100644 --- a/plugins/SitesManager/templates/dialogs/dialogs.html +++ b/plugins/SitesManager/templates/dialogs/dialogs.html @@ -1,2 +1 @@ -<div ng-include="'plugins/SitesManager/templates/dialogs/edit-dialog.html'"></div> <div ng-include="'plugins/SitesManager/templates/dialogs/remove-dialog.html'"></div> diff --git a/plugins/SitesManager/templates/dialogs/edit-dialog.html b/plugins/SitesManager/templates/dialogs/edit-dialog.html deleted file mode 100644 index 1bb6067f47..0000000000 --- a/plugins/SitesManager/templates/dialogs/edit-dialog.html +++ /dev/null @@ -1,5 +0,0 @@ -<div class="ui-confirm" piwik-dialog="site.editDialog.show"> - <h2>{{ site.editDialog.title }}</h2> - - <input role="no" type="button" value="{{ 'General_Ok'|translate }}"/> -</div> diff --git a/plugins/SitesManager/templates/sites-list/site-fields.html b/plugins/SitesManager/templates/sites-list/site-fields.html index 2f0eaa7b76..29b74a48b5 100644 --- a/plugins/SitesManager/templates/sites-list/site-fields.html +++ b/plugins/SitesManager/templates/sites-list/site-fields.html @@ -1,4 +1,4 @@ -<div class="site" ng-class="{'editingSite': site.editMode==true}"> +<div class="site" idsite="{{site.idSite}}" ng-class="{'editingSite': site.editMode==true}"> <div class="row" ng-if="!site.editMode"> @@ -71,57 +71,12 @@ <input type="text" ng-model="site.name"/> </div> - <div class="form-group typeSettings" - ng-include="'?module=SitesManager&action=getMeasurableTypeSettings&idSite=' + site.idsite + '&idType=' + site.type" - > - </div> - - <div class="form-group"> - <label>{{ 'SitesManager_Urls'|translate }}</label> - <div class="form-help"> - {{ 'SitesManager_AliasUrlHelp' | translate }} - </div> - <div sites-manager-multiline-field field="site.alias_urls" cols="25" rows="3"></div> + <div piwik-activity-indicator loading="site.isLoading"></div> - <div class="form-help"> - {{ 'SitesManager_OnlyMatchedUrlsAllowedHelp' | translate }} - {{ 'SitesManager_OnlyMatchedUrlsAllowedHelpExamples' | translate }} + <div ng-repeat="settingsPerPlugin in measurableSettings"> + <div ng-repeat="setting in settingsPerPlugin.settings"> + <div piwik-form-field="setting" all-settings="settingsPerPlugin.settings"></div> </div> - <label class="checkbox"> - <input type="checkbox" ng-model="site.exclude_unknown_urls" ng-true-value="1" ng-false-value="0"> {{ 'SitesManager_OnlyMatchedUrlsAllowed' | translate:'':'' }} - </label> - </div> - - <div class="form-group"> - <label>{{ 'SitesManager_KeepURLFragmentsLong'|translate }}</label> - <select ng-options="option.key as option.value for option in keepURLFragmentsOptions" - ng-model="site.keep_url_fragment"></select> - </div> - - <div class="form-group"> - <label>{{ 'SitesManager_ExcludedIps'|translate }}</label> - <div ng-include="'plugins/SitesManager/templates/help/excluded-ip-help.html'"></div> - <div sites-manager-multiline-field field="site.excluded_ips" cols="20" rows="4"></div> - </div> - - <div class="form-group"> - <label>{{ 'SitesManager_ExcludedParameters'|translate }}</label> - <div ng-include="'plugins/SitesManager/templates/help/excluded-query-parameters-help.html'"></div> - <div sites-manager-multiline-field field="site.excluded_parameters" cols="20" rows="4"></div> - </div> - - <div class="form-group" ng-if="globalSettings.siteSpecificUserAgentExcludeEnabled"> - <label>{{ 'SitesManager_ExcludedUserAgents'|translate }}</label> - <div ng-include="'plugins/SitesManager/templates/help/excluded-user-agents-help.html'"></div> - <div sites-manager-multiline-field field="site.excluded_user_agents" cols="20" rows="4"></div> - </div> - - <div ng-include="'plugins/SitesManager/templates/sites-list/site-search-field.html'"></div> - - <div class="form-group"> - <label>{{ 'SitesManager_Timezone'|translate }}</label> - <div ng-include="'plugins/SitesManager/templates/help/timezone-help.html'"></div> - <select ng-model="site.timezone" ng-options="t.code as t.label group by t.group for t in timezones"></select> </div> <div class="form-group"> @@ -133,18 +88,14 @@ </div> <div class="form-group"> - <label>{{ 'Goals_Ecommerce'|translate }}</label> - <div class="form-help"> - {{ 'SitesManager_EcommerceHelp' | translate }} - <br/> - <span ng-bind-html="'SitesManager_PiwikOffersEcommerceAnalytics'|translate:'<a href=\'http://piwik.org/docs/ecommerce-analytics/\' target=\'_blank\'>':'</a>'"></span> - </div> - <select ng-options="option.key as option.value for option in eCommerceptions" ng-model="site.ecommerce"></select> + <label>{{ 'SitesManager_Timezone'|translate }}</label> + <div ng-include="'plugins/SitesManager/templates/help/timezone-help.html'"></div> + <select ng-model="site.timezone" ng-options="t.code as t.label group by t.group for t in timezones"></select> </div> <div class="editingSiteFooter"> - <input type="submit" class="btn" value="{{ 'General_Save' | translate }}" ng-click="saveSite()"/> - <button class="btn btn-link" ng-click="cancelEditSite($event)">{{ 'General_Cancel' | translate:'':'' }}</button> + <input ng-show="!site.isLoading" type="submit" class="btn" value="{{ 'General_Save' | translate }}" ng-click="saveSite()"/> + <button class="btn btn-link" ng-click="cancelEditSite(site)">{{ 'General_Cancel' | translate:'':'' }}</button> </div> </div> diff --git a/plugins/SitesManager/tests/Integration/ApiTest.php b/plugins/SitesManager/tests/Integration/ApiTest.php index 3b26d215cb..ba897a4000 100644 --- a/plugins/SitesManager/tests/Integration/ApiTest.php +++ b/plugins/SitesManager/tests/Integration/ApiTest.php @@ -8,10 +8,13 @@ namespace Piwik\Plugins\SitesManager\tests\Integration; +use Piwik\Container\StaticContainer; +use Piwik\Settings\Measurable\MeasurableSetting; +use Piwik\Settings\Measurable\MeasurableSettings; use Piwik\Piwik; use Piwik\Plugin; use Piwik\Plugins\MobileAppMeasurable; -use Piwik\Plugins\MobileAppMeasurable\tests\Framework\Mock\Type; +use Piwik\Plugins\MobileAppMeasurable\Type; use Piwik\Plugins\SitesManager\API; use Piwik\Plugins\SitesManager\Model; use Piwik\Plugins\UsersManager\API as APIUsersManager; @@ -189,13 +192,13 @@ class ApiTest extends IntegrationTestCase /** * @expectedException \Exception - * @expectedExceptionMessage Only 100 characters are allowed + * @expectedExceptionMessage SitesManager_OnlyMatchedUrlsAllowed */ public function test_addSite_ShouldFailAndNotCreatedASite_IfASettingIsInvalid() { try { $type = MobileAppMeasurable\Type::ID; - $settings = array('app_id' => str_pad('test', 789, 't')); + $settings = array('WebsiteMeasurable' => array(array('name' => 'exclude_unknown_urls', 'value' => 'fooBar'))); $this->addSiteWithType($type, $settings); } catch (Exception $e) { @@ -210,15 +213,24 @@ class ApiTest extends IntegrationTestCase public function test_addSite_ShouldSavePassedMeasurableSettings_IfSettingsAreValid() { $type = MobileAppMeasurable\Type::ID; - $settings = array('app_id' => 'org.piwik.mobile2'); + $settings = array('WebsiteMeasurable' => array(array('name' => 'urls', 'value' => array('http://www.piwik.org')))); $idSite = $this->addSiteWithType($type, $settings); $this->assertSame(1, $idSite); - $measurable = new Measurable($idSite); - $appId = $measurable->getSettingValue('app_id'); + $settings = $this->getWebsiteMeasurable($idSite); + $urls = $settings->urls->getValue(); + + $this->assertSame(array('http://www.piwik.org'), $urls); + } - $this->assertSame('org.piwik.mobile2', $appId); + /** + * @return \Piwik\Plugins\WebsiteMeasurable\MeasurableSettings + */ + private function getWebsiteMeasurable($idSite) + { + $settings = StaticContainer::get('Piwik\Plugin\SettingsProvider'); + return $settings->getMeasurableSettings('WebsiteMeasurable', $idSite, null); } /** @@ -642,6 +654,7 @@ class ApiTest extends IntegrationTestCase FakeAccess::setIdSitesAdmin(array()); $sites = API::getInstance()->getSitesWithViewAccess(); + // we dont test the ts_created unset($sites[0]['ts_created']); unset($sites[1]['ts_created']); @@ -753,6 +766,7 @@ class ApiTest extends IntegrationTestCase // Updating the group to something $group = 'something'; API::getInstance()->updateSite($idsite, "test toto@{}", $newMainUrl, $ecommerce = 0, $ss = true, $ss_kwd = null, $ss_cat = '', $ips = null, $parametersExclude = null, $timezone = null, $currency = null, $group); + $websites = API::getInstance()->getSitesFromGroup($group); $this->assertEquals(1, count($websites)); $this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($websites[0]['ts_created']))); @@ -844,7 +858,7 @@ class ApiTest extends IntegrationTestCase /** * @expectedException \Exception - * @expectedExceptionMessage Only 100 characters are allowed + * @expectedExceptionMessage SitesManager_OnlyMatchedUrlsAllowed */ public function test_updateSite_ShouldFailAndNotUpdateSite_IfASettingIsInvalid() { @@ -852,7 +866,8 @@ class ApiTest extends IntegrationTestCase $idSite = $this->addSiteWithType($type, array()); try { - $this->updateSiteSettings($idSite, 'newSiteName', array('app_id' => str_pad('t', 589, 't'))); + $settings = array('WebsiteMeasurable' => array(array('name' => 'exclude_unknown_urls', 'value' => 'fooBar'))); + $this->updateSiteSettings($idSite, 'newSiteName', $settings); } catch (Exception $e) { // verify nothing was updated (not even the name) @@ -870,12 +885,16 @@ class ApiTest extends IntegrationTestCase $this->assertSame(1, $idSite); - $this->updateSiteSettings($idSite, 'newSiteName', $settings = array('app_id' => 'org.piwik.mobile2')); + $settings = array('WebsiteMeasurable' => array(array('name' => 'urls', 'value' => array('http://www.piwik.org')))); + + $this->updateSiteSettings($idSite, 'newSiteName', $settings); + + $settings = $this->getWebsiteMeasurable($idSite); // verify it was updated $measurable = new Measurable($idSite); $this->assertSame('newSiteName', $measurable->getName()); - $this->assertSame('org.piwik.mobile2', $measurable->getSettingValue('app_id')); + $this->assertSame(array('http://www.piwik.org'), $settings->urls->getValue()); } public function test_updateSite_CorreclySavesExcludedUnknownUrlSettings() @@ -1210,7 +1229,6 @@ class ApiTest extends IntegrationTestCase { return array( 'Piwik\Access' => new FakeAccess(), - 'Piwik\Plugins\MobileAppMeasurable\Type' => new Type() ); } diff --git a/plugins/SitesManager/tests/System/ApiTest.php b/plugins/SitesManager/tests/System/ApiTest.php index d4cc198888..393a8fc6a4 100644 --- a/plugins/SitesManager/tests/System/ApiTest.php +++ b/plugins/SitesManager/tests/System/ApiTest.php @@ -59,6 +59,11 @@ class ApiTest extends SystemTestCase 'otherRequestParameters' => array('pattern' => 'SiteTest1') ) ); + $apiToTest[] = array(array('SitesManager.getSiteSettings'), + array( + 'idSite' => 1 + ) + ); return $apiToTest; } diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getSiteSettings.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getSiteSettings.xml new file mode 100644 index 0000000000..6be613478e --- /dev/null +++ b/plugins/SitesManager/tests/System/expected/test_SitesManager__SitesManager.getSiteSettings.xml @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>WebsiteMeasurable</pluginName> + <settings> + <row> + <name>urls</name> + <title>URLs</title> + <value> + <row>http://piwik.net</row> + </value> + <defaultValue> + <row>http://siteUrl.com/</row> + <row>http://siteUrl2.com/</row> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + <cols>25</cols> + <rows>3</rows> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>It is recommended, but not required, to specify the various URLs, one per line, that your visitors use to access this website. Alias URLs for a website will not appear in the Referrers > Websites report. Note that it is not necessary to specify the URLs with and without 'www' as Piwik automatically considers both.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>exclude_unknown_urls</name> + <title>Only track visits and actions when the action URL starts with one of the above URLs.</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>When enabled, Piwik will only track internal actions when the Page URL is one of the known URLs for your website. This prevents people from spamming your analytics with URLs for other websites.<br />The domain and the path has to be an exact match and each valid subdomain has to be specified separately. For example when the known URLs are 'http://example.com/path' and 'http://good.example.com', tracking requests for 'http://example.com/otherpath' or 'http://bad.example.com' are ignored.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>keep_url_fragment</name> + <title>Keep Page URL fragments when tracking Page URLs</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>string</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <row>No (Default)</row> + <row>Yes</row> + <row>No</row> + </availableValues> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>excluded_ips</name> + <title>Excluded IPs</title> + <value> + <row/> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + <cols>20</cols> + <rows>4</rows> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Enter the list of IPs, one per line, that you wish to exclude from being tracked by Piwik. You can use wildcards, eg. 1.2.3.* or 1.2.*.*<br /><br />Your current IP address is <i>127.0.0.1</i></inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>excluded_parameters</name> + <title>Excluded Parameters</title> + <value> + <row/> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + <cols>20</cols> + <rows>4</rows> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Enter the list of URL Query Parameters, one per line, to exclude from the Page URLs reports.<br /><br />Piwik will automatically exclude the common session parameters (phpsessid, sessionid, ...).</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>excluded_user_agents</name> + <title>Excluded User Agents</title> + <value> + <row/> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + <cols>20</cols> + <rows>4</rows> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Enter the list of user agents to exclude from being tracked by Piwik.<br /><br />If the visitor's user agent string contains any of the strings you specify, the visitor will be excluded from Piwik.<br />You can use this to exclude some bots from being tracked.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>sitesearch</name> + <title>Site Search</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>integer</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <row key="1">Site Search tracking enabled</row> + <row key="0">Do not track Site Search</row> + </availableValues> + <description /> + <inlineHelp>You can use Piwik to track and report what visitors are searching in your website's internal search engine.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>use_default_site_search_params</name> + <title>Use <a href='#globalSettings'>default</a> Site Search parameters</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>Query parameter (Default): q,query,s,search,searchword,k,keyword & Category parameter: </description> + <inlineHelp /> + <introduction /> + <condition>1 && sitesearch</condition> + </row> + <row> + <name>sitesearch_keyword_parameters</name> + <title>Query parameter</title> + <value> + <row/> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>text</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Enter a comma separated list of all query parameter names containing the site search keyword.</inlineHelp> + <introduction /> + <condition>sitesearch && !use_default_site_search_params</condition> + </row> + <row> + <name>sitesearch_category_parameters</name> + <title>Category parameter</title> + <value> + <row/> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>text</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>(optional)<br /><br />You may enter a comma-separated list of query parameters specifying the search category.</inlineHelp> + <introduction /> + <condition>1 && sitesearch && !use_default_site_search_params</condition> + </row> + <row> + <name>ecommerce</name> + <title>Ecommerce</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <row>Not an Ecommerce site</row> + <row>Ecommerce enabled</row> + </availableValues> + <description /> + <inlineHelp>When enabled, the "Goals" report will have a new "Ecommerce" section.<br />Piwik allows for advanced Ecommerce Analytics tracking & reporting. Learn more about <a href='http://piwik.org/docs/ecommerce-analytics/' target='_blank'> Ecommerce Analytics</a>.</inlineHelp> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>contact_email</name> + <title>Contact email addresses</title> + <value> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/plugins/UserLanguage/Reports/GetLanguage.php b/plugins/UserLanguage/Reports/GetLanguage.php index 8db3629134..ef570b229f 100644 --- a/plugins/UserLanguage/Reports/GetLanguage.php +++ b/plugins/UserLanguage/Reports/GetLanguage.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\UserLanguage\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\UserLanguage\Columns\Language; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetLanguage extends Base { @@ -37,7 +37,7 @@ class GetLanguage extends Base public function getRelatedReports() { return array( - Reports::factory('UserLanguage', 'getLanguageCode'), + ReportsProvider::factory('UserLanguage', 'getLanguageCode'), ); } diff --git a/plugins/UserLanguage/Reports/GetLanguageCode.php b/plugins/UserLanguage/Reports/GetLanguageCode.php index 9d9a46f201..278bf7a751 100644 --- a/plugins/UserLanguage/Reports/GetLanguageCode.php +++ b/plugins/UserLanguage/Reports/GetLanguageCode.php @@ -10,7 +10,7 @@ namespace Piwik\Plugins\UserLanguage\Reports; use Piwik\Piwik; use Piwik\Plugins\UserLanguage\Columns\Language; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetLanguageCode extends GetLanguage { @@ -26,7 +26,7 @@ class GetLanguageCode extends GetLanguage public function getRelatedReports() { return array( - Reports::factory('UserLanguage', 'getLanguage'), + ReportsProvider::factory('UserLanguage', 'getLanguage'), ); } diff --git a/plugins/UsersManager/templates/userSettings.twig b/plugins/UsersManager/templates/userSettings.twig index 5752807eaf..48d5f47ed9 100644 --- a/plugins/UsersManager/templates/userSettings.twig +++ b/plugins/UsersManager/templates/userSettings.twig @@ -117,18 +117,24 @@ </form> -<h2 id="excludeCookie">{{ 'UsersManager_ExcludeVisitsViaCookie'|translate }}</h2> -<p> - {% if ignoreCookieSet %} - {{ 'UsersManager_YourVisitsAreIgnoredOnDomain'|translate("<strong>", piwikHost, "</strong>")|raw }} - {% else %} - {{ 'UsersManager_YourVisitsAreNotIgnored'|translate("<strong>","</strong>")|raw }} - {% endif %} -</p> -<span style="margin-left:20px;"> + <hr /> + + <h2 piwik-enriched-headline>{{ 'CoreAdminHome_PersonalPluginSettings'|translate }}</h2> + + <div piwik-plugin-settings mode="user"></div> + + <hr /> + <h2 id="excludeCookie">{{ 'UsersManager_ExcludeVisitsViaCookie'|translate }}</h2> + <p> + {% if ignoreCookieSet %} + {{ 'UsersManager_YourVisitsAreIgnoredOnDomain'|translate("<strong>", piwikHost, "</strong>")|raw }} + {% else %} + {{ 'UsersManager_YourVisitsAreNotIgnored'|translate("<strong>","</strong>")|raw }} + {% endif %} + </p> + <span style="margin-left:20px;"> <a href='{{ linkTo({'ignoreSalt':ignoreSalt, 'action':'setIgnoreCookie'}) }}#excludeCookie'>› {% if ignoreCookieSet %}{{ 'UsersManager_ClickHereToDeleteTheCookie'|translate }} {% else %}{{'UsersManager_ClickHereToSetTheCookieOnDomain'|translate(piwikHost) }}{% endif %} <br/> </a></span> - {% endblock %} diff --git a/plugins/VisitTime/Reports/GetByDayOfWeek.php b/plugins/VisitTime/Reports/GetByDayOfWeek.php index a93c6f21ee..2cc5bdc6ad 100644 --- a/plugins/VisitTime/Reports/GetByDayOfWeek.php +++ b/plugins/VisitTime/Reports/GetByDayOfWeek.php @@ -14,7 +14,7 @@ use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; use Piwik\Plugins\VisitTime\Columns\DayOfTheWeek; use Piwik\Period; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Site; class GetByDayOfWeek extends Base @@ -78,7 +78,7 @@ class GetByDayOfWeek extends Base public function getRelatedReports() { return array( - Reports::factory('VisitTime', 'getVisitInformationPerLocalTime') + ReportsProvider::factory('VisitTime', 'getVisitInformationPerLocalTime') ); } } diff --git a/plugins/VisitTime/Reports/GetVisitInformationPerLocalTime.php b/plugins/VisitTime/Reports/GetVisitInformationPerLocalTime.php index 9372875619..5f79fdc6fe 100644 --- a/plugins/VisitTime/Reports/GetVisitInformationPerLocalTime.php +++ b/plugins/VisitTime/Reports/GetVisitInformationPerLocalTime.php @@ -13,7 +13,7 @@ use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; use Piwik\Plugins\VisitTime\Columns\LocalTime; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; class GetVisitInformationPerLocalTime extends Base { @@ -49,7 +49,7 @@ class GetVisitInformationPerLocalTime extends Base public function getRelatedReports() { return array( - Reports::factory('VisitTime', 'getByDayOfWeek') + ReportsProvider::factory('VisitTime', 'getByDayOfWeek') ); } } diff --git a/plugins/VisitsSummary/API.php b/plugins/VisitsSummary/API.php index c46bd93b7b..db0a52ce86 100644 --- a/plugins/VisitsSummary/API.php +++ b/plugins/VisitsSummary/API.php @@ -12,7 +12,7 @@ use Piwik\Archive; use Piwik\Metrics\Formatter; use Piwik\Piwik; use Piwik\Plugin\Report; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\SettingsPiwik; /** @@ -30,7 +30,7 @@ class API extends \Piwik\Plugin\API $requestedColumns = Piwik::getArrayFromApiParameter($columns); - $report = Reports::factory("VisitsSummary", "get"); + $report = ReportsProvider::factory("VisitsSummary", "get"); $columns = $report->getMetricsRequiredForReport($this->getCoreColumns($period), $requestedColumns); $dataTable = $archive->getDataTableFromNumeric($columns); diff --git a/plugins/VisitsSummary/Widgets/Index.php b/plugins/VisitsSummary/Widgets/Index.php index 1b39fad7c6..0de38cfd42 100644 --- a/plugins/VisitsSummary/Widgets/Index.php +++ b/plugins/VisitsSummary/Widgets/Index.php @@ -11,7 +11,7 @@ namespace Piwik\Plugins\VisitsSummary\Widgets; use Piwik\Plugin\Report; use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Report\ReportWidgetFactory; use Piwik\Widget\WidgetsList; @@ -24,12 +24,12 @@ class Index extends \Piwik\Widget\WidgetContainerConfig public function isEnabled() { - return Reports::factory('VisitsSummary', 'get')->isEnabled(); + return ReportsProvider::factory('VisitsSummary', 'get')->isEnabled(); } public function getWidgetConfigs() { - $report = Reports::factory('VisitsSummary', 'get'); + $report = ReportsProvider::factory('VisitsSummary', 'get'); $factory = new ReportWidgetFactory($report); $widgets = array(); diff --git a/plugins/WebsiteMeasurable/MeasurableSettings.php b/plugins/WebsiteMeasurable/MeasurableSettings.php new file mode 100644 index 0000000000..625007238f --- /dev/null +++ b/plugins/WebsiteMeasurable/MeasurableSettings.php @@ -0,0 +1,314 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\WebsiteMeasurable; +use Piwik\IP; +use Piwik\Network\IPUtils; +use Piwik\Piwik; +use Piwik\Plugin; +use Piwik\Plugins\WebsiteMeasurable\Settings\Urls; +use Piwik\Settings\Setting; +use Piwik\Settings\FieldConfig; +use Piwik\Plugins\SitesManager; +use Exception; + +/** + * Defines Settings for ExampleSettingsPlugin. + * + * Usage like this: + * $settings = new MeasurableSettings($idSite); + * $settings->autoRefresh->getValue(); + * $settings->metric->getValue(); + */ +class MeasurableSettings extends \Piwik\Settings\Measurable\MeasurableSettings +{ + /** @var Setting */ + public $urls; + + /** @var Setting */ + public $onlyTrackWhitelstedUrls; + + /** @var Setting */ + public $keepPageUrlFragments; + + /** @var Setting */ + public $excludeKnownUrls; + + /** @var Setting */ + public $excludedUserAgents; + + /** @var Setting */ + public $excludedIps; + + /** @var Setting */ + public $siteSearch; + + /** @var Setting */ + public $useDefaultSiteSearchParams; + + /** @var Setting */ + public $siteSearchKeywords; + + /** @var Setting */ + public $siteSearchCategory; + + /** @var Setting */ + public $excludedParameters; + + /** @var Setting */ + public $ecommerce; + + /** + * @var SitesManager\API + */ + private $sitesManagerApi; + + /** + * @var Plugin\Manager + */ + private $pluginManager; + + public function __construct(SitesManager\API $api, Plugin\Manager $pluginManager, $idSite, $idMeasurableType) + { + $this->sitesManagerApi = $api; + $this->pluginManager = $pluginManager; + + parent::__construct($idSite, $idMeasurableType); + } + + protected function init() + { + $this->urls = new Urls($this->idSite); + $this->addSetting($this->urls); + + $this->excludeKnownUrls = $this->makeExcludeUnknownUrls(); + $this->keepPageUrlFragments = $this->makeKeepUrlFragments($this->sitesManagerApi); + $this->excludedIps = $this->makeExcludeIps(); + $this->excludedParameters = $this->makeExcludedParameters(); + $this->excludedUserAgents = $this->makeExcludedUserAgents(); + + /** + * SiteSearch + */ + $this->siteSearch = $this->makeSiteSearch(); + $this->useDefaultSiteSearchParams = $this->makeUseDefaultSiteSearchParams($this->sitesManagerApi); + $this->siteSearchKeywords = $this->makeSiteSearchKeywords(); + + $siteSearchKeywords = $this->siteSearchKeywords->getValue(); + $this->useDefaultSiteSearchParams->setDefaultValue(empty($siteSearchKeywords)); + + $this->siteSearchCategory = $this->makeSiteSearchCategory($this->pluginManager); + /** + * SiteSearch End + */ + + $this->ecommerce = $this->makeEcommerce(); + } + + private function makeExcludeUnknownUrls() + { + return $this->makeProperty('exclude_unknown_urls', $default = false, FieldConfig::TYPE_BOOL, function (FieldConfig $field) { + $field->title = Piwik::translate('SitesManager_OnlyMatchedUrlsAllowed'); + $field->inlineHelp = Piwik::translate('SitesManager_OnlyMatchedUrlsAllowedHelp') + . '<br />' + . Piwik::translate('SitesManager_OnlyMatchedUrlsAllowedHelpExamples'); + $field->uiControl = FieldConfig::UI_CONTROL_CHECKBOX; + }); + } + + private function makeKeepUrlFragments(SitesManager\API $sitesManagerApi) + { + return $this->makeProperty('keep_url_fragment', $default = '0', FieldConfig::TYPE_STRING, function (FieldConfig $field) use ($sitesManagerApi) { + $field->title = Piwik::translate('SitesManager_KeepURLFragmentsLong'); + $field->uiControl = FieldConfig::UI_CONTROL_SINGLE_SELECT; + + if ($sitesManagerApi->getKeepURLFragmentsGlobal()) { + $default = Piwik::translate('General_Yes'); + } else { + $default = Piwik::translate('General_No'); + } + + $field->availableValues = array( + '0' => $default . ' (' . Piwik::translate('General_Default') . ')', + '1' => Piwik::translate('General_Yes'), + '2' => Piwik::translate('General_No') + ); + }); + } + + private function makeExcludeIps() + { + return $this->makeProperty('excluded_ips', $default = array(), FieldConfig::TYPE_ARRAY, function (FieldConfig $field) { + $ip = IP::getIpFromHeader(); + + $field->title = Piwik::translate('SitesManager_ExcludedIps'); + $field->inlineHelp = Piwik::translate('SitesManager_HelpExcludedIps', array('1.2.3.*', '1.2.*.*')) + . '<br /><br />' + . Piwik::translate('SitesManager_YourCurrentIpAddressIs', array('<i>' . $ip . '</i>')); + $field->uiControl = FieldConfig::UI_CONTROL_TEXTAREA; + $field->uiControlAttributes = array('cols' => '20', 'rows' => '4'); + + $field->validate = function ($value) { + if (!empty($value)) { + $ips = array_map('trim', $value); + $ips = array_filter($ips, 'strlen'); + + foreach ($ips as $ip) { + if (IPUtils::getIPRangeBounds($ip) === null) { + throw new Exception(Piwik::translate('SitesManager_ExceptionInvalidIPFormat', array($ip, "1.2.3.4, 1.2.3.*, or 1.2.3.4/5"))); + } + } + } + }; + $field->transform = function ($value) { + if (empty($value)) { + return array(); + } + + $ips = array_map('trim', $value); + $ips = array_filter($ips, 'strlen'); + return $ips; + }; + }); + } + + private function makeExcludedParameters() + { + $self = $this; + return $this->makeProperty('excluded_parameters', $default = array(), FieldConfig::TYPE_ARRAY, function (FieldConfig $field) use ($self) { + $field->title = Piwik::translate('SitesManager_ExcludedParameters'); + $field->inlineHelp = Piwik::translate('SitesManager_ListOfQueryParametersToExclude') + . '<br /><br />' + . Piwik::translate('SitesManager_PiwikWillAutomaticallyExcludeCommonSessionParameters', array('phpsessid, sessionid, ...')); + $field->uiControl = FieldConfig::UI_CONTROL_TEXTAREA; + $field->uiControlAttributes = array('cols' => '20', 'rows' => '4'); + $field->transform = function ($value) use ($self) { + return $self->checkAndReturnCommaSeparatedStringList($value); + }; + }); + } + + private function makeExcludedUserAgents() + { + $self = $this; + return $this->makeProperty('excluded_user_agents', $default = array(), FieldConfig::TYPE_ARRAY, function (FieldConfig $field) use ($self) { + $field->title = Piwik::translate('SitesManager_ExcludedUserAgents'); + $field->inlineHelp = Piwik::translate('SitesManager_GlobalExcludedUserAgentHelp1') + . '<br /><br />' + . Piwik::translate('SitesManager_GlobalListExcludedUserAgents_Desc') + . '<br />' + . Piwik::translate('SitesManager_GlobalExcludedUserAgentHelp2'); + $field->uiControl = FieldConfig::UI_CONTROL_TEXTAREA; + $field->uiControlAttributes = array('cols' => '20', 'rows' => '4'); + $field->transform = function ($value) use ($self) { + return $self->checkAndReturnCommaSeparatedStringList($value); + }; + }); + } + + private function makeSiteSearch() + { + return $this->makeProperty('sitesearch', $default = 1, FieldConfig::TYPE_INT, function (FieldConfig $field) { + $field->title = Piwik::translate('Actions_SubmenuSitesearch'); + $field->inlineHelp = Piwik::translate('SitesManager_SiteSearchUse'); + $field->uiControl = FieldConfig::UI_CONTROL_SINGLE_SELECT; + $field->availableValues = array( + 1 => Piwik::translate('SitesManager_EnableSiteSearch'), + 0 => Piwik::translate('SitesManager_DisableSiteSearch') + ); + }); + } + + private function makeUseDefaultSiteSearchParams(SitesManager\API $sitesManagerApi) + { + return $this->makeSetting('use_default_site_search_params', $default = true, FieldConfig::TYPE_BOOL, function (FieldConfig $field) use ($sitesManagerApi) { + + if (Piwik::hasUserSuperUserAccess()) { + $title = Piwik::translate('SitesManager_SearchUseDefault', array("<a href='#globalSettings'>","</a>")); + } else { + $title = Piwik::translate('SitesManager_SearchUseDefault', array('', '')); + } + + $field->title = $title; + $field->uiControl = FieldConfig::UI_CONTROL_CHECKBOX; + + $searchKeywordsGlobal = $sitesManagerApi->getSearchKeywordParametersGlobal(); + + $hasParams = (int) !empty($searchKeywordsGlobal); + $field->condition = $hasParams . ' && sitesearch'; + + $searchKeywordsGlobal = $sitesManagerApi->getSearchKeywordParametersGlobal(); + $searchCategoryGlobal = $sitesManagerApi->getSearchCategoryParametersGlobal(); + + $field->description = Piwik::translate('SitesManager_SearchKeywordLabel'); + $field->description .= ' (' . Piwik::translate('General_Default') . ')'; + $field->description .= ': '; + $field->description .= $searchKeywordsGlobal; + $field->description .= ' & '; + $field->description .= Piwik::translate('SitesManager_SearchCategoryLabel'); + $field->description .= ': '; + $field->description .= $searchCategoryGlobal; + $field->transform = function () { + return null;// never actually save a value for this + }; + }); + } + + private function makeSiteSearchKeywords() + { + return $this->makeProperty('sitesearch_keyword_parameters', $default = array(), FieldConfig::TYPE_ARRAY, function (FieldConfig $field) { + $field->title = Piwik::translate('SitesManager_SearchKeywordLabel'); + $field->uiControl = FieldConfig::UI_CONTROL_TEXT; + $field->inlineHelp = Piwik::translate('SitesManager_SearchKeywordParametersDesc'); + $field->condition = Piwik::translate('sitesearch && !use_default_site_search_params'); + }); + } + + private function makeSiteSearchCategory(Plugin\Manager $pluginManager) + { + return $this->makeProperty('sitesearch_category_parameters', $default = array(), FieldConfig::TYPE_ARRAY, function (FieldConfig $field) use ($pluginManager) { + $field->title = Piwik::translate('SitesManager_SearchCategoryLabel'); + $field->uiControl = FieldConfig::UI_CONTROL_TEXT; + $field->inlineHelp = Piwik::translate('Goals_Optional') + . '<br /><br />' + . Piwik::translate('SitesManager_SearchCategoryParametersDesc'); + + $hasCustomVars = (int) $pluginManager->isPluginActivated('CustomVariables'); + $field->condition = $hasCustomVars . ' && sitesearch && !use_default_site_search_params'; + }); + } + + private function makeEcommerce() + { + return $this->makeProperty('ecommerce', $default = 0, FieldConfig::TYPE_INT, function (FieldConfig $field) { + $field->title = Piwik::translate('Goals_Ecommerce'); + $field->inlineHelp = Piwik::translate('SitesManager_EcommerceHelp') + . '<br />' + . Piwik::translate('SitesManager_PiwikOffersEcommerceAnalytics', + array("<a href='http://piwik.org/docs/ecommerce-analytics/' target='_blank'>", '</a>')); + $field->uiControl = FieldConfig::UI_CONTROL_SINGLE_SELECT; + $field->availableValues = array( + 0 => Piwik::translate('SitesManager_NotAnEcommerceSite'), + 1 => Piwik::translate('SitesManager_EnableEcommerce') + ); + }); + } + + public function checkAndReturnCommaSeparatedStringList($parameters) + { + if (empty($parameters)) { + return array(); + } + + $parameters = array_map('trim', $parameters); + $parameters = array_filter($parameters, 'strlen'); + $parameters = array_unique($parameters); + return $parameters; + } + +} diff --git a/plugins/WebsiteMeasurable/Settings/Urls.php b/plugins/WebsiteMeasurable/Settings/Urls.php new file mode 100644 index 0000000000..3aa023c1e0 --- /dev/null +++ b/plugins/WebsiteMeasurable/Settings/Urls.php @@ -0,0 +1,141 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\WebsiteMeasurable\Settings; +use Piwik\Common; +use Piwik\Piwik; +use Piwik\Plugin; +use Piwik\Settings\FieldConfig; +use Piwik\Plugins\SitesManager; +use Exception; +use Piwik\UrlHelper; + +class Urls extends \Piwik\Settings\Measurable\MeasurableProperty +{ + + public function __construct($idSite) + { + $name = 'urls'; + $pluginName = 'WebsiteMeasurable'; + $defaultValue = array('http://siteUrl.com/', 'http://siteUrl2.com/'); + $type = FieldConfig::TYPE_ARRAY; + + parent::__construct($name, $defaultValue, $type, $pluginName, $idSite); + } + + public function configureField() + { + if ($this->config) { + return $this->config; + } + + $config = new FieldConfig(); + $config->title = Piwik::translate('SitesManager_Urls'); + $config->inlineHelp = Piwik::translate('SitesManager_AliasUrlHelp'); + $config->uiControl = FieldConfig::UI_CONTROL_TEXTAREA; + $config->uiControlAttributes = array('cols' => '25', 'rows' => '3'); + + $self = $this; + $config->validate = function ($urls) use ($self) { + $self->checkUrls($urls); + $self->checkAtLeastOneUrl($urls); + }; + + $config->transform = function ($urls) use ($self) { + return $this->cleanParameterUrls($urls); + }; + + $this->config = $config; + return $this->config; + } + + /** + * Checks that the array has at least one element + * + * @param array $urls + * @throws Exception + */ + public function checkAtLeastOneUrl($urls) + { + $urls = $this->cleanParameterUrls($urls); + + if (!is_array($urls) + || count($urls) == 0 + ) { + throw new Exception(Piwik::translate('SitesManager_ExceptionNoUrl')); + } + } + + /** + * Check that the array of URLs are valid URLs + * + * @param array $urls + * @throws Exception if any of the urls is not valid + */ + public function checkUrls($urls) + { + $urls = $this->cleanParameterUrls($urls); + + foreach ($urls as $url) { + if (!UrlHelper::isLookLikeUrl($url)) { + throw new Exception(sprintf(Piwik::translate('SitesManager_ExceptionInvalidUrl'), $url)); + } + } + } + + /** + * Clean the parameter URLs: + * - if the parameter is a string make it an array + * - remove the trailing slashes if found + * + * @param string|array urls + * @return array the array of cleaned URLs + */ + public function cleanParameterUrls($urls) + { + if (!is_array($urls)) { + $urls = array($urls); + } + + $urls = array_filter($urls); + $urls = array_map('urldecode', $urls); + + foreach ($urls as &$url) { + $url = $this->removeTrailingSlash($url); + $scheme = parse_url($url, PHP_URL_SCHEME); + if (empty($scheme) + && strpos($url, '://') === false + ) { + $url = 'http://' . $url; + } + $url = trim($url); + $url = Common::sanitizeInputValue($url); + } + + $urls = array_unique($urls); + return $urls; + } + + /** + * Remove the final slash in the URLs if found + * + * @param string $url + * @return string the URL without the trailing slash + */ + private function removeTrailingSlash($url) + { + // if there is a final slash, we take the URL without this slash (expected URL format) + if (strlen($url) > 5 + && $url[strlen($url) - 1] == '/' + ) { + $url = substr($url, 0, strlen($url) - 1); + } + + return $url; + } +} diff --git a/plugins/WebsiteMeasurable/Type.php b/plugins/WebsiteMeasurable/Type.php index 714b9dd580..0552f47103 100644 --- a/plugins/WebsiteMeasurable/Type.php +++ b/plugins/WebsiteMeasurable/Type.php @@ -15,5 +15,6 @@ class Type extends \Piwik\Measurable\Type protected $namePlural = 'SitesManager_Sites'; // translated into more languages protected $description = 'WebsiteMeasurable_WebsiteDescription'; protected $howToSetupUrl = '?module=CoreAdminHome&action=trackingCodeGenerator'; + } diff --git a/tests/PHPUnit/Framework/Fixture.php b/tests/PHPUnit/Framework/Fixture.php index 9ef26bbb67..736ffad5fe 100644 --- a/tests/PHPUnit/Framework/Fixture.php +++ b/tests/PHPUnit/Framework/Fixture.php @@ -342,6 +342,7 @@ class Fixture extends \PHPUnit_Framework_Assert self::unloadAllPlugins(); + if ($this->dropDatabaseInTearDown) { $this->dropDatabase(); } @@ -368,6 +369,7 @@ class Fixture extends \PHPUnit_Framework_Assert Singleton::clearAll(); PluginsArchiver::$archivers = array(); + Plugin\API::unsetAllInstances(); $_GET = $_REQUEST = array(); Translate::reset(); diff --git a/tests/PHPUnit/Framework/Mock/Settings/FakeBackend.php b/tests/PHPUnit/Framework/Mock/Settings/FakeBackend.php new file mode 100644 index 0000000000..f57a90d159 --- /dev/null +++ b/tests/PHPUnit/Framework/Mock/Settings/FakeBackend.php @@ -0,0 +1,43 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Framework\Mock\Settings; + +use Piwik\Settings\Storage\Backend\BackendInterface; + +class FakeBackend implements BackendInterface +{ + private $storageId; + + private $data = array('field1' => 'value1', 'field2' => 'value2'); + + public function __construct($storageId) + { + $this->storageId = $storageId; + } + + public function load() + { + return $this->data; + } + + public function getStorageId() + { + return $this->storageId; + } + + public function delete() + { + $this->data = array(); + } + + public function save($values) + { + $this->data = $values; + } +} diff --git a/tests/PHPUnit/Framework/Mock/Settings/FakeMeasurableSettings.php b/tests/PHPUnit/Framework/Mock/Settings/FakeMeasurableSettings.php new file mode 100644 index 0000000000..4fb569b9d8 --- /dev/null +++ b/tests/PHPUnit/Framework/Mock/Settings/FakeMeasurableSettings.php @@ -0,0 +1,36 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Framework\Mock\Settings; + +use Piwik\Settings\Setting; + +class FakeMeasurableSettings extends \Piwik\Plugins\ExampleSettingsPlugin\MeasurableSettings { + protected $pluginName = 'ExampleSettingsPlugin'; + + public function init() + { + + } + + public function makeSetting($name, $defaultValue, $type, $fieldConfigCallback) + { + return parent::makeSetting($name, $defaultValue, $type, $fieldConfigCallback); + } + + public function makeProperty($name, $defaultValue, $type, $configureCallback) + { + return parent::makeProperty($name, $defaultValue, $type, $configureCallback); + } + + public function addSetting(Setting $setting) + { + parent::addSetting($setting); + } +} + diff --git a/tests/PHPUnit/Framework/Mock/Settings/FakeSystemSettings.php b/tests/PHPUnit/Framework/Mock/Settings/FakeSystemSettings.php new file mode 100644 index 0000000000..b7c77def9e --- /dev/null +++ b/tests/PHPUnit/Framework/Mock/Settings/FakeSystemSettings.php @@ -0,0 +1,31 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Framework\Mock\Settings; + +use Piwik\Settings\Setting; + +class FakeSystemSettings extends \Piwik\Plugins\ExampleSettingsPlugin\SystemSettings { + protected $pluginName = 'ExampleSettingsPlugin'; + + public function init() + { + + } + + public function makeSetting($name, $defaultValue, $type, $configureCallback) + { + return parent::makeSetting($name, $defaultValue, $type, $configureCallback); + } + + public function addSetting(Setting $setting) + { + parent::addSetting($setting); + } +} + diff --git a/tests/PHPUnit/Framework/Mock/Settings/FakeUserSettings.php b/tests/PHPUnit/Framework/Mock/Settings/FakeUserSettings.php new file mode 100644 index 0000000000..9d4bb5fdb1 --- /dev/null +++ b/tests/PHPUnit/Framework/Mock/Settings/FakeUserSettings.php @@ -0,0 +1,31 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Framework\Mock\Settings; + +use Piwik\Settings\Setting; + +class FakeUserSettings extends \Piwik\Plugins\ExampleSettingsPlugin\UserSettings { + protected $pluginName = 'ExampleSettingsPlugin'; + + public function init() + { + + } + + public function makeSetting($name, $defaultValue, $type, $configureCallback) + { + return parent::makeSetting($name, $defaultValue, $type, $configureCallback); + } + + public function addSetting(Setting $setting) + { + parent::addSetting($setting); + } +} + diff --git a/tests/PHPUnit/Integration/Measurable/MeasurableSettingTest.php b/tests/PHPUnit/Integration/Measurable/MeasurableSettingTest.php index d0e45286e7..5c8ae14aad 100644 --- a/tests/PHPUnit/Integration/Measurable/MeasurableSettingTest.php +++ b/tests/PHPUnit/Integration/Measurable/MeasurableSettingTest.php @@ -8,8 +8,10 @@ namespace Piwik\Tests\Integration\Measurable; -use Piwik\Measurable\MeasurableSetting; -use Piwik\Settings\Storage; +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Measurable\MeasurableSetting; +use Piwik\Settings\Storage\Storage; +use Piwik\Tests\Framework\Fixture; use Piwik\Tests\Framework\Mock\FakeAccess; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; @@ -21,14 +23,13 @@ class MeasurableSettingTest extends IntegrationTestCase public function setUp() { parent::setUp(); + Fixture::createWebsite('2014-01-01 00:00:01'); FakeAccess::$superUser = true; } private function createSetting() { - $setting = new MeasurableSetting('name', 'test'); - $storage = new Storage('test'); - $setting->setStorage($storage); + $setting = new MeasurableSetting('name', $default = '', FieldConfig::TYPE_STRING, 'Plugin', $idSite = 1); return $setting; } @@ -62,16 +63,6 @@ class MeasurableSettingTest extends IntegrationTestCase $this->createSetting()->setValue('test'); } - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed - */ - public function testGetSettingValue_shouldThrowException_IfNoPermissionToRead() - { - FakeAccess::clearAccess(); - $this->createSetting()->getValue(); - } - public function provideContainerConfig() { return array( diff --git a/tests/PHPUnit/Integration/Measurable/MeasurableSettingsTest.php b/tests/PHPUnit/Integration/Measurable/MeasurableSettingsTest.php index 60e1951d93..c4f8a19ce9 100644 --- a/tests/PHPUnit/Integration/Measurable/MeasurableSettingsTest.php +++ b/tests/PHPUnit/Integration/Measurable/MeasurableSettingsTest.php @@ -8,12 +8,11 @@ namespace Piwik\Tests\Integration\Measurable; +use Piwik\Access; use Piwik\Db; use Piwik\Plugin; -use Piwik\Plugins\MobileAppMeasurable\tests\Framework\Mock\Type; -use Piwik\Plugins\MobileAppMeasurable\Type as MobileAppType; -use Piwik\Measurable\MeasurableSetting; -use Piwik\Measurable\MeasurableSettings; +use Piwik\Plugins\WebsiteMeasurable\Type as WebsiteType; +use Piwik\Plugins\WebsiteMeasurable\MeasurableSettings; use Piwik\Tests\Framework\Fixture; use Piwik\Tests\Framework\Mock\FakeAccess; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; @@ -36,10 +35,8 @@ class MeasurableSettingsTest extends IntegrationTestCase FakeAccess::$superUser = true; - Plugin\Manager::getInstance()->activatePlugin('MobileAppMeasurable'); - if (!Fixture::siteCreated($this->idSite)) { - $type = MobileAppType::ID; + $type = WebsiteType::ID; Fixture::createWebsite('2015-01-01 00:00:00', $ecommerce = 0, $siteName = false, $siteUrl = false, $siteSearch = 1, $searchKeywordParameters = null, @@ -49,50 +46,37 @@ class MeasurableSettingsTest extends IntegrationTestCase $this->settings = $this->createSettings(); } - public function test_init_shouldAddSettingsFromType() - { - $this->assertNotEmpty($this->settings->getSetting('app_id')); - } - public function test_save_shouldActuallyStoreValues() { - $this->settings->getSetting('test2')->setValue('value2'); - $this->settings->getSetting('test3')->setValue('value3'); - - $this->assertStoredSettingsValue(null, 'test2'); - $this->assertStoredSettingsValue(null, 'test3'); - + $this->settings->siteSearchKeywords->setValue(array('value2')); + $this->settings->siteSearchCategory->setValue(array('value3')); $this->settings->save(); - $this->assertStoredSettingsValue('value2', 'test2'); - $this->assertStoredSettingsValue('value3', 'test3'); + $this->assertStoredSettingsValue(array('value2'), 'sitesearch_keyword_parameters'); + $this->assertStoredSettingsValue(array('value3'), 'sitesearch_category_parameters'); } /** * @expectedException \Exception - * @expectedExceptionMessage checkUserHasAdminAccess + * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed */ public function test_save_shouldCheckAdminPermissionsForThatSite() { FakeAccess::clearAccess(); + $this->settings = $this->createSettings(); + $this->settings->siteSearchKeywords->setValue(array('value4')); $this->settings->save(); } private function createSettings() { - $settings = new MeasurableSettings($this->idSite, MobileAppType::ID); - $settings->addSetting($this->createSetting('test2')); - $settings->addSetting($this->createSetting('test3')); + $provider = new Plugin\SettingsProvider(Plugin\Manager::getInstance()); + $settings = $provider->getMeasurableSettings('WebsiteMeasurable', $this->idSite, WebsiteType::ID); return $settings; } - private function createSetting($name) - { - return new MeasurableSetting($name, $name . ' Name'); - } - private function assertStoredSettingsValue($expectedValue, $settingName) { $settings = $this->createSettings(); @@ -105,7 +89,6 @@ class MeasurableSettingsTest extends IntegrationTestCase { return array( 'Piwik\Access' => new FakeAccess(), - 'Piwik\Plugins\MobileAppMeasurable\Type' => new Type() ); } } diff --git a/tests/PHPUnit/Integration/Measurable/MeasurableTest.php b/tests/PHPUnit/Integration/Measurable/MeasurableTest.php deleted file mode 100644 index e74ebb7b40..0000000000 --- a/tests/PHPUnit/Integration/Measurable/MeasurableTest.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Tests\Integration\Measurable; - -use Piwik\Db; -use Piwik\Plugins\MobileAppMeasurable\tests\Framework\Mock\Type; -use Piwik\Plugins\MobileAppMeasurable\Type as MobileAppType; -use Piwik\Plugin; -use Piwik\Measurable\Measurable; -use Piwik\Measurable\MeasurableSettings; -use Piwik\Tests\Framework\Fixture; -use Piwik\Tests\Framework\Mock\FakeAccess; -use Piwik\Tests\Framework\TestCase\IntegrationTestCase; - -/** - * @group Core - */ -class MeasurableTest extends IntegrationTestCase -{ - private $idSite = 1; - - /** - * @var Measurable - */ - private $measurable; - - private $settingName = 'app_id'; - - public function setUp() - { - parent::setUp(); - - Plugin\Manager::getInstance()->activatePlugin('MobileAppMeasurable'); - - if (!Fixture::siteCreated($this->idSite)) { - $type = MobileAppType::ID; - Fixture::createWebsite('2015-01-01 00:00:00', - $ecommerce = 0, $siteName = false, $siteUrl = false, - $siteSearch = 1, $searchKeywordParameters = null, - $searchCategoryParameters = null, $timezone = null, $type); - } - - $this->measurable = new Measurable($this->idSite); - } - - public function testGetSettingValue_shouldReturnValue_IfSettingExistsAndIsReadable() - { - $setting = new MeasurableSettings($this->idSite, Measurable::getTypeFor($this->idSite)); - $setting->getSetting($this->settingName)->setValue('mytest'); - - $value = $this->measurable->getSettingValue($this->settingName); - $this->assertNull($value); - - $setting->save(); // actually save value - - $value = $this->measurable->getSettingValue($this->settingName); - $this->assertSame('mytest', $value); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage does not exist - */ - public function testGetSettingValue_shouldThrowException_IfSettingDoesNotExist() - {; - $this->measurable->getSettingValue('NoTeXisTenT'); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed - */ - public function testGetSettingValue_shouldThrowException_IfNoPermissionToRead() - { - FakeAccess::clearAccess(); - $this->measurable->getSettingValue('app_id'); - } - - public function provideContainerConfig() - { - return array( - 'Piwik\Access' => new FakeAccess(), - 'Piwik\Plugins\MobileAppMeasurable\Type' => new Type() - ); - } - -} diff --git a/tests/PHPUnit/Integration/Measurable/Settings/StorageTest.php b/tests/PHPUnit/Integration/Measurable/Settings/StorageTest.php deleted file mode 100644 index 9264a51ba3..0000000000 --- a/tests/PHPUnit/Integration/Measurable/Settings/StorageTest.php +++ /dev/null @@ -1,188 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Tests\Integration\Measurable\Settings; - -use Piwik\Db; -use Piwik\Measurable\MeasurableSetting; -use Piwik\Measurable\Settings\Storage; -use Piwik\Settings\Setting; -use Piwik\Tests\Framework\Fixture; -use Piwik\Tests\Framework\TestCase\IntegrationTestCase; - -/** - * @group Core - */ -class StorageTest extends IntegrationTestCase -{ - private $idSite = 1; - - /** - * @var Storage - */ - private $storage; - - /** - * @var MeasurableSetting - */ - private $setting; - - public function setUp() - { - parent::setUp(); - - if (!Fixture::siteCreated($this->idSite)) { - Fixture::createWebsite('2015-01-01 00:00:00'); - } - - $this->storage = $this->createStorage(); - $this->setting = $this->createSetting('test'); - } - - private function createStorage($idSite = null) - { - if (!isset($idSite)) { - $idSite = $this->idSite; - } - - return new Storage(Db::get(), $idSite); - } - - private function createSetting($name) - { - return new MeasurableSetting($name, $name . ' Name'); - } - - public function test_getValue_shouldReturnNullByDefault() - { - $value = $this->storage->getValue($this->setting); - $this->assertNull($value); - } - - public function test_getValue_shouldReturnADefaultValueIfOneIsSet() - { - $this->setting->defaultValue = 194.34; - $value = $this->storage->getValue($this->setting); - $this->assertSame(194.34, $value); - } - - public function test_setValue_getValue_shouldSetAndGetActualValue() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - $value = $this->storage->getValue($this->setting); - $this->assertEquals('myRandomVal', $value); - } - - public function test_setValue_shouldNotSaveItInDatabase() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - - // make sure not actually stored - $this->assertSettingValue(null, $this->setting); - } - - public function test_save_shouldPersistValueInDatabase() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - $this->storage->save(); - - // make sure actually stored - $this->assertSettingValue('myRandomVal', $this->setting); - } - - public function test_save_shouldPersistValueForEachSiteInDatabase() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - $this->storage->save(); - - // make sure actually stored - $this->assertSettingValue('myRandomVal', $this->setting); - - $storage = $this->createStorage($idSite = 2); - $valueForDifferentSite = $storage->getValue($this->setting); - $this->assertNull($valueForDifferentSite); - } - - public function test_save_shouldPersistMultipleValues_ContainingInt() - { - $this->saveMultipleValues(); - - $this->assertSettingValue('myRandomVal', $this->setting); - $this->assertSettingValue(5, $this->createSetting('test2')); - $this->assertSettingValue(array(1, 2, '4'), $this->createSetting('test3')); - } - - public function test_deleteAll_ShouldRemoveTheEntireEntry() - { - $this->saveMultipleValues(); - - $this->assertSettingNotEmpty($this->setting); - $this->assertSettingNotEmpty($this->createSetting('test2')); - $this->assertSettingNotEmpty($this->createSetting('test3')); - - $this->storage->deleteAllValues(); - - $this->assertSettingEmpty($this->setting); - $this->assertSettingEmpty($this->createSetting('test2')); - $this->assertSettingEmpty($this->createSetting('test3')); - } - - public function test_deleteValue_ShouldOnlyDeleteOneValue() - { - $this->saveMultipleValues(); - - $this->assertSettingNotEmpty($this->setting); - $this->assertSettingNotEmpty($this->createSetting('test2')); - $this->assertSettingNotEmpty($this->createSetting('test3')); - - $this->storage->deleteValue($this->createSetting('test2')); - $this->storage->save(); - - $this->assertSettingEmpty($this->createSetting('test2')); - - $this->assertSettingNotEmpty($this->setting); - $this->assertSettingNotEmpty($this->createSetting('test3')); - } - - public function test_deleteValue_saveValue_ShouldNotResultInADeletedValue() - { - $this->saveMultipleValues(); - - $this->storage->deleteValue($this->createSetting('test2')); - $this->storage->setValue($this->createSetting('test2'), 'PiwikTest'); - $this->storage->save(); - - $this->assertSettingValue('PiwikTest', $this->createSetting('test2')); - } - - private function assertSettingValue($expectedValue, $setting) - { - $value = $this->createStorage()->getValue($setting); - $this->assertSame($expectedValue, $value); - } - - private function assertSettingNotEmpty(Setting $setting) - { - $value = $this->createStorage()->getValue($setting); - $this->assertNotNull($value); - } - - private function assertSettingEmpty(Setting $setting) - { - $value = $this->createStorage()->getValue($setting); - $this->assertNull($value); - } - - private function saveMultipleValues() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - $this->storage->setValue($this->createSetting('test2'), 5); - $this->storage->setValue($this->createSetting('test3'), array(1, 2, '4')); - $this->storage->save(); - } -} diff --git a/tests/PHPUnit/Integration/Plugin/ManagerTest.php b/tests/PHPUnit/Integration/Plugin/ManagerTest.php index e05e3a021e..5822e22c05 100644 --- a/tests/PHPUnit/Integration/Plugin/ManagerTest.php +++ b/tests/PHPUnit/Integration/Plugin/ManagerTest.php @@ -97,7 +97,7 @@ class ManagerTest extends IntegrationTestCase list($controller, $module, $action) = explode('.', $hook); try { - $resolver = new ControllerResolver(StaticContainer::getContainer(), new Plugin\Widgets($this->manager)); + $resolver = new ControllerResolver(StaticContainer::getContainer(), new Plugin\WidgetsProvider($this->manager)); $params = array(); $controller = $resolver->getController($module, $action, $params); } catch (\Exception $e) { diff --git a/tests/PHPUnit/Integration/Plugin/SettingsProviderTest.php b/tests/PHPUnit/Integration/Plugin/SettingsProviderTest.php new file mode 100644 index 0000000000..ceb804228b --- /dev/null +++ b/tests/PHPUnit/Integration/Plugin/SettingsProviderTest.php @@ -0,0 +1,165 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Plugin; + +use Piwik\Container\StaticContainer; +use Piwik\Db; +use Piwik\Plugin; +use Piwik\Plugin\SettingsProvider; +use Piwik\Settings\Measurable\MeasurableSettings; +use Piwik\Settings\Plugin\SystemSettings; +use Piwik\Settings\Plugin\UserSettings; +use Piwik\Settings\Storage; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group SettingsProvider + * @group SettingsProviderTest + */ +class SettingsProviderTest extends IntegrationTestCase +{ + /** + * @var SettingsProvider + */ + private $settings; + + /** + * @var Plugin\Manager + */ + private $pluginManager; + + private $examplePlugin = 'ExampleSettingsPlugin'; + + public function setUp() + { + parent::setUp(); + + $_GET['idSite'] = 1; + if (!Fixture::siteCreated(1)) { + Fixture::createWebsite('2015-01-01 00:00:00'); + } + + $this->pluginManager = StaticContainer::get('Piwik\Plugin\Manager'); + $this->settings = new SettingsProvider($this->pluginManager); + } + + public function tearDown() + { + parent::tearDown(); + unset($_GET['idSite']); + } + + public function test_getSystemSettings_shouldFindASystemSettingOfPlugin() + { + $settings = $this->settings->getSystemSettings($this->examplePlugin); + + $this->assertTrue($settings instanceof SystemSettings); + $this->assertSame($this->examplePlugin, $settings->getPluginName()); + } + + public function test_getSystemSettings_shouldReturnNull_IfPluginHasNoSystemSettings() + { + $settings = $this->settings->getSystemSettings('Intl'); + + $this->assertNull($settings); + } + + public function test_getSystemSettings_shouldReturnNull_IfPluginHasSettingButIsNotLoaded() + { + $this->pluginManager->unloadPlugin($this->examplePlugin); + $settings = $this->settings->getSystemSettings($this->examplePlugin); + $this->pluginManager->loadPlugin($this->examplePlugin); + + $this->assertNull($settings); + } + + public function test_getAllSystemSettings_shouldFindAllSystemSettings() + { + $settings = $this->settings->getAllSystemSettings(); + + $this->assertArrayHasKey($this->examplePlugin, $settings); + $this->assertArrayHasKey('AnonymousPiwikUsageMeasurement', $settings); + $this->assertArrayHasKey('QueuedTracking', $settings); + + foreach ($settings as $setting) { + $this->assertTrue($setting instanceof SystemSettings); + } + } + + public function test_getUserSettings_shouldFindASystemSettingOfPlugin() + { + $settings = $this->settings->getUserSettings($this->examplePlugin); + + $this->assertTrue($settings instanceof UserSettings); + $this->assertSame($this->examplePlugin, $settings->getPluginName()); + } + + public function test_getUserSettings_shouldReturnNull_IfPluginHasNoSystemSettings() + { + $settings = $this->settings->getUserSettings('Intl'); + + $this->assertNull($settings); + } + + public function test_getUserSettings_shouldReturnNull_IfPluginHasSettingButIsNotLoaded() + { + $this->pluginManager->unloadPlugin($this->examplePlugin); + $settings = $this->settings->getUserSettings($this->examplePlugin); + $this->pluginManager->loadPlugin($this->examplePlugin); + + $this->assertNull($settings); + } + + public function test_getAllUserSettings_shouldFindAllSystemSettings() + { + $settings = $this->settings->getAllUserSettings(); + + $this->assertArrayHasKey($this->examplePlugin, $settings); + + foreach ($settings as $setting) { + $this->assertTrue($setting instanceof UserSettings); + } + } + + public function test_getMeasurableSettings_shouldFindASystemSettingOfPlugin() + { + $settings = $this->settings->getMeasurableSettings($this->examplePlugin, $idSite = 1, $idType = null); + + $this->assertTrue($settings instanceof MeasurableSettings); + $this->assertSame($this->examplePlugin, $settings->getPluginName()); + } + + public function test_getMeasurableSettings_shouldReturnNull_IfPluginHasNoSystemSettings() + { + $settings = $this->settings->getMeasurableSettings('Intl', $idSite = 1, $idType = null); + + $this->assertNull($settings); + } + + public function test_getMeasurableSettings_shouldReturnNull_IfPluginHasSettingButIsNotLoaded() + { + $this->pluginManager->unloadPlugin($this->examplePlugin); + $settings = $this->settings->getMeasurableSettings($this->examplePlugin, $idSite = 1, $idType = null); + $this->pluginManager->loadPlugin($this->examplePlugin); + + $this->assertNull($settings); + } + + public function test_getAllMeasurableSettings_shouldReturnOnlyMeasurableSettings() + { + $settings = $this->settings->getAllMeasurableSettings($idSite = 1, $idType = null); + $this->assertArrayHasKey($this->examplePlugin, $settings); + + foreach ($settings as $setting) { + $this->assertTrue($setting instanceof MeasurableSettings); + } + } + +} diff --git a/tests/PHPUnit/Integration/Plugin/SettingsTest.php b/tests/PHPUnit/Integration/Plugin/SettingsTest.php deleted file mode 100644 index 679b84ccba..0000000000 --- a/tests/PHPUnit/Integration/Plugin/SettingsTest.php +++ /dev/null @@ -1,372 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Tests\Integration\Plugin; - -use Piwik\Db; -use Piwik\Plugin\Settings as PluginSettings; -use Piwik\Settings\Storage; -use Piwik\SettingsServer; -use Piwik\Tests\Integration\Settings\CorePluginTestSettings; -use Piwik\Tests\Integration\Settings\IntegrationTestCase; -use Piwik\Tracker\Cache; -use Piwik\Tracker\SettingsStorage; - -/** - * @group PluginSettings - */ -class SettingsTest extends IntegrationTestCase -{ - - public function test_constructor_shouldNotEstablishADatabaseConnection() - { - Db::destroyDatabaseObject(); - - $this->assertNotDbConnectionCreated(); - - $this->createSettingsInstance(); - - $this->assertNotDbConnectionCreated(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage A setting with name "myname" does already exist for plugin "ExampleSettingsPlugin" - */ - public function test_addSetting_shouldThrowException_InCaseTwoSettingsHaveTheSameName() - { - $this->addUserSetting('myname', 'mytitle'); - - $setting = $this->buildUserSetting('myname', 'mytitle2'); - $this->settings->addSetting($setting); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage The setting name "myname-" in plugin "ExampleSettingsPlugin" is not valid. Only underscores, alpha and numerical characters are allowed - */ - public function test_addSetting_shouldThrowException_IfTheSettingNameIsNotValid() - { - $setting = $this->buildUserSetting('myname-', 'mytitle'); - $this->settings->addSetting($setting); - } - - public function test_addSetting_shouldAssignDefaultType_IfFieldIsGivenButNoType() - { - $setting = $this->buildUserSetting('myname', 'mytitle'); - $setting->uiControlType = CorePluginTestSettings::CONTROL_MULTI_SELECT; - - $this->settings->addSetting($setting); - - $this->assertEquals(CorePluginTestSettings::TYPE_ARRAY, $setting->type); - } - - public function test_addSetting_shouldAssignDefaultField_IfTypeIsGivenButNoField() - { - $setting = $this->buildUserSetting('myname', 'mytitle'); - $setting->type = CorePluginTestSettings::TYPE_ARRAY; - - $this->settings->addSetting($setting); - - $this->assertEquals(CorePluginTestSettings::CONTROL_MULTI_SELECT, $setting->uiControlType); - } - - public function test_addSetting_shouldAddAValidator_IfFieldOptionsAreGiven() - { - $setting = $this->buildUserSetting('myname', 'mytitle'); - $setting->availableValues = array('allowedval' => 'DisplayName', 'allowedval2' => 'Name 2'); - - $this->settings->addSetting($setting); - - $this->assertInstanceOf('\Closure', $setting->validate); - } - - public function test_addSetting_shouldAddTheSettings_IfValid() - { - $setting = $this->addUserSetting('myname', 'mytitle'); - - $this->assertEquals(array('myname' => $setting), $this->settings->getSettings()); - } - - public function test_addSetting_shouldPassTheStorage_ToTheSetting() - { - $this->setSuperUser(); - - $setting = $this->buildUserSetting('myname', 'mytitle', 'myRandomName'); - $this->settings->addSetting($setting); - - $storage = $setting->getStorage(); - $this->assertTrue($storage instanceof Storage); - - $setting->setValue(5); - $this->assertSettingHasValue($setting, 5); - - $this->assertEquals($this->settings->getSetting('myname')->getValue(), 5); - } - - public function test_addSetting_shouldPassTrackerStorage_IfInTrackerMode() - { - $this->setSuperUser(); - - SettingsServer::setIsTrackerApiRequest(); - - $settings = $this->createSettingsInstance(); - $setting = $this->buildUserSetting('myname', 'mytitle', 'myRandomName'); - $settings->addSetting($setting); - - SettingsServer::setIsNotTrackerApiRequest(); - - $storage = $setting->getStorage(); - $this->assertTrue($storage instanceof SettingsStorage); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingsValueNotAllowed - */ - public function test_setSettingValue_shouldApplyValidationAndFail_IfOptionsAreSet() - { - $this->setUser(); - $setting = $this->buildUserSetting('mysystem', 'mytitle'); - $setting->availableValues = array('allowed' => 'text', 'allowed2' => 'text2'); - $this->settings->addSetting($setting); - $setting->setValue('notallowed'); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingsValueNotAllowed - */ - public function test_setSettingValue_shouldApplyValidationAndFail_IfOptionsAreSetAndValueIsAnArray() - { - $this->setUser(); - $setting = $this->buildUserSetting('mysystem', 'mytitle'); - $setting->availableValues = array('allowed' => 'text', 'allowed2' => 'text2'); - $setting->uiControlType = PluginSettings::CONTROL_MULTI_SELECT; - - $this->settings->addSetting($setting); - - $setting->setValue(array('allowed', 'notallowed')); - } - - public function test_setSettingValue_shouldApplyValidationAndSucceed_IfOptionsAreSet() - { - $this->setUser(); - $setting = $this->buildUserSetting('mysystem', 'mytitle'); - $setting->availableValues = array('allowed' => 'text', 'allowed2' => 'text2'); - $setting->uiControlType = PluginSettings::CONTROL_MULTI_SELECT; - - $this->settings->addSetting($setting); - - $setting->setValue(array('allowed', 'allowed2')); - $this->assertSettingHasValue($setting, array('allowed', 'allowed2')); - - $setting->type = PluginSettings::TYPE_STRING; - $setting->setValue('allowed'); - $this->assertSettingHasValue($setting, 'allowed'); - } - - public function test_getSettingsForCurrentUser_shouldOnlyReturnSettingsHavingEnoughAdminPermissions() - { - $this->setUser(); - - $this->addSystemSetting('mysystemsetting1', 'mytitle1'); - $this->addSystemSetting('mysystemsetting2', 'mytitle2'); - $this->addSystemSetting('mysystemsetting3', 'mytitle3'); - $this->addSystemSetting('mysystemsetting4', 'mytitle4'); - $userSetting = $this->addUserSetting('myusersetting1', 'mytitle5'); - - $this->assertEquals(array('myusersetting1' => $userSetting), $this->settings->getSettingsForCurrentUser()); - - // but all of them should be available via getSettings() - $this->assertCount(5, $this->settings->getSettings()); - } - - public function test_getSettingsForCurrentUser_shouldReturnAllSettingsIfEnoughPermissionsAndSortThemBySettingOrder() - { - $this->setSuperUser(); - - $this->addSystemSetting('mysystemsetting1', 'mytitle1'); - $this->addSystemSetting('mysystemsetting2', 'mytitle2'); - $this->addUserSetting('myusersetting2', 'mytitle6'); - $this->addSystemSetting('mysystemsetting3', 'mytitle3'); - $this->addSystemSetting('mysystemsetting4', 'mytitle4'); - $this->addUserSetting('myusersetting1', 'mytitle5'); - - $expected = array('myusersetting2', 'myusersetting1', 'mysystemsetting1', 'mysystemsetting2', 'mysystemsetting3', 'mysystemsetting4'); - $this->assertEquals($expected, array_keys($this->settings->getSettingsForCurrentUser())); - } - - public function test_save_shouldSaveAllValues() - { - $this->setSuperUser(); - - $this->addSystemSetting('mysystemsetting2', 'mytitle2'); - $this->addSystemSetting('mysystemsetting1', 'mytitle1')->setValue('111'); - $this->addSystemSetting('mysystemsetting4', 'mytitle4')->setValue('4444'); - $this->addUserSetting('myusersetting1', 'mytitle5')->setValue('55555'); - $this->addSystemSetting('mysystemsetting3', 'mytitle3'); - - $this->settings->save(); - - // verify actually saved - $verifySettings = $this->createSettingsInstance(); - - $setting1 = $this->buildSystemSetting('mysystemsetting1', 'mytitle1'); - $setting2 = $this->buildSystemSetting('mysystemsetting2', 'mytitle2'); - $setting3 = $this->buildSystemSetting('mysystemsetting3', 'mytitle3'); - $setting4 = $this->buildSystemSetting('mysystemsetting4', 'mytitle4'); - $setting5 = $this->buildUserSetting('myusersetting1', 'mytitle5'); - - $verifySettings->addSetting($setting1); - $verifySettings->addSetting($setting2); - $verifySettings->addSetting($setting3); - $verifySettings->addSetting($setting4); - $verifySettings->addSetting($setting5); - - $this->assertEquals('111', $setting1->getValue()); - $this->assertEquals(null, $setting2->getValue()); - $this->assertEquals(null, $setting3->getValue()); - $this->assertEquals('4444', $setting4->getValue()); - $this->assertEquals('55555', $setting5->getValue()); - } - - - public function test_save_shouldClearTrackerCacheEntries() - { - $this->setSuperUser(); - - Cache::setCacheGeneral(array('testSetting' => 1)); - - $this->assertArrayHasKey('testSetting', Cache::getCacheGeneral()); - - $this->addSystemSetting('mysystemsetting2', 'mytitle2'); - $this->settings->save(); - - $this->assertArrayNotHasKey('testSetting', Cache::getCacheGeneral()); - } - - public function test_removeAllPluginSettings_shouldRemoveAllSettings() - { - $this->setSuperUser(); - - $this->addSystemSetting('mysystemsetting3', 'mytitle3'); - $this->addSystemSetting('mysystemsetting4', 'mytitle4'); - $this->addSystemSetting('mysystemsetting1', 'mytitle1')->setValue('111'); - $this->addSystemSetting('mysystemsetting2', 'mytitle2')->setValue('4444'); - $this->addUserSetting('myusersetting1', 'mytitle5')->setValue('55555'); - $this->settings->save(); - - $this->settings->removeAllPluginSettings(); - - $verifySettings = $this->createSettingsInstance(); - - $setting1 = $this->buildSystemSetting('mysystemsetting1', 'mytitle1'); - $setting2 = $this->buildSystemSetting('mysystemsetting2', 'mytitle2'); - $setting3 = $this->buildSystemSetting('mysystemsetting3', 'mytitle3'); - $setting4 = $this->buildSystemSetting('mysystemsetting4', 'mytitle4'); - $setting5 = $this->buildUserSetting('myusersetting1', 'mytitle5'); - - $verifySettings->addSetting($setting1); - $verifySettings->addSetting($setting2); - $verifySettings->addSetting($setting3); - $verifySettings->addSetting($setting4); - $verifySettings->addSetting($setting5); - - $this->assertEquals(null, $setting1->getValue()); - $this->assertEquals(null, $setting2->getValue()); - $this->assertEquals(null, $setting3->getValue()); - $this->assertEquals(null, $setting4->getValue()); - $this->assertEquals(null, $setting5->getValue()); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage checkUserHasSuperUserAccess Fake exception - */ - public function test_removeAllPluginSettings_shouldThrowException_InCaseUserIsNotSuperUser() - { - $this->setUser(); - - $this->settings->removeAllPluginSettings(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage checkUserHasSuperUserAccess Fake exception - */ - public function test_removeAllPluginSettings_shouldThrowException_InCaseAnonymousUser() - { - $this->setAnonymousUser(); - - $this->settings->removeAllPluginSettings(); - } - - public function test_userSetting_shouldGenerateDifferentKey_ThenSystemSetting() - { - $this->setSuperUser(); - - $user = $this->buildUserSetting('myname', 'mytitle'); - $system = $this->buildSystemSetting('myname', 'mytitle'); - - $this->assertNotEquals($user->getKey(), $system->getKey()); - $this->assertEquals('myname', $system->getKey()); - $this->assertEquals('myname#superUserLogin#', $user->getKey()); - } - - public function test_userSetting_shouldSaveValuesPerUser() - { - $this->setSuperUser(); - $user1Login = 'user1'; - $user2Login = '_user2_'; - $user3Login = null; // current loggged in user - - $user = $this->buildUserSetting('myuser', 'mytitle', $user1Login); - - $this->settings->addSetting($user); - - $user->setValue('111'); - $user->setUserLogin($user2Login); - $user->setValue('222'); - $user->setUserLogin($user3Login); - $user->setValue('333'); - - $user->setUserLogin($user1Login); - $this->assertSettingHasValue($user, '111'); - $user->setUserLogin($user2Login); - $this->assertSettingHasValue($user, '222'); - $user->setUserLogin($user3Login); - $this->assertSettingHasValue($user, '333'); - - $user->setUserLogin($user2Login); - $user->removeValue(); - - $user->setUserLogin($user1Login); - $this->assertSettingHasValue($user, '111'); - $user->setUserLogin($user2Login); - $this->assertSettingHasValue($user, null); - $user->setUserLogin($user3Login); - $this->assertSettingHasValue($user, '333'); - - $this->settings->removeAllPluginSettings(); - - $user->setUserLogin($user1Login); - $this->assertSettingHasValue($user, null); - $user->setUserLogin($user2Login); - $this->assertSettingHasValue($user, null); - $user->setUserLogin($user3Login); - $this->assertSettingHasValue($user, null); - } - - public function test_construct_shouldDetectTheNameOfThePluginAutomatically_IfPluginNameNotGiven() - { - $setting = new \Piwik\Plugins\ExampleSettingsPlugin\Settings(); - - $this->assertEquals('ExampleSettingsPlugin', $setting->getPluginName()); - } -} diff --git a/tests/PHPUnit/Integration/Plugin/WidgetsTest.php b/tests/PHPUnit/Integration/Plugin/WidgetsProviderTest.php index 185faba064..9370c8f628 100644 --- a/tests/PHPUnit/Integration/Plugin/WidgetsTest.php +++ b/tests/PHPUnit/Integration/Plugin/WidgetsProviderTest.php @@ -10,7 +10,7 @@ namespace Piwik\Tests\Integration\Plugin; use Piwik\Container\StaticContainer; use Piwik\Db; -use Piwik\Plugin\Widgets; +use Piwik\Plugin\WidgetsProvider; use Piwik\Settings\Storage; use Piwik\Tests\Framework\Fixture; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; @@ -18,13 +18,13 @@ use Piwik\Widget\WidgetConfig; use Piwik\Widget\WidgetContainerConfig; /** - * @group Widgets - * @group WidgetsTest + * @group WidgetsProvider + * @group WidgetsProviderTest */ -class WidgetsTest extends IntegrationTestCase +class WidgetsProviderTest extends IntegrationTestCase { /** - * @var Widgets + * @var WidgetsProvider */ private $widgets; @@ -37,7 +37,7 @@ class WidgetsTest extends IntegrationTestCase Fixture::createWebsite('2015-01-01 00:00:00'); } - $this->widgets = new Widgets(StaticContainer::get('Piwik\Plugin\Manager')); + $this->widgets = new WidgetsProvider(StaticContainer::get('Piwik\Plugin\Manager')); } public function tearDown() diff --git a/tests/PHPUnit/Integration/Report/ReportsTest.php b/tests/PHPUnit/Integration/Report/ReportsTest.php index 9681d73e64..874bd4fb94 100644 --- a/tests/PHPUnit/Integration/Report/ReportsTest.php +++ b/tests/PHPUnit/Integration/Report/ReportsTest.php @@ -16,7 +16,7 @@ use Piwik\Piwik; use Piwik\Metrics; use Piwik\Plugins\ExampleTracker\Columns\ExampleDimension; use Piwik\Plugins\Referrers\Columns\Keyword; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Report\ReportWidgetFactory; use Piwik\Translate; use Piwik\Plugin\Manager as PluginManager; @@ -34,7 +34,7 @@ class ReportTest extends IntegrationTestCase { $this->unloadAllPlugins(); - $reports = new Reports(); + $reports = new ReportsProvider(); $report = $reports->getAllReports(); $this->assertEquals(array(), $report); @@ -45,7 +45,7 @@ class ReportTest extends IntegrationTestCase $this->loadExampleReportPlugin(); $this->loadMorePlugins(); - $reports = new Reports(); + $reports = new ReportsProvider(); $reports = $reports->getAllReports(); $this->assertGreaterThan(20, count($reports)); diff --git a/tests/PHPUnit/Integration/ReportTest.php b/tests/PHPUnit/Integration/ReportTest.php index 036516bd91..fc89a4ea59 100644 --- a/tests/PHPUnit/Integration/ReportTest.php +++ b/tests/PHPUnit/Integration/ReportTest.php @@ -16,7 +16,7 @@ use Piwik\Piwik; use Piwik\Metrics; use Piwik\Plugins\ExampleTracker\Columns\ExampleDimension; use Piwik\Plugins\Referrers\Columns\Keyword; -use Piwik\Plugin\Reports; +use Piwik\Plugin\ReportsProvider; use Piwik\Report\ReportWidgetFactory; use Piwik\Translate; use Piwik\Plugin\Manager as PluginManager; @@ -315,14 +315,14 @@ class ReportTest extends IntegrationTestCase $module = 'ExampleReport'; $action = 'getExampleReport'; - $report = Reports::factory($module, $action); + $report = ReportsProvider::factory($module, $action); $this->assertInstanceOf('Piwik\Plugins\ExampleReport\Reports\GetExampleReport', $report); $this->assertEquals($module, $report->getModule()); $this->assertEquals($action, $report->getAction()); // action ucfirst should work as well - $report = Reports::factory($module, ucfirst($action)); + $report = ReportsProvider::factory($module, ucfirst($action)); $this->assertInstanceOf('Piwik\Plugins\ExampleReport\Reports\GetExampleReport', $report); $this->assertEquals($module, $report->getModule()); @@ -345,7 +345,7 @@ class ReportTest extends IntegrationTestCase { PluginManager::getInstance()->loadPlugins(array('Referrers')); - $report = Reports::factory('Referrers', 'getSearchEngines'); + $report = ReportsProvider::factory('Referrers', 'getSearchEngines'); $subtableDimension = $report->getSubtableDimension(); $this->assertNotNull($subtableDimension); diff --git a/tests/PHPUnit/Integration/Settings/BaseSettingsTestCase.php b/tests/PHPUnit/Integration/Settings/BaseSettingsTestCase.php new file mode 100644 index 0000000000..0a94ba7953 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/BaseSettingsTestCase.php @@ -0,0 +1,111 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings; + +use Piwik\Db; +use Piwik\Piwik; +use Piwik\Settings\FieldConfig; + +/** + * @group PluginSettings + * @group SystemSettings + */ +class BaseSettingsTestCase extends IntegrationTestCase +{ + protected $updateEventName; + + public function test_constructor_shouldNotEstablishADatabaseConnection() + { + Db::destroyDatabaseObject(); + + $this->assertNotDbConnectionCreated(); + + $this->createSettingsInstance(); + + $this->assertNotDbConnectionCreated(); + } + + public function test_makeSetting_ShouldAlsoAddTheSetting() + { + $this->assertNull($this->settings->getSetting('myName')); + + $this->makeSetting('myName'); + + $this->assertNotNull($this->settings->getSetting('myName')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage A setting with name "myName" does already exist for plugin "ExampleSettingsPlugin" + */ + public function test_makeSetting_ShouldFailWhenAdingSameSettingTwice() + { + $this->makeSetting('myName'); + $this->makeSetting('myName'); + } + + public function test_getSetting_CanRetrieveAspecificSetting() + { + $this->makeSetting('myName'); + + $this->assertSame('myName', $this->settings->getSetting('myName')->getName()); + } + + public function test_getSetting_IsCaseSensitive() + { + $this->makeSetting('myName'); + + $this->assertNull($this->settings->getSetting('myname')); + } + + public function test_getSetting_ReturnsNullWhenNoSuchSettingFound() + { + $this->assertNull($this->settings->getSetting('myName')); + } + + public function test_getSettingsWritableByCurrentUser_returnsOnlySettingsThatAreWritable() + { + $this->assertSame(array(), $this->settings->getSettingsWritableByCurrentUser()); + + $setting1 = $this->makeSetting('myName1'); + $setting1->setIsWritableByCurrentUser(true); + + $setting2 = $this->makeSetting('myName2'); + $setting2->setIsWritableByCurrentUser(false); + + $setting3 = $this->makeSetting('myName3'); + $setting3->setIsWritableByCurrentUser(true); + + $expected = array( + 'myName1' => $setting1, + 'myName3' => $setting3 + ); + $this->assertSame($expected, $this->settings->getSettingsWritableByCurrentUser()); + } + + public function test_save_triggersAnEvent() + { + $settings = null; + + Piwik::addAction($this->updateEventName, function ($instance) use (&$settings) { + $settings = $instance; + }); + + $this->settings->save(); + + $this->assertSame($settings, $this->settings); + } + + protected function makeSetting($name) + { + $type = FieldConfig::TYPE_STRING; + return $this->settings->makeSetting($name, $default = '', $type, function () {}); + } + +} diff --git a/tests/PHPUnit/Integration/Settings/CorePluginTestSettings.php b/tests/PHPUnit/Integration/Settings/CorePluginTestSettings.php deleted file mode 100644 index 7792f1f353..0000000000 --- a/tests/PHPUnit/Integration/Settings/CorePluginTestSettings.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Tests\Integration\Settings; - -use Piwik\Settings\Setting; - -class CorePluginTestSettings extends \Piwik\Plugins\ExampleSettingsPlugin\Settings { - - public function init() - { - - } - - public function addSetting(Setting $setting) - { - parent::addSetting($setting); - } -} - diff --git a/tests/PHPUnit/Integration/Settings/IntegrationTestCase.php b/tests/PHPUnit/Integration/Settings/IntegrationTestCase.php index 37c8a205a6..8a323924a6 100644 --- a/tests/PHPUnit/Integration/Settings/IntegrationTestCase.php +++ b/tests/PHPUnit/Integration/Settings/IntegrationTestCase.php @@ -9,9 +9,13 @@ namespace Piwik\Tests\Integration\Settings; use Piwik\Db; +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Plugin\SystemSetting; +use Piwik\Settings\Plugin\UserSetting; use Piwik\Settings\Setting; use Piwik\Settings\Storage; use Piwik\Tests\Framework\Mock\FakeAccess; +use Piwik\Tests\Framework\Mock\Settings\FakeSystemSettings; /** * @group PluginSettings @@ -21,7 +25,7 @@ use Piwik\Tests\Framework\Mock\FakeAccess; class IntegrationTestCase extends \Piwik\Tests\Framework\TestCase\IntegrationTestCase { /** - * @var CorePluginTestSettings + * @var FakeSystemSettings */ protected $settings; @@ -32,28 +36,9 @@ class IntegrationTestCase extends \Piwik\Tests\Framework\TestCase\IntegrationTes $this->settings = $this->createSettingsInstance(); } - public function tearDown() - { - $this->setSuperUser(); - if ($this->settings) { - $this->settings->removeAllPluginSettings(); - } - - parent::tearDown(); - } - - public function test_constructor_shouldNotEstablishADatabaseConnection() - { - $this->assertNotDbConnectionCreated(); - - new Storage('PluginName'); - - $this->assertNotDbConnectionCreated(); - } - protected function assertSettingHasValue(Setting $setting, $expectedValue, $expectedType = null) { - $value = $setting->getValue($setting); + $value = $setting->getValue(); $this->assertEquals($expectedValue, $value); if (!is_null($expectedType)) { @@ -61,22 +46,6 @@ class IntegrationTestCase extends \Piwik\Tests\Framework\TestCase\IntegrationTes } } - protected function buildUserSetting($name, $title, $userLogin = null) - { - $userSetting = new \Piwik\Settings\UserSetting($name, $title, $userLogin); - $userSetting->setStorage(new Storage('ExampleSettingsPlugin')); - - return $userSetting; - } - - protected function buildSystemSetting($name, $title) - { - $systemSetting = new \Piwik\Settings\SystemSetting($name, $title); - $systemSetting->setStorage(new Storage('ExampleSettingsPlugin')); - - return $systemSetting; - } - protected function setSuperUser() { FakeAccess::$superUser = true; @@ -95,32 +64,7 @@ class IntegrationTestCase extends \Piwik\Tests\Framework\TestCase\IntegrationTes protected function createSettingsInstance() { - return new CorePluginTestSettings('ExampleSettingsPlugin'); - } - - protected function addSystemSetting($name, $title) - { - $setting = $this->buildSystemSetting($name, $title); - $this->settings->addSetting($setting); - return $setting; - } - - protected function addUserSetting($name, $title) - { - $setting = $this->buildUserSetting($name, $title); - $this->settings->addSetting($setting); - return $setting; - } - - - protected function assertSettingIsNotSavedInTheDb($settingName, $expectedValue) - { - // by creating a new instance... - $setting = $this->buildSystemSetting($settingName, 'mytitle'); - $verifySettings = $this->createSettingsInstance(); - $verifySettings->addSetting($setting); - - $this->assertEquals($expectedValue, $setting->getValue()); + return new FakeSystemSettings(); } public function provideContainerConfig() diff --git a/tests/PHPUnit/Integration/Settings/Measurable/MeasurablePropertyTest.php b/tests/PHPUnit/Integration/Settings/Measurable/MeasurablePropertyTest.php new file mode 100644 index 0000000000..38a8c43b70 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Measurable/MeasurablePropertyTest.php @@ -0,0 +1,62 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Plugin; + +use Piwik\Config; +use Piwik\Db; +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Measurable\MeasurableProperty; +use Piwik\Settings\Measurable\MeasurableSetting; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\Settings\FakeMeasurableSettings; +use Piwik\Tests\Integration\Settings\IntegrationTestCase; + +/** + * @group MeasurableSettings + * @group Settings + * @group MeasurableProperty + */ +class MeasurablePropertyTest extends IntegrationTestCase +{ + + public function setUp() + { + parent::setUp(); + Db::destroyDatabaseObject(); + } + + protected function createSettingsInstance() + { + if (!Fixture::siteCreated(1)) { + Fixture::createWebsite('2014-01-01 01:01:01'); + } + + return new FakeMeasurableSettings($idSite = 1); + } + + public function test_constructor_shouldNotEstablishADatabaseConnection() + { + $this->assertNotDbConnectionCreated(); + + new MeasurableProperty('ecommerce', $default = 5, FieldConfig::TYPE_INT, 'MyPlugin', $idSite = 1); + + $this->assertNotDbConnectionCreated(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Name "name" is not allowed to be used + */ + public function test_constructor_shouldThrowAnExceptionWhenNotWhitelistedNameIsUsed() + { + new MeasurableProperty('name', $default = 5, FieldConfig::TYPE_INT, 'MyPlugin', $idSite = 1); + } + + +} diff --git a/tests/PHPUnit/Integration/Settings/Measurable/MeasurableSettingTest.php b/tests/PHPUnit/Integration/Settings/Measurable/MeasurableSettingTest.php new file mode 100644 index 0000000000..29f6be7c67 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Measurable/MeasurableSettingTest.php @@ -0,0 +1,98 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Plugin; + +use Piwik\Config; +use Piwik\Db; +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Measurable\MeasurableSetting; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\Settings\FakeMeasurableSettings; +use Piwik\Tests\Integration\Settings\IntegrationTestCase; + +/** + * @group MeasurableSettings + * @group Settings + * @group MeasurableSetting + */ +class MeasurableSettingTest extends IntegrationTestCase +{ + + public function setUp() + { + parent::setUp(); + foreach (array(2,3) as $idSite) { + if (!Fixture::siteCreated($idSite)) { + Fixture::createWebsite('2014-01-01 01:01:01'); + } + } + + Db::destroyDatabaseObject(); + } + + protected function createSettingsInstance() + { + if (!Fixture::siteCreated(1)) { + Fixture::createWebsite('2014-01-01 01:01:01'); + } + + return new FakeMeasurableSettings($idSite = 1); + } + + public function test_constructor_shouldNotEstablishADatabaseConnection() + { + $this->assertNotDbConnectionCreated(); + + new MeasurableSetting('name', $default = 5, FieldConfig::TYPE_INT, 'MyPlugin', $idSite = 1); + + $this->assertNotDbConnectionCreated(); + } + + public function test_save() + { + $site1 = $this->buildSetting('field1', null, $site = '1'); + $site1->setValue('value1'); + $site1->save(); + + $site2 = $this->buildSetting('field1', null, $site = '2'); + $this->assertSame('value1', $site1->getValue()); + $this->assertSame('', $site2->getValue()); + $site2->setValue('value2'); + $site2->save(); + + $site3 = $this->buildSetting('field1', null, $site = '3'); + $this->assertSame('value1', $site1->getValue()); + $this->assertSame('value2', $site2->getValue()); + $this->assertSame('', $site3->getValue()); + + $site1Field2 = $this->buildSetting('field2', null, $site = '1'); + $this->assertSame('', $site1Field2->getValue()); + $site1Field2->setValue('value1Field2'); + $site1Field2->save(); + + $this->assertSame('value1', $site1->getValue()); + $this->assertSame('value1Field2', $site1Field2->getValue()); + } + + private function buildSetting($name, $type = null, $idSite = null) + { + if (!isset($type)) { + $type = FieldConfig::TYPE_STRING; + } + + if (!isset($idSite)) { + $idSite = 1; + } + + $userSetting = new MeasurableSetting($name, $default = '', $type, 'MyPluginName', $idSite); + + return $userSetting; + } + +} diff --git a/tests/PHPUnit/Integration/Settings/Measurable/MeasurableSettingsTest.php b/tests/PHPUnit/Integration/Settings/Measurable/MeasurableSettingsTest.php new file mode 100644 index 0000000000..689bab5d92 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Measurable/MeasurableSettingsTest.php @@ -0,0 +1,58 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Plugin; + +use Piwik\Db; +use Piwik\Plugins\WebsiteMeasurable\Type; +use Piwik\Settings\Measurable\MeasurableSetting; +use Piwik\Settings\Measurable\MeasurableSettings; +use Piwik\Settings\Plugin\UserSetting; +use Piwik\Settings\Plugin\UserSettings; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\Settings\FakeMeasurableSettings; +use Piwik\Tests\Integration\Settings\BaseSettingsTestCase; + +/** + * @group PluginSettings + * @group UserSettings + */ +class MeasurableSettingsTest extends BaseSettingsTestCase +{ + protected $updateEventName = 'MeasurableSettings.updated'; + + protected function createSettingsInstance() + { + if (!Fixture::siteCreated(1)) { + Fixture::createWebsite('2014-01-01 00:00:01'); + } + Db::destroyDatabaseObject(); + return new FakeMeasurableSettings($idSite = 1, $type = Type::ID); + } + + public function test_weAreWorkingWithMeasurableSettings() + { + $this->assertTrue($this->settings instanceof MeasurableSettings); + } + + public function test_constructor_getPluginName_canDetectPluginNameAutomatically() + { + $this->assertSame('ExampleSettingsPlugin', $this->settings->getPluginName()); + + $settings = new \Piwik\Plugins\ExampleSettingsPlugin\MeasurableSettings($idSite = 1); + $this->assertSame('ExampleSettingsPlugin', $settings->getPluginName()); + } + + public function test_makeSetting_ShouldCreateAMeasurableSetting() + { + $setting = $this->makeSetting('myName'); + + $this->assertTrue($setting instanceof MeasurableSetting); + } + +} diff --git a/tests/PHPUnit/Integration/Settings/SystemSettingTest.php b/tests/PHPUnit/Integration/Settings/Plugin/SystemSettingTest.php index e740f61f70..9b9e93db5f 100644 --- a/tests/PHPUnit/Integration/Settings/SystemSettingTest.php +++ b/tests/PHPUnit/Integration/Settings/Plugin/SystemSettingTest.php @@ -6,12 +6,13 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ -namespace Piwik\Tests\Integration\Settings; +namespace Piwik\Tests\Integration\Settings\Plugin; use Piwik\Config; use Piwik\Db; -use Piwik\Plugin\Settings; -use Piwik\Settings\SystemSetting; +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Plugin\SystemSetting; +use Piwik\Tests\Integration\Settings\IntegrationTestCase; /** * @group PluginSettings @@ -31,7 +32,7 @@ class SystemSettingTest extends IntegrationTestCase { $this->assertNotDbConnectionCreated(); - new SystemSetting('name', 'title'); + new SystemSetting('name', $default = 5, FieldConfig::TYPE_INT, 'MyPlugin'); $this->assertNotDbConnectionCreated(); } @@ -43,7 +44,7 @@ class SystemSettingTest extends IntegrationTestCase public function test_setSettingValue_shouldThrowException_IfAUserIsTryingToSetASettingWhichNeedsSuperUserPermission() { $this->setUser(); - $setting = $this->addSystemSetting('mysystem', 'mytitle'); + $setting = $this->buildSetting('mysystem'); $setting->setValue(2); } @@ -55,7 +56,7 @@ class SystemSettingTest extends IntegrationTestCase public function test_setSettingValue_shouldThrowException_IfAnonymousIsTryingToSetASettingWhichNeedsSuperUserPermission() { $this->setAnonymousUser(); - $setting = $this->addSystemSetting('mysystem', 'mytitle'); + $setting = $this->buildSetting('mysystem'); $setting->setValue(2); } @@ -64,59 +65,23 @@ class SystemSettingTest extends IntegrationTestCase { $this->setSuperUser(); - $setting = $this->addSystemSetting('mysystem', 'mytitle'); + $setting = $this->buildSetting('mysystem'); $setting->setValue(2); $this->assertSettingHasValue($setting, 2); } - public function test_setSettingValue_shouldNotPersistValueInDatabase_OnSuccess() - { - $this->setSuperUser(); - - $setting = $this->buildSystemSetting('mysystem', 'mytitle'); - $this->settings->addSetting($setting); - $setting->setValue(2); - - // make sure stored on the instance - $this->assertSettingHasValue($setting, 2); - $this->assertSettingIsNotSavedInTheDb('mysystem', null); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed - */ - public function test_getSettingValue_shouldThrowException_IfUserHasNotEnoughPermissionToReadValue() - { - $this->setUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->getValue(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed - */ - public function test_getSettingValue_shouldThrowException_IfAnonymousTriedToReadValue() - { - $this->setAnonymousUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->getValue(); - } - public function test_getSettingValue_shouldBeReadableBySuperUser() { $this->setSuperUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); + $setting = $this->buildSetting('myusersetting'); $this->assertEquals('', $setting->getValue()); } public function test_getSettingValue_shouldReturnValue_IfReadbleByCurrentUserIsAllowed() { $this->setUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->readableByCurrentUser = true; + $setting = $this->buildSetting('myusersetting'); $this->assertEquals('', $setting->getValue()); } @@ -124,8 +89,7 @@ class SystemSettingTest extends IntegrationTestCase public function test_getSettingValue_fromConfig_IfOneIsConfiguredInsteadOfTheValueFromDatabase() { $this->setSuperUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->setPluginName('MyPluginName'); + $setting = $this->buildSetting('myusersetting'); $setting->setValue('test'); $this->assertEquals('test', $setting->getValue()); @@ -137,26 +101,21 @@ class SystemSettingTest extends IntegrationTestCase public function test_getSettingValue_fromConfig_ShouldConvertToTheSpecifiedType() { $this->setSuperUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->setPluginName('MyPluginName'); + $setting = $this->buildSetting('myusersetting', FieldConfig::TYPE_BOOL); Config::getInstance()->MyPluginName = array('myusersetting' => '1'); - $this->assertSame('1', $setting->getValue()); - - $setting->type = Settings::TYPE_BOOL; $this->assertTrue($setting->getValue()); } public function test_getSettingValue_fromConfig_isCaseSensitive() { $this->setSuperUser(); - $setting = $this->addSystemSetting('myUsersetting', 'mytitle'); - $setting->setPluginName('MyPluginName'); + $setting = $this->buildSetting('myUsersetting'); Config::getInstance()->MyPluginName = array('myusersetting' => '1'); - $this->assertNull($setting->getValue()); + $this->assertSame('', $setting->getValue()); Config::getInstance()->MyPluginName = array('myUsersetting' => '1'); @@ -166,8 +125,7 @@ class SystemSettingTest extends IntegrationTestCase public function test_getSettingsValue_fromConfig_ShouldSetObjectToNotWritableAsSoonAsAValueIsConfigured() { $this->setSuperUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->setPluginName('MyPluginName'); + $setting = $this->buildSetting('myusersetting'); $this->assertTrue($setting->isWritableByCurrentUser()); @@ -178,8 +136,7 @@ class SystemSettingTest extends IntegrationTestCase public function test_setIsWritableByCurrentUser() { $this->setSuperUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->setPluginName('MyPluginName'); + $setting = $this->buildSetting('myusersetting'); $this->assertTrue($setting->isWritableByCurrentUser()); @@ -197,47 +154,50 @@ class SystemSettingTest extends IntegrationTestCase public function test_setSettingsValue_shouldNotBePossible_AsSoonAsAConfigValueIsConfigured() { $this->setSuperUser(); - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->setPluginName('MyPluginName'); + $setting = $this->buildSetting('myusersetting'); Config::getInstance()->MyPluginName = array('myusersetting' => '0'); $setting->setValue('test'); } - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed - */ - public function test_removeSettingValue_shouldThrowException_IfUserHasNotEnoughAdminPermissions() + public function test_save_shouldSaveDifferentValuesForDifferentPluginsAndFields() { - $this->setUser(); - $setting = $this->addSystemSetting('mysystemsetting', 'mytitle'); - $setting->removeValue(); - } + $plugin1 = $this->buildSetting('field1', null, $login = 'plugin1'); + $plugin1->setValue('value1'); + $plugin1->save(); - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed - */ - public function test_removeSettingValue_shouldThrowException_IfAnonymousTriesToRemoveValue() - { - $this->setAnonymousUser(); - $setting = $this->addSystemSetting('mysystemsetting', 'mytitle'); - $setting->removeValue(); + $plugin2 = $this->buildSetting('field1', null, $login = 'plugin2'); + $this->assertSame('value1', $plugin1->getValue()); + $this->assertSame('', $plugin2->getValue()); + $plugin2->setValue('value2'); + $plugin2->save(); + + $plugin3 = $this->buildSetting('field1', null, $login = 'plugin3'); + $this->assertSame('value1', $plugin1->getValue()); + $this->assertSame('value2', $plugin2->getValue()); + $this->assertSame('', $plugin3->getValue()); + + $plugin1Field2 = $this->buildSetting('field2', null, $login = 'plugin1'); + $this->assertSame('', $plugin1Field2->getValue()); + $plugin1Field2->setValue('value1Field2'); + $plugin1Field2->save(); + + $this->assertSame('value1', $plugin1->getValue()); + $this->assertSame('value1Field2', $plugin1Field2->getValue()); } - public function test_removeSettingValue_shouldRemoveValue_ShouldNotSaveValueInDb() + private function buildSetting($name, $type = null, $plugin = null) { - $this->setSuperUser(); + if (!isset($type)) { + $type = FieldConfig::TYPE_STRING; + } + if (!isset($plugin)) { + $plugin = 'MyPluginName'; + } - $setting = $this->addSystemSetting('myusersetting', 'mytitle'); - $setting->setValue('12345657'); - $this->settings->save(); + $systemSetting = new SystemSetting($name, $default = '', $type, $plugin); - $setting->removeValue(); - $this->assertSettingHasValue($setting, null); - - // should still have same value - $this->assertSettingIsNotSavedInTheDb('myusersetting', '12345657'); + return $systemSetting; } + } diff --git a/tests/PHPUnit/Integration/Settings/Plugin/SystemSettingsTest.php b/tests/PHPUnit/Integration/Settings/Plugin/SystemSettingsTest.php new file mode 100644 index 0000000000..52339b6a81 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Plugin/SystemSettingsTest.php @@ -0,0 +1,43 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Plugin; + +use Piwik\Db; +use Piwik\Settings\Plugin\SystemSetting; +use Piwik\Settings\Plugin\SystemSettings; +use Piwik\Tests\Integration\Settings\BaseSettingsTestCase; + +/** + * @group PluginSettings + * @group SystemSettings + */ +class SystemSettingsTest extends BaseSettingsTestCase +{ + protected $updateEventName = 'SystemSettings.updated'; + + public function test_weAreWorkingWithSystemSettings() + { + $this->assertTrue($this->settings instanceof SystemSettings); + } + + public function test_constructor_getPluginName_canDetectPluginNameAutomatically() + { + $settings = new \Piwik\Plugins\ExampleSettingsPlugin\SystemSettings(); + $this->assertSame('ExampleSettingsPlugin', $settings->getPluginName()); + $this->assertSame('ExampleSettingsPlugin', $this->settings->getPluginName()); + } + + public function test_makeSetting_ShouldCreateASystemSetting() + { + $setting = $this->makeSetting('myName'); + + $this->assertTrue($setting instanceof SystemSetting); + } + +} diff --git a/tests/PHPUnit/Integration/Settings/Plugin/UserSettingTest.php b/tests/PHPUnit/Integration/Settings/Plugin/UserSettingTest.php new file mode 100644 index 0000000000..2a4935db69 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Plugin/UserSettingTest.php @@ -0,0 +1,227 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Plugin; + +use Piwik\Db; +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Plugin\UserSetting; +use Piwik\Settings\Storage\Storage; +use Piwik\Tests\Framework\Mock\FakeAccess; +use Piwik\Tests\Framework\Mock\Settings\FakeBackend; +use Piwik\Tests\Framework\Mock\Settings\FakeUserSettings; +use Piwik\Tests\Integration\Settings\IntegrationTestCase; + +/** + * @group PluginSettings + * @group Settings + * @group UserSetting + */ +class UserSettingTest extends IntegrationTestCase +{ + protected function createSettingsInstance() + { + return new FakeUserSettings(); + } + + public function test_constructor_shouldNotEstablishADatabaseConnection() + { + $this->assertNotDbConnectionCreated(); + + new UserSetting('name', $default = 5, FieldConfig::TYPE_INT, 'MyPlugin', 'login'); + + $this->assertNotDbConnectionCreated(); + } + + public function test_constructor_shouldEstablishADatabaseConnection_AsSoonAsWeGetAValue() + { + $this->setSuperUser(); + Db::destroyDatabaseObject(); + + $setting = $this->buildSetting('testSetting'); + + $this->assertNotDbConnectionCreated(); + + $setting->getValue(); + + $this->assertDbConnectionCreated(); + } + + public function test_constructor_shouldEstablishADatabaseConnection_AsSoonAsWeSetAValue() + { + $this->setSuperUser(); + Db::destroyDatabaseObject(); + + $setting = $this->buildSetting('testSetting'); + $settings = $this->createSettingsInstance(); + $settings->addSetting($setting); + + $this->assertNotDbConnectionCreated(); + + $setting->setValue('5'); + + $this->assertDbConnectionCreated(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed + */ + public function test_setSettingValue_shouldThrowException_IfAnonymousIsTryingToSetASettingWhichNeedsUserPermission() + { + $this->setAnonymousUser(); + $setting = $this->buildSetting('mysystem'); + + $setting->setValue(2); + } + + public function test_setSettingValue_shouldSucceed_IfUserIsTryingToSetASettingWhichNeedsUserPermission() + { + $this->setUser(); + $setting = $this->buildSetting('mysystem'); + $setting->setValue(2); + + $this->assertSettingHasValue($setting, 2); + } + + public function test_setSettingValue_shouldCastValue_IfTypeIsSetButNoFilter() + { + $this->setUser(); + + // cast to INT + $setting = $this->buildSetting('mysystem', FieldConfig::TYPE_INT); + $setting->setValue('31'); + $this->assertSettingHasValue($setting, 31, 'integer'); + + // ARRAY + $setting = $this->buildSetting('mysystem2', FieldConfig::TYPE_ARRAY); + $setting->setValue('31xm42'); + $this->assertSettingHasValue($setting, array('31xm42'), 'array'); + + // BOOL + $setting = $this->buildSetting('mysystem3', FieldConfig::TYPE_BOOL); + $setting->setValue('1'); + $this->assertSettingHasValue($setting, true, 'boolean'); + + // FLOAT + $setting = $this->buildSetting('mysystem4', FieldConfig::TYPE_FLOAT); + $setting->setValue('1.21'); + $this->assertSettingHasValue($setting, 1.21, 'float'); + + // STRING + $setting = $this->buildSetting('mysystem5', FieldConfig::TYPE_STRING); + $setting->setValue('31xm42'); + $this->assertSettingHasValue($setting, '31xm42'); + } + + public function test_setSettingValue_shouldApplyFilterAndNotCast_IfAFilterIsSet() + { + $this->setUser(); + + $self = $this; + + $setting = $this->buildSetting('mysystem', FieldConfig::TYPE_INT); + $setting->setConfigureCallback(function (FieldConfig $field) use ($self, $setting) { + $field->transform = function ($value, $userSetting) use ($self, $setting) { + $self->assertEquals('31', $value); + $self->assertEquals($setting, $userSetting); + + return '43939kmf3m3'; + }; + }); + + $setting->setValue('31'); + + // should not be casted to int + $this->assertSettingHasValue($setting, 43939, 'integer'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Validation Fail + */ + public function test_setSettingValue_shouldValidateAValue_IfAFilterIsSet() + { + $this->setUser(); + $self = $this; + + $setting = $this->buildSetting('mysystem', FieldConfig::TYPE_INT); + $setting->setConfigureCallback(function (FieldConfig $field) use ($self, $setting) { + $field->validate = function ($value, $userSetting) use ($self, $setting) { + $self->assertEquals('31xm42', $value); + $self->assertEquals($setting, $userSetting); + + throw new \Exception('Validation Fail'); + }; + }); + + $setting->setValue('31xm42'); + } + + public function test_getSettingValue_shouldReturnUncastedDefaultValue_IfNoValueIsSet() + { + $this->setUser(); + + $setting = $this->buildSetting('mydefaultsystem', FieldConfig::TYPE_INT, $default = 'mytestvalue'); + $this->settings->addSetting($setting); + + // should not be casted to int + $this->assertSettingHasValue($setting, 'mytestvalue', 'string'); + } + + public function test_getSettingValue_shouldReturnValue_IfValueExistsAndUserHasPermission() + { + $this->setUser(); + $setting = $this->buildSetting('myusersetting', FieldConfig::TYPE_ARRAY); + $setting->setValue(array(2,3,4)); + + $this->assertSettingHasValue($setting, array(2,3,4)); + } + + public function test_save_shouldSaveDifferentValuesForDifferentUsersAndFields() + { + $login1 = $this->buildSetting('field1', null, '', $login = 'user1'); + $login1->setValue('value1'); + $login1->save(); + + $login2 = $this->buildSetting('field1', null, '', $login = 'user2'); + $this->assertSame('value1', $login1->getValue()); + $this->assertSame('', $login2->getValue()); + $login2->setValue('value2'); + $login2->save(); + + $login3 = $this->buildSetting('field1', null, '', $login = 'user3'); + $this->assertSame('value1', $login1->getValue()); + $this->assertSame('value2', $login2->getValue()); + $this->assertSame('', $login3->getValue()); + + $login1Field2 = $this->buildSetting('field2', null, '', $login = 'user1'); + $this->assertSame('', $login1Field2->getValue()); + $login1Field2->setValue('value1Field2'); + $login1Field2->save(); + + $this->assertSame('value1', $login1->getValue()); + $this->assertSame('value1Field2', $login1Field2->getValue()); + } + + private function buildSetting($name, $type = null, $default = '', $login = null) + { + if (!isset($type)) { + $type = FieldConfig::TYPE_STRING; + } + + if (!isset($login)) { + $login = FakeAccess::$identity; + } + + $userSetting = new UserSetting($name, $default, $type, 'MyPluginName', $login); + + return $userSetting; + } + +} diff --git a/tests/PHPUnit/Integration/Settings/Plugin/UserSettingsTest.php b/tests/PHPUnit/Integration/Settings/Plugin/UserSettingsTest.php new file mode 100644 index 0000000000..66c9a30152 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Plugin/UserSettingsTest.php @@ -0,0 +1,49 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Plugin; + +use Piwik\Db; +use Piwik\Settings\Plugin\UserSetting; +use Piwik\Settings\Plugin\UserSettings; +use Piwik\Tests\Framework\Mock\Settings\FakeUserSettings; +use Piwik\Tests\Integration\Settings\BaseSettingsTestCase; + +/** + * @group PluginSettings + * @group UserSettings + */ +class UserSettingsTest extends BaseSettingsTestCase +{ + protected $updateEventName = 'UserSettings.updated'; + + protected function createSettingsInstance() + { + return new FakeUserSettings(); + } + + public function test_weAreWorkingWithUserSettings() + { + $this->assertTrue($this->settings instanceof UserSettings); + } + + public function test_constructor_getPluginName_canDetectPluginNameAutomatically() + { + $settings = new \Piwik\Plugins\ExampleSettingsPlugin\UserSettings(); + $this->assertSame('ExampleSettingsPlugin', $settings->getPluginName()); + $this->assertSame('ExampleSettingsPlugin', $this->settings->getPluginName()); + } + + public function test_makeSetting_ShouldCreateAUserSetting() + { + $setting = $this->makeSetting('myName'); + + $this->assertTrue($setting instanceof UserSetting); + } + +} diff --git a/tests/PHPUnit/Integration/Settings/SettingTest.php b/tests/PHPUnit/Integration/Settings/SettingTest.php new file mode 100644 index 0000000000..70aa00db49 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/SettingTest.php @@ -0,0 +1,249 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Plugin; + +use Piwik\Db; +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Setting; +use Piwik\Settings\Storage\Storage; +use Piwik\Settings\Storage\Backend; +use Exception; +use Piwik\Tests\Framework\Mock\Settings\FakeBackend; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + + +/** + * @group Settings + * @group Setting + */ +class SettingTest extends IntegrationTestCase +{ + + /** + * @expectedException \Exception + * @expectedExceptionMessage setting name "myname-" in plugin "MyPluginName" is invalid + */ + public function test_constructor_shouldThrowException_IfTheSettingNameIsNotValid() + { + $this->makeSetting('myname-'); + } + + public function test_configureField_shouldAssignDefaultField_IfTypeIsGivenButNoField() + { + $setting = $this->makeSetting('myname', FieldConfig::TYPE_ARRAY); + $field = $setting->configureField(); + $this->assertEquals(FieldConfig::UI_CONTROL_MULTI_SELECT, $field->uiControl); + + $setting = $this->makeSetting('myname2', FieldConfig::TYPE_BOOL); + $field = $setting->configureField(); + $this->assertEquals(FieldConfig::UI_CONTROL_CHECKBOX, $field->uiControl); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Type must be an array when using a multi select + */ + public function test_configureField_ShouldCheckThatTypeMakesActuallySenseForConfiguredUiControl() + { + $setting = $this->makeSetting('myname', FieldConfig::TYPE_STRING, $default = '', function (FieldConfig $field) { + $field->uiControl = FieldConfig::UI_CONTROL_MULTI_SELECT; + }); + $setting->configureField(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Type does not exist + */ + public function test_configureField_ChecksTheGivenTypeIsKnown() + { + $setting = $this->makeSetting('myname', 'unknOwnTyPe'); + $setting->configureField(); + } + + public function test_setValue_shouldValidateAutomatically_IfFieldOptionsAreGiven() + { + $setting = $this->makeSetting('myname', null, $default = '', function (FieldConfig $field) { + $field->availableValues = array('allowedval' => 'DisplayName', 'allowedval2' => 'Name 2'); + }); + + // valid value + $setting->setValue('allowedval'); + $this->assertSame('allowedval', $setting->getValue()); + + try { + $setting->setValue('invAliDValue'); + } catch (Exception $e) { + $this->assertContains('CoreAdminHome_PluginSettingsValueNotAllowed', $e->getMessage()); + return; + } + + $this->fail('An expected exception has not been thrown'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage CoreAdminHome_PluginSettingsValueNotAllowed + */ + public function test_setValue_shouldApplyValidationAndFail_IfOptionsAreSetAndValueIsAnArray() + { + $setting = $this->makeSetting('myname', FieldConfig::TYPE_ARRAY, $default = '', function (FieldConfig $field) { + $field->availableValues = array('allowedval' => 'DisplayName', 'allowedval2' => 'Name 2'); + $field->uiControl = FieldConfig::UI_CONTROL_MULTI_SELECT; + }); + + $setting->setValue(array('allowed', 'notallowed')); + } + + public function test_setSettingValue_shouldApplyValidationAndSucceed_IfOptionsAreSet() + { + $setting = $this->makeSetting('myname', FieldConfig::TYPE_ARRAY, $default = '', function (FieldConfig $field) { + $field->availableValues = array('allowedval' => 'DisplayName', 'allowedval2' => 'Name 2'); + $field->uiControl = FieldConfig::UI_CONTROL_MULTI_SELECT; + }); + $setting->setValue(array('allowedval', 'allowedval2')); + $this->assertSame($setting->getValue(), array('allowedval', 'allowedval2')); + + $setting = $this->makeSetting('myname2', null, $default = '', function (FieldConfig $field) { + $field->availableValues = array('allowedval' => 'DisplayName', 'allowedval2' => 'Name 2'); + }); + + $setting->setValue('allowedval'); + $this->assertSame($setting->getValue(), 'allowedval'); + } + + public function test_setValue_shouldValidateAutomatically_IfTypeBoolIsUsed() + { + $setting = $this->makeSetting('myname', FieldConfig::TYPE_BOOL); + + // valid values + $setting->setValue('1'); + $this->assertSame(true, $setting->getValue()); + $setting->setValue(false); + $this->assertSame(false, $setting->getValue()); + $setting->setValue(1); + $this->assertSame(true, $setting->getValue()); + + try { + $setting->setValue('invAliDValue'); + } catch (Exception $e) { + $this->assertContains('CoreAdminHome_PluginSettingsValueNotAllowed', $e->getMessage()); + return; + } + + $this->fail('An expected exception has not been thrown'); + } + + /** + * @dataProvider getNumericTypes + */ + public function test_setValue_shouldValidateAutomatically_IfTypeIsNumeric($type) + { + $setting = $this->makeSetting('myname', $type); + + // valid values + $setting->setValue('1'); + $setting->setValue('1.5'); + $setting->setValue(0); + $setting->setValue(0.5); + $setting->setValue(-22.5); + + try { + $setting->setValue('1invalid'); + } catch (Exception $e) { + $this->assertContains('CoreAdminHome_PluginSettingsValueNotAllowed', $e->getMessage()); + return; + } + + $this->fail('An expected exception has not been thrown'); + } + + public function getNumericTypes() + { + return array(array(FieldConfig::TYPE_INT), array(FieldConfig::TYPE_FLOAT)); + } + + public function test_isWritableByCurrentUser_shouldNotBeWritableByDefault() + { + $setting = new Setting($name = 'test', $default = 0, $type = FieldConfig::TYPE_INT, function () {}); + $this->assertFalse($setting->isWritableByCurrentUser()); + } + + public function test_setIsWritableByCurrentUser() + { + $setting = $this->makeSetting('myName'); + $this->assertTrue($setting->isWritableByCurrentUser()); + + $setting->setIsWritableByCurrentUser(0); + $this->assertFalse($setting->isWritableByCurrentUser()); + + $setting->setIsWritableByCurrentUser(1); + $this->assertTrue($setting->isWritableByCurrentUser()); + + $setting->setIsWritableByCurrentUser(false); + $this->assertFalse($setting->isWritableByCurrentUser()); + } + + public function test_setDefaultValue_getDefaultValue() + { + $setting = $this->makeSetting('myname'); + $setting->setDefaultValue(5); + $this->assertSame(5, $setting->getDefaultValue()); + } + + public function test_getType() + { + $setting = $this->makeSetting('myname', FieldConfig::TYPE_ARRAY); + $this->assertSame(FieldConfig::TYPE_ARRAY, $setting->getType()); + } + + public function test_getName() + { + $setting = $this->makeSetting('myName'); + $this->assertSame('myName', $setting->getName()); + } + + protected function makeSetting($name, $type = null, $default = '', $configure = null) + { + if (!isset($type)) { + $type = FieldConfig::TYPE_STRING; + } + + $setting = new Setting($name, $default, $type, 'MyPluginName'); + $setting->setStorage(new Storage(new Backend\Null('myId'))); + $setting->setIsWritableByCurrentUser(true); + + if (isset($configure)) { + $setting->setConfigureCallback($configure); + } + + return $setting; + } + + public function test_save_shouldPersistValue() + { + $value = array(2,3,4); + + $backend = new FakeBackend('test'); + $backend->save(array()); + $storage = new Storage($backend); + + $setting = $this->makeSetting('mysetting', FieldConfig::TYPE_ARRAY); + $setting->setStorage($storage); + $setting->setValue($value); + + // assert not saved in backend + $this->assertSame(array(), $backend->load()); + + $setting->save(); + + // assert saved in backend + $this->assertSame(array('mysetting' => $value), $backend->load()); + } +} diff --git a/tests/PHPUnit/Integration/Settings/Storage/Backend/CacheTest.php b/tests/PHPUnit/Integration/Settings/Storage/Backend/CacheTest.php new file mode 100644 index 0000000000..c43b90fd1d --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Storage/Backend/CacheTest.php @@ -0,0 +1,116 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Storage\Backend; + +use Piwik\Settings\Storage\Backend\Cache; +use Piwik\Tests\Framework\Mock\Settings\FakeBackend; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group Settings + * @group Backend + * @group Storage + */ +class CacheTest extends IntegrationTestCase +{ + + /** + * @var FakeBackend + */ + private $backend; + + /** + * @var Cache + */ + private $cacheBackend; + + public function setUp() + { + parent::setUp(); + + $this->backend = new FakeBackend('MySuperStorageKey'); + $this->cacheBackend = new Cache($this->backend); + } + + public function test_getStorageId_shouldReturnStorageOfActualPlugin() + { + $this->assertSame('MySuperStorageKey', $this->cacheBackend->getStorageId()); + } + + public function test_load_ShouldActuallyLoadDataFromBackend() + { + $this->assertSame($this->backend->load(), $this->cacheBackend->load()); + $this->assertNotEmpty($this->cacheBackend->load()); + } + + public function test_load_ShouldCacheData() + { + $this->assertNotValueCached(); + + $this->cacheBackend->load(); + + $this->assertValueCached(); + $this->assertValueIsActuallyInCache(); + } + + public function test_delete_ShouldClearCacheAndData() + { + $this->cacheBackend->load(); + $this->assertValueCached(); + + $this->cacheBackend->delete(); + + $this->assertNotValueCached(); + $this->assertSame(array(), $this->cacheBackend->load()); + $this->assertSame(array(), $this->backend->load()); + } + + public function test_save_ShouldClearCacheAndUpdateData() + { + $this->cacheBackend->load(); + $this->assertValueCached(); + + $value = array('new' => 'value'); + $this->cacheBackend->save($value); + + $this->assertNotValueCached(); + + $this->assertSame($value, $this->cacheBackend->load()); + $this->assertSame($value, $this->backend->load()); + + $this->assertValueCached(); + $this->assertValueIsActuallyInCache(); + } + + private function assertValueIsActuallyInCache() + { + $this->assertSame($this->backend->load(), $this->getCache()->fetch($this->getCacheKey())); + } + + private function assertValueCached() + { + $this->assertTrue($this->getCache()->contains($this->getCacheKey())); + } + + private function assertNotValueCached() + { + $this->assertFalse($this->getCache()->contains($this->getCacheKey())); + } + + private function getCacheKey() + { + return $this->cacheBackend->getStorageId(); + } + + private function getCache() + { + return Cache::buildCache(); + } + +} diff --git a/tests/PHPUnit/Integration/Settings/Storage/Backend/MeasurableSettingsTableTest.php b/tests/PHPUnit/Integration/Settings/Storage/Backend/MeasurableSettingsTableTest.php new file mode 100644 index 0000000000..8b0e7a5bec --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Storage/Backend/MeasurableSettingsTableTest.php @@ -0,0 +1,253 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Storage\Backend; + +use Piwik\Config; +use Piwik\Db; +use Piwik\Settings\Storage\Backend\MeasurableSettingsTable; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group Settings + * @group Backend + * @group Storage + */ +class MeasurableSettingsTableTest extends IntegrationTestCase +{ + + /** + * @var MeasurableSettingsTable + */ + private $backendSite1; + + /** + * @var MeasurableSettingsTable + */ + private $backendSite2; + + /** + * @var MeasurableSettingsTable + */ + private $backendSite1Plugin2; + + /** + * @var MeasurableSettingsTable[] + */ + private $allBackends = array(); + + public function setUp() + { + parent::setUp(); + + $this->backendSite1 = $this->createSettings(1, 'MyPluginName'); + $this->backendSite2 = $this->createSettings(2, 'MyPluginName'); + $this->backendSite1Plugin2 = $this->createSettings(1, 'MyPluginName2'); + $this->allBackends = array($this->backendSite1, $this->backendSite2, $this->backendSite1Plugin2); + } + + private function createSettings($idSite, $plugin) + { + return new MeasurableSettingsTable($idSite, $plugin); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage No plugin name given + */ + public function test_construct_shouldThrowAnException_IfPluginNameIsEmpty() + { + $this->createSettings(1, ''); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage No idSite given + */ + public function test_construct_shouldThrowAnException_IfIdSiteIsEmpty() + { + $this->createSettings(0, 'MyPlugin'); + } + + public function test_load_shouldNotHaveAnySettingsByDefault() + { + $this->assertSame(array(), $this->backendSite1->load()); + $this->assertSame(array(), $this->backendSite2->load()); + $this->assertSame(array(), $this->backendSite1Plugin2->load()); + } + + public function test_getStorageId_shouldIncludePluginNameAndLogin() + { + $this->assertSame('MeasurableSettings_1_MyPluginName', $this->backendSite1->getStorageId()); + $this->assertSame('MeasurableSettings_2_MyPluginName', $this->backendSite2->getStorageId()); + $this->assertSame('MeasurableSettings_1_MyPluginName2', $this->backendSite1Plugin2->getStorageId()); + } + + public function test_save_ShouldOnlySaveForSpecificPluginAndIdSite() + { + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => 'val'); + + $this->backendSite1->save($value1); + + $this->assertSame($value1, $this->backendSite1->load()); + $this->assertSame(array(), $this->backendSite2->load()); + $this->assertSame(array(), $this->backendSite1Plugin2->load()); + + $value2 = $this->getExampleValues(); + $this->backendSite2->save($value2); + + $this->assertSame($value1, $this->backendSite1->load()); + $this->assertSame($value2, $this->backendSite2->load()); + $this->assertSame(array(), $this->backendSite1Plugin2->load()); + + $value3 = $this->getExampleValues(); + $this->backendSite1Plugin2->save($value3); + + $this->assertSame($value1, $this->backendSite1->load()); + $this->assertSame($value2, $this->backendSite2->load()); + $this->assertSame($value3, $this->backendSite1Plugin2->load()); + } + + public function test_delete_shouldDeleteAllValuesButOnlyForSpecificPluginAndIdSite() + { + $values = array(); + foreach ($this->allBackends as $index => $backend) { + $values[$index] = $this->getExampleValues(); + $backend->save($values[$index]); + $this->assertSame($values[$index], $backend->load()); + } + + foreach ($this->allBackends as $index => $backend) { + $backend->delete(); + $values[$index] = array(); + + // we make sure the values for all others are still set and only the current one was deleted + foreach ($this->allBackends as $j => $backend2) { + $this->assertSame($values[$j], $backend2->load()); + } + } + } + + public function test_save_DuplicateValuesShouldBeOverwritten() + { + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => 'val'); + + $this->backendSite1->save($value1); + + $this->assertSame($value1, $this->backendSite1->load()); + + $value2 = array('mytest' => 'test2', 'Mysetting2' => 'val', 'Mysetting1' => 'valueNew'); + $this->backendSite1->save($value2); + + $this->assertEquals($value2, $this->backendSite1->load()); + } + + public function test_save_NoLongerExistingValues_shouldBeRemoved() + { + $value = $this->saveValueForAllBackends(); + + // overwrite only user1 + $value2 = array('mytest' => 'test2', 'Mysetting1' => 'valueNew'); + $this->backendSite1Plugin2->save($value2); + $this->assertEquals($value2, $this->backendSite1Plugin2->load()); + + // make sure other backends remain unchanged + foreach ($this->allBackends as $backend) { + if ($backend !== $this->backendSite1Plugin2) { + $this->assertSame($value, $backend->load()); + } + } + } + + public function test_save_load_ShouldBeAbleToSaveAndLoadArrayValues() + { + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => array('val', 'val7', 'val5')); + + $this->backendSite1Plugin2->save($value1); + $this->assertEquals($value1, $this->backendSite1Plugin2->load()); + } + + public function test_save_load_ShouldBeAbleToSaveAndLoadArrayValues_OnlyOneKey() + { + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => array('val')); + + $this->backendSite1Plugin2->save($value1); + + + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => 'val'); + // it doesn't return an array for Mysetting2 but it is supposed to be casted to array by storage in this case + $this->assertEquals($value1, $this->backendSite1Plugin2->load()); + } + + public function test_save_ShouldBeAbleToSaveBoolValues() + { + $value1 = array('Mysetting1' => true, 'Mysetting2' => array('val', 'val7', false, true, 'val5')); + + $this->backendSite1Plugin2->save($value1); + + $value1 = array('Mysetting1' => '1', 'Mysetting2' => array('val', 'val7', '0', '1', 'val5')); + $this->assertEquals($value1, $this->backendSite1Plugin2->load()); + } + + public function test_save_ShouldIgnoreNullValues() + { + $value1 = array('Mysetting1' => true, 'MySetting3' => null, 'Mysetting2' => array('val', null, true, 'val5')); + + $this->backendSite1Plugin2->save($value1); + + $value1 = array('Mysetting1' => '1', 'Mysetting2' => array('val', '1', 'val5')); + $this->assertEquals($value1, $this->backendSite1Plugin2->load()); + } + + public function test_removeAllUserSettingsForUser_shouldOnlyRemoveSettingsForThatUser() + { + $value = $this->saveValueForAllBackends(); + + MeasurableSettingsTable::removeAllSettingsForSite('2'); + + foreach ($this->allBackends as $backend) { + if ($backend === $this->backendSite2) { + $this->assertSame(array(), $backend->load()); + } else { + $this->assertSame($value, $backend->load()); + } + } + } + + public function test_removeAllSettingsForPlugin_shouldOnlyRemoveSettingsForThatPlugin() + { + $value = $this->saveValueForAllBackends(); + + MeasurableSettingsTable::removeAllSettingsForPlugin('MyPluginName'); + + foreach ($this->allBackends as $backend) { + if ($backend === $this->backendSite1Plugin2) { + $this->assertSame($value, $backend->load()); + } else { + $this->assertSame(array(), $backend->load()); + } + } + } + + private function saveValueForAllBackends() + { + $value = array('Mysetting1' => 'value1', 'Mysetting2' => 'val'); + + foreach ($this->allBackends as $backend) { + $backend->save($value); + $this->assertSame($value, $backend->load()); + } + + return $value; + } + + private function getExampleValues() + { + return array('Mysetting3' => 'value3', 'Mysetting4' . rand(4,99) => 'val' . rand(0, 10)); + } +} diff --git a/tests/PHPUnit/Integration/Settings/Storage/Backend/NullTest.php b/tests/PHPUnit/Integration/Settings/Storage/Backend/NullTest.php new file mode 100644 index 0000000000..fcbafa3b82 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Storage/Backend/NullTest.php @@ -0,0 +1,48 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Storage\Backend; + +use Piwik\Config; +use Piwik\Db; +use Piwik\Settings\Storage\Backend\Null; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group Settings + * @group Backend + * @group Storage + */ +class NullTest extends IntegrationTestCase +{ + + /** + * @var Null + */ + private $backend; + + public function setUp() + { + parent::setUp(); + + $this->backend = new Null('storageId1FooBar'); + } + + public function test_getStorageId_shouldReturnStorageId() + { + $this->assertSame('storageId1FooBar', $this->backend->getStorageId()); + } + + public function test_save_load_shouldNotSaveAnything() + { + $this->assertSame(array(), $this->backend->load()); + $this->backend->save(array('foo' => 'bar')); + $this->assertSame(array(), $this->backend->load()); + } + +} diff --git a/tests/PHPUnit/Integration/Settings/Storage/Backend/PluginSettingsTableTest.php b/tests/PHPUnit/Integration/Settings/Storage/Backend/PluginSettingsTableTest.php new file mode 100644 index 0000000000..9a15bee767 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Storage/Backend/PluginSettingsTableTest.php @@ -0,0 +1,288 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Storage\Backend; + +use Piwik\Config; +use Piwik\Db; +use Piwik\Settings\Storage\Backend\PluginSettingsTable; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group Settings + * @group Backend + * @group Storage + */ +class PluginSettingsTableTest extends IntegrationTestCase +{ + + /** + * @var PluginSettingsTable + */ + private $backendPlugin1; + + /** + * @var PluginSettingsTable + */ + private $backendPlugin2; + + /** + * @var PluginSettingsTable + */ + private $backendUser1; + + /** + * @var PluginSettingsTable + */ + private $backendUser2; + + /** + * @var PluginSettingsTable[] + */ + private $allBackends = array(); + + public function setUp() + { + parent::setUp(); + + $this->backendPlugin1 = $this->createSettings('MyPluginName', ''); + $this->backendPlugin2 = $this->createSettings('MyPluginName2', ''); + $this->backendUser1 = $this->createSettings('MyPluginName', 'user1'); + $this->backendUser2 = $this->createSettings('MyPluginName', 'user2'); + $this->allBackends = array($this->backendPlugin1, $this->backendPlugin2, $this->backendUser1, $this->backendUser2); + } + + private function createSettings($plugin, $login) + { + return new PluginSettingsTable($plugin, $login); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage No plugin name given + */ + public function test_construct_shouldThrowAnException_IfPluginNameIsEmpty() + { + $this->createSettings('', ''); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Invalid user login name + */ + public function test_construct_shouldThrowAnException_IfUserLoginFalse() + { + $this->createSettings('MyPlugin', false); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Invalid user login name + */ + public function test_construct_shouldThrowAnException_IfUserLoginNull() + { + $this->createSettings('MyPlugin', null); + } + + public function test_load_shouldNotHaveAnySettingsByDefault() + { + $this->assertSame(array(), $this->backendPlugin1->load()); + $this->assertSame(array(), $this->backendPlugin2->load()); + $this->assertSame(array(), $this->backendUser1->load()); + $this->assertSame(array(), $this->backendUser2->load()); + } + + public function test_getStorageId_shouldIncludePluginNameAndLogin() + { + $this->assertSame('PluginSettings_MyPluginName_User_', $this->backendPlugin1->getStorageId()); + $this->assertSame('PluginSettings_MyPluginName2_User_', $this->backendPlugin2->getStorageId()); + $this->assertSame('PluginSettings_MyPluginName_User_user1', $this->backendUser1->getStorageId()); + $this->assertSame('PluginSettings_MyPluginName_User_user2', $this->backendUser2->getStorageId()); + } + + public function test_save_ShouldOnlySaveForSpecificPlugin_NoUserGiven() + { + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => 'val'); + + $this->backendPlugin1->save($value1); + + $this->assertSame($value1, $this->backendPlugin1->load()); + $this->assertSame(array(), $this->backendPlugin2->load()); + $this->assertSame(array(), $this->backendUser1->load()); + $this->assertSame(array(), $this->backendUser2->load()); + + $value2 = array('mytest' => 'test2'); + $this->backendPlugin2->save($value2); + + $this->assertSame($value1, $this->backendPlugin1->load()); + $this->assertSame($value2, $this->backendPlugin2->load()); + $this->assertSame(array(), $this->backendUser1->load()); + $this->assertSame(array(), $this->backendUser2->load()); + } + + public function test_save_ShouldOnlySaveForSpecificPluginAndUser() + { + $values = array_fill(0, count($this->allBackends), array()); + + foreach ($this->allBackends as $index => $backend) { + $values[$index] = $this->getExampleValues(); + $backend->save($values[$index]); + + foreach ($this->allBackends as $j => $backend2) { + $this->assertSame($values[$j], $backend2->load()); + } + } + } + + public function test_delete_shouldDeleteAllValuesButOnlyForSpecificPluginAndLogin() + { + $values = array(); + foreach ($this->allBackends as $index => $backend) { + $values[$index] = $this->getExampleValues(); + $backend->save($values[$index]); + $this->assertSame($values[$index], $backend->load()); + } + + foreach ($this->allBackends as $index => $backend) { + $backend->delete(); + $values[$index] = array(); + + // we make sure the values for all others are still set and only the current one was deleted + foreach ($this->allBackends as $j => $backend2) { + $this->assertSame($values[$j], $backend2->load()); + } + } + } + + public function test_save_DuplicateValuesShouldBeOverwritten() + { + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => 'val'); + + $this->backendPlugin1->save($value1); + + $this->assertSame($value1, $this->backendPlugin1->load()); + + $value2 = array('mytest' => 'test2', 'Mysetting2' => 'val', 'Mysetting1' => 'valueNew'); + $this->backendPlugin1->save($value2); + + $this->assertEquals($value2, $this->backendPlugin1->load()); + } + + public function test_save_NoLongerExistingValues_shouldBeRemoved() + { + $value = $this->saveValueForAllBackends(); + + // overwrite only user1 + $value2 = array('mytest' => 'test2', 'Mysetting1' => 'valueNew'); + $this->backendUser1->save($value2); + $this->assertEquals($value2, $this->backendUser1->load()); + + // make sure other backends remain unchanged + foreach ($this->allBackends as $backend) { + if ($backend !== $this->backendUser1) { + $this->assertSame($value, $backend->load()); + } + } + } + + public function test_save_load_ShouldBeAbleToSaveAndLoadArrayValues() + { + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => array('val', 'val7', 'val5')); + + $this->backendUser1->save($value1); + $this->assertEquals($value1, $this->backendUser1->load()); + } + + public function test_save_load_ShouldBeAbleToSaveAndLoadArrayValues_OnlyOneKey() + { + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => array('val')); + + $this->backendUser1->save($value1); + + + $value1 = array('Mysetting1' => 'value1', 'Mysetting2' => 'val'); + // it doesn't return an array for Mysetting2 but it is supposed to be casted to array by storage in this case + $this->assertEquals($value1, $this->backendUser1->load()); + } + + public function test_save_ShouldBeAbleToSaveBoolValues() + { + $value1 = array('Mysetting1' => true, 'Mysetting2' => array('val', 'val7', false, true, 'val5')); + + $this->backendUser1->save($value1); + + $value1 = array('Mysetting1' => '1', 'Mysetting2' => array('val', 'val7', '0', '1', 'val5')); + $this->assertEquals($value1, $this->backendUser1->load()); + } + + public function test_save_ShouldIgnoreNullValues() + { + $value1 = array('Mysetting1' => true, 'MySetting3' => null, 'Mysetting2' => array('val', null, true, 'val5')); + + $this->backendUser1->save($value1); + + $value1 = array('Mysetting1' => '1', 'Mysetting2' => array('val', '1', 'val5')); + $this->assertEquals($value1, $this->backendUser1->load()); + } + + public function test_removeAllUserSettingsForUser_shouldOnlyRemoveSettingsForThatUser() + { + $value = $this->saveValueForAllBackends(); + + PluginSettingsTable::removeAllUserSettingsForUser('user1'); + + foreach ($this->allBackends as $backend) { + if ($backend === $this->backendUser1) { + $this->assertSame(array(), $backend->load()); + } else { + $this->assertSame($value, $backend->load()); + } + } + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage No userLogin specified + */ + public function test_removeAllUserSettingsForUser_shouldThrowAnExceptionIfLoginIsEmpty() + { + PluginSettingsTable::removeAllUserSettingsForUser(''); + } + + public function test_removeAllSettingsForPlugin_shouldOnlyRemoveSettingsForThatPlugin() + { + $value = $this->saveValueForAllBackends(); + + PluginSettingsTable::removeAllSettingsForPlugin('MyPluginName'); + + foreach ($this->allBackends as $backend) { + if ($backend === $this->backendPlugin2) { + $this->assertSame($value, $backend->load()); + } else { + $this->assertSame(array(), $backend->load()); + } + } + } + + private function saveValueForAllBackends() + { + $value = array('Mysetting1' => 'value1', 'Mysetting2' => 'val'); + + foreach ($this->allBackends as $backend) { + $backend->save($value); + $this->assertSame($value, $backend->load()); + } + + return $value; + } + + private function getExampleValues() + { + return array('Mysetting3' => 'value3', 'Mysetting4' . rand(4,99) => 'val' . rand(0, 10)); + } +} diff --git a/tests/PHPUnit/Integration/Settings/Storage/Backend/SitesTableTest.php b/tests/PHPUnit/Integration/Settings/Storage/Backend/SitesTableTest.php new file mode 100644 index 0000000000..1b3000a443 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Storage/Backend/SitesTableTest.php @@ -0,0 +1,194 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Storage\Backend; + +use Piwik\Config; +use Piwik\Db; +use Piwik\Settings\Storage\Backend\SitesTable; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group Settings + * @group Backend + * @group Storage + */ +class SitesTableTest extends IntegrationTestCase +{ + + /** + * @var SitesTable + */ + private $backendSite1; + + /** + * @var SitesTable + */ + private $backendSite2; + + public function setUp() + { + parent::setUp(); + + $idSite1 = Fixture::createWebsite('2014-01-01 00:01:02'); + $idSite2 = Fixture::createWebsite('2014-01-01 00:01:02'); + + $this->backendSite1 = $this->createSettings($idSite1); + $this->backendSite2 = $this->createSettings($idSite2); + } + + private function createSettings($idSite) + { + return new SitesTable($idSite); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage No idSite given + */ + public function test_construct_shouldThrowAnException_IfPluginNameIsEmpty() + { + $this->createSettings(0); + } + + public function test_load_shouldHaveValuesByDefaultForExistingSites() + { + $this->assertFieldsLoaded(array('idsite' => '1'), $this->backendSite1); + $this->assertFieldsLoaded(array('idsite' => '2'), $this->backendSite2); + } + + /** + * @expectedException \Piwik\Exception\UnexpectedWebsiteFoundException + */ + public function test_load_shouldThrowException_IfSiteDoesNotExist() + { + $this->createSettings($idSite = 999)->load(); + } + + public function test_getStorageId_shouldIncludePluginNameAndLogin() + { + $this->assertSame('SitesTable_1', $this->backendSite1->getStorageId()); + $this->assertSame('SitesTable_2', $this->backendSite2->getStorageId()); + } + + public function test_save_ShouldOnlySaveForSpecificIdSite() + { + $value1 = array('ecommerce' => '1', 'sitesearch' => '0'); + $this->backendSite1->save($value1); + + $value2 = array('ecommerce' => '0', 'sitesearch' => '1'); + $this->backendSite2->save($value2); + + $this->assertFieldsLoaded($value1, $this->backendSite1); + $this->assertFieldsLoaded($value2, $this->backendSite2); + } + + public function test_delete_shouldNotDeleteAnything() + { + $value = $this->saveValueForAllBackends(); + + $this->backendSite1->delete(); + $this->backendSite2->delete(); + + $this->assertFieldsLoaded($value, $this->backendSite1); + $this->assertFieldsLoaded($value, $this->backendSite2); + } + + public function test_save_DuplicateValuesShouldBeOverwritten() + { + $value = array('ecommerce' => '0', 'sitesearch' => '1'); + + $this->backendSite1->save($value); + $this->assertFieldsLoaded($value, $this->backendSite1); + + $value = array('ecommerce' => '1', 'sitesearch' => '0'); + $this->backendSite1->save($value); + $this->assertFieldsLoaded($value, $this->backendSite1); + } + + public function test_save_ShouldNotRemoveAnyExistingUrls_WhenNoUrlsGiven() + { + $value = array('ecommerce' => '0', 'sitesearch' => '1'); + + $this->backendSite1->save($value); + + $value = array('urls' => array('http://piwik.net')); + $this->assertFieldsLoaded($value, $this->backendSite1); + } + + public function test_save_ShouldBeAbleToHandleBooleanValues() + { + $value = array('ecommerce' => true, 'sitesearch' => false); + $this->backendSite1->save($value); + + $value = array('ecommerce' => '1', 'sitesearch' => '0'); + $this->assertFieldsLoaded($value, $this->backendSite1); + } + + public function test_save_NotSetValues_ShouldRemain() + { + $value = array('ecommerce' => '0', 'sitesearch' => '1'); + $this->backendSite1->save($value); + $this->assertFieldsLoaded($value, $this->backendSite1); + + // make sure name is still set + $this->assertFieldsLoaded(array('name' => 'Piwik test'), $this->backendSite1); + } + + public function test_save_load_ShouldBeAbleToSaveAndLoadArrayValues() + { + $value1 = array( + 'sitesearch_keyword_parameters' => array('val', 'val7', 'val5'), + 'excluded_parameters' => array('val4', 'val17', 'val45'), + ); + + $this->backendSite1->save($value1); + $this->assertFieldsLoaded($value1, $this->backendSite1); + } + + public function test_save_SaveMainUrlAndUrlsCorrectly_ManyUrls() + { + $urls = array('piwik.org', 'demo.piwik.org', 'test.piwik.org'); + $value = array('urls' => $urls); + $this->backendSite1->save($value); + + $value = array('main_url' => 'piwik.org', 'urls' => $urls); + $this->assertFieldsLoaded($value, $this->backendSite1); + } + + public function test_save_SaveMainUrlAndUrlsCorrectly_OnlyOneUrlGiven() + { + $urls = array('piwik.org'); + $value = array('urls' => $urls); + $this->backendSite1->save($value); + + $value = array('main_url' => 'piwik.org', 'urls' => $urls); + $this->assertFieldsLoaded($value, $this->backendSite1); + } + + private function assertFieldsLoaded($expectedValues, SitesTable $backend) + { + $loaded = $backend->load(); + foreach ($expectedValues as $key => $value) { + $this->assertEquals($loaded[$key], $value); + } + } + + private function saveValueForAllBackends() + { + $value = array('ecommerce' => '1', 'sitesearch' => '0'); + + foreach (array($this->backendSite1, $this->backendSite2) as $backend) { + $backend->save($value); + $this->assertFieldsLoaded($value, $backend); + } + + return $value; + } +} diff --git a/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php b/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php index 9c2de66667..2f2590cdca 100644 --- a/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php +++ b/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php @@ -8,11 +8,16 @@ namespace Piwik\Tests\Integration\Settings\Storage; -use Piwik\Settings\Storage; +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Storage\Backend\Cache; +use Piwik\Settings\Storage\Backend\Null; +use Piwik\Settings\Storage\Backend\SitesTable; +use Piwik\Settings\Storage\Backend\MeasurableSettingsTable; +use Piwik\Settings\Storage\Backend\PluginSettingsTable; +use Piwik\Settings\Storage\Storage; use Piwik\Settings\Storage\Factory; use Piwik\SettingsServer; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; -use Piwik\Tracker\SettingsStorage; /** * @group Tracker @@ -23,41 +28,120 @@ use Piwik\Tracker\SettingsStorage; */ class FactoryTest extends IntegrationTestCase { + /** + * @var Factory + */ + private $factory; - public function test_make_shouldCreateDefaultInstance() + public function setUp() { - $storage = Factory::make('PluginName'); + parent::setUp(); + $this->factory = new Factory(); + } + + public function test_getPluginStorage_shouldReturnStorageWithPluginBackend() + { + $storage = $this->factory->getPluginStorage('PluginName', $login = 'user5'); + $this->assertTrue($storage instanceof Storage); + + $backend = $storage->getBackend(); + $this->assertTrue($backend instanceof PluginSettingsTable); + $this->assertSame('PluginSettings_PluginName_User_user5', $backend->getStorageId()); + } + + public function test_getPluginStorage_shouldDecorateWithCacheBackendInTrackerMode() + { + SettingsServer::setIsTrackerApiRequest(); + $storage = $this->factory->getPluginStorage('pluginName', 'userlogin'); + SettingsServer::setIsNotTrackerApiRequest(); + + $this->assertTrue($storage->getBackend() instanceof Cache); + } + + public function test_getMeasurableSettingsStorage_shouldReturnStorageWithMeasurableSettingsBackend() + { + $storage = $this->factory->getMeasurableSettingsStorage($idSite = 4, 'PluginNameFoo'); $this->assertTrue($storage instanceof Storage); + + $backend = $storage->getBackend(); + $this->assertTrue($backend instanceof MeasurableSettingsTable); + $this->assertSame('MeasurableSettings_4_PluginNameFoo', $backend->getStorageId()); } - public function test_make_shouldCreateTrackerInstance_IfInTrackerMode() + public function test_getMeasurableSettingsStorage_shouldDecorateWithCacheBackendInTrackerMode() { - $storage = $this->makeTrackerInstance(); + SettingsServer::setIsTrackerApiRequest(); + $storage = $this->factory->getMeasurableSettingsStorage($idSite = 4, 'PluginNameFoo'); + SettingsServer::setIsNotTrackerApiRequest(); - $this->assertTrue($storage instanceof SettingsStorage); + $this->assertTrue($storage->getBackend() instanceof Cache); } - public function test_make_shouldPassThePluginNameToTheStorage() + public function test_getMeasurableSettingsStorage_shouldReturnNonPersistentStorageWhenEmptySiteIsGiven() { - $storage = Factory::make('PluginName'); - $this->assertEquals('Plugin_PluginName_Settings', $storage->getOptionKey()); + $storage = $this->factory->getMeasurableSettingsStorage($idSite = 0, 'PluginNameFoo'); + $this->assertTrue($storage instanceof Storage); + + $backend = $storage->getBackend(); + $this->assertTrue($backend instanceof Null); + $this->assertSame('measurableSettings0#PluginNameFoo#nonpersistent', $backend->getStorageId()); } - public function test_make_shouldPassThePluginNameToTheSettingsStorage() + public function test_getSitesTable_shouldReturnStorageWithSitesTableBackend() { - $storage = $this->makeTrackerInstance(); + $storage = $this->factory->getSitesTable($idSite = 3); + $this->assertTrue($storage instanceof Storage); - $this->assertEquals('Plugin_PluginName_Settings', $storage->getOptionKey()); + $backend = $storage->getBackend(); + $this->assertTrue($backend instanceof SitesTable); + $this->assertSame('SitesTable_3', $backend->getStorageId()); } - private function makeTrackerInstance() + public function test_getSitesTable_shouldDecorateWithCacheBackendInTrackerMode() { SettingsServer::setIsTrackerApiRequest(); + $storage = $this->factory->getSitesTable($idSite = 3); + SettingsServer::setIsNotTrackerApiRequest(); + + $this->assertTrue($storage->getBackend() instanceof Cache); + } + + public function test_getSitesTable_shouldReturnNonPersistentStorageWhenEmptySiteIsGiven() + { + $storage = $this->factory->getSitesTable($idSite = 0); + $this->assertTrue($storage instanceof Storage); + + $backend = $storage->getBackend(); + $this->assertTrue($backend instanceof Null); + $this->assertSame('sitesTable#0#nonpersistent', $backend->getStorageId()); + } - $storage = Factory::make('PluginName'); + public function test_getNonPersistentStorage_shouldReturnStorageWithNullBackend() + { + $storage = $this->factory->getNonPersistentStorage('myKey'); + $this->assertTrue($storage instanceof Storage); + $backend = $storage->getBackend(); + $this->assertTrue($backend instanceof Null); + $this->assertSame('myKey', $backend->getStorageId()); + } + + public function test_getNonPersistentStorage_shouldNotDecorateWithCacheBackendInTrackerMode() + { + SettingsServer::setIsTrackerApiRequest(); + $storage = $this->factory->getNonPersistentStorage('anykey'); SettingsServer::setIsNotTrackerApiRequest(); - return $storage; + $this->assertTrue($storage->getBackend() instanceof Null); } + + public function test_getNonPersistentStorage_shouldNotUseCache() + { + $storage = $this->factory->getNonPersistentStorage('myKey'); + $storage->setValue('mytest', 'myval'); + + $storage = $this->factory->getNonPersistentStorage('myKey'); + $this->assertSame('', $storage->getValue('mytest', $default = '', FieldConfig::TYPE_STRING)); + } + } diff --git a/tests/PHPUnit/Integration/Settings/Storage/StorageTest.php b/tests/PHPUnit/Integration/Settings/Storage/StorageTest.php new file mode 100644 index 0000000000..7e014c9002 --- /dev/null +++ b/tests/PHPUnit/Integration/Settings/Storage/StorageTest.php @@ -0,0 +1,153 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration\Settings\Storage; + +use Piwik\Settings\FieldConfig; +use Piwik\Settings\Storage\Backend\BackendInterface; +use Piwik\Settings\Storage\Storage; +use Piwik\Tests\Framework\Mock\Settings\FakeBackend; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Tracker\Cache as TrackerCache; + +/** + * @group PluginSettings + * @group Storage + */ +class StorageTest extends IntegrationTestCase +{ + /** + * @var Storage + */ + protected $storage; + + /** + * @var BackendInterface + */ + protected $backend; + + /** + * @var string + */ + protected $settingName = 'myname'; + + public function setUp() + { + parent::setUp(); + + $this->backend = new FakeBackend('MyTestId'); + $this->backend->save(array($this->settingName => 'value1')); + $this->storage = $this->buildStorage(); + } + + public function test_getBackend() + { + $this->assertSame($this->backend, $this->storage->getBackend()); + } + + public function test_getValue_shouldReturnDefaultValue_IfNoValueIsSet() + { + $value = $this->storage->getValue('UnkNownFielD', $default = '123', FieldConfig::TYPE_STRING); + $this->assertSame($default, $value); + } + + public function test_getValue_shouldReturnDefaultValue_AndNotCastDefaultValue() + { + $value = $this->storage->getValue('UnkNownFielD', $default = '123', FieldConfig::TYPE_INT); + $this->assertSame($default, $value); + } + + public function test_getValue_shouldReturnASavedValueFromBackend() + { + $value = $this->getValueFromStorage($this->settingName); + $this->assertSame('value1', $value); + } + + public function test_setValue_getValue_shouldSetAndGetActualValue() + { + $this->storage->setValue($this->settingName, 'myRandomVal'); + $value = $this->getValueFromStorage($this->settingName); + $this->assertEquals('myRandomVal', $value); + } + + public function test_setValue_getValue_shouldCastValueWhenGettingTheValue() + { + $this->storage->setValue($this->settingName, '1'); + $value = $this->getValueFromStorage($this->settingName, FieldConfig::TYPE_BOOL); + $this->assertTrue($value); + } + + public function test_setValue_shouldNotSaveItInDatabase() + { + $loaded = $this->backend->load(); + $this->storage->setValue($this->settingName, 'myRandomVal'); + + $this->assertSame($loaded, $this->loadValuesFromBackend()); + } + + public function test_save_shouldPersistValueInDatabase() + { + $this->storage->setValue($this->settingName, 'myRandomVal'); + $this->storage->save(); + + $this->assertEquals(array($this->settingName => 'myRandomVal'), + $this->loadValuesFromBackend()); + } + + public function test_save_shouldPersistMultipleValues_ContainingInt() + { + $this->storage->setValue($this->settingName, 'myRandomVal'); + $this->storage->setValue('mySecondName', 5); + $this->storage->save(); + + $this->assertEquals(array($this->settingName => 'myRandomVal', 'mySecondName' => 5), + $this->loadValuesFromBackend()); + } + + public function test_save_shouldNotClearTrackerCacheEntries_IfThereWasNoChange() + { + TrackerCache::setCacheGeneral(array('testSetting' => 1)); + + $this->assertArrayHasKey('testSetting', TrackerCache::getCacheGeneral()); + + $this->storage->save(); + + $this->assertArrayHasKey('testSetting', TrackerCache::getCacheGeneral()); + } + + public function test_save_shouldClearTrackerCacheEntries_IfThereWasActuallyAChange() + { + TrackerCache::setCacheGeneral(array('testSetting' => 1)); + + $this->assertArrayHasKey('testSetting', TrackerCache::getCacheGeneral()); + + $this->storage->setValue('myTest', 5); // it will save only when there was actually a change + $this->storage->save(); + + $this->assertArrayNotHasKey('testSetting', TrackerCache::getCacheGeneral()); + } + + private function getValueFromStorage($settingName, $type = null) + { + if (!isset($type)) { + $type = FieldConfig::TYPE_STRING; + } + return $this->storage->getValue($settingName, $default = '', $type); + } + + protected function buildStorage() + { + return new Storage($this->backend); + } + + protected function loadValuesFromBackend() + { + return $this->backend->load(); + } + +} diff --git a/tests/PHPUnit/Integration/Settings/StorageTest.php b/tests/PHPUnit/Integration/Settings/StorageTest.php deleted file mode 100644 index 20a5652e67..0000000000 --- a/tests/PHPUnit/Integration/Settings/StorageTest.php +++ /dev/null @@ -1,162 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Tests\Integration\Settings; - -use Piwik\Option; -use Piwik\Settings\Storage; -use Piwik\Settings\Setting; - -/** - * @group PluginSettings - * @group Storage - */ -class StorageTest extends IntegrationTestCase -{ - /** - * @var Storage - */ - protected $storage; - - /** - * @var Setting - */ - protected $setting; - - public function setUp() - { - parent::setUp(); - - $this->setSuperUser(); - $this->storage = $this->buildStorage(); - $this->setting = $this->buildUserSetting('myname', 'My Name'); - } - - public function test_constructor_shouldNotEstablishADatabaseConnection() - { - $this->assertNotDbConnectionCreated(); - - $this->buildStorage(); - - $this->assertNotDbConnectionCreated(); - } - - public function test_getValue_shouldEstablishADatabaseConnection() - { - $this->assertNotDbConnectionCreated(); - - $this->storage->getValue($this->setting); - - $this->assertDbConnectionCreated(); - } - - public function test_setValue_shouldEstablishADatabaseConnection() - { - $this->assertNotDbConnectionCreated(); - - $this->storage->setValue($this->setting, 5); - - $this->assertDbConnectionCreated(); - } - - public function test_deleteValue_shouldEstablishADatabaseConnection() - { - $this->assertNotDbConnectionCreated(); - - $this->storage->deleteValue($this->setting, 5); - - $this->assertDbConnectionCreated(); - } - - public function test_deleteAll_shouldEstablishADatabaseConnection() - { - $this->assertNotDbConnectionCreated(); - - $this->storage->deleteAllValues(); - - $this->assertDbConnectionCreated(); - } - - public function test_save_shouldEstablishADatabaseConnection() - { - $this->assertNotDbConnectionCreated(); - - $this->storage->save(); - - $this->assertDbConnectionCreated(); - } - - public function test_getValue_shouldReturnNullByDefault() - { - $value = $this->storage->getValue($this->setting); - $this->assertNull($value); - } - - public function test_getValue_shouldReturnADefaultValueIfOneIsSet() - { - $this->setting->defaultValue = 194.34; - $value = $this->storage->getValue($this->setting); - $this->assertSame(194.34, $value); - } - - public function test_setValue_getValue_shouldSetAndGetActualValue() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - $value = $this->storage->getValue($this->setting); - $this->assertEquals('myRandomVal', $value); - } - - public function test_setValue_shouldNotSaveItInDatabase() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - - $this->assertFalse($this->getValueFromOptionTable()); - } - - public function test_save_shouldPersistValueInDatabase() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - $this->storage->save(); - - $this->assertEquals('a:1:{s:22:"myname#superUserLogin#";s:11:"myRandomVal";}', $this->getValueFromOptionTable()); - } - - public function test_save_shouldPersistMultipleValues_ContainingInt() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - $this->storage->setValue($this->buildUserSetting('mySecondName', 'My Name'), 5); - $this->storage->save(); - - $this->assertEquals('a:2:{s:22:"myname#superUserLogin#";s:11:"myRandomVal";s:28:"mySecondName#superUserLogin#";i:5;}', $this->getValueFromOptionTable()); - } - - public function test_deleteAll_ShouldRemoveTheEntireEntry() - { - $this->storage->setValue($this->setting, 'myRandomVal'); - $this->storage->save(); - $this->storage->deleteAllValues(); - - $this->assertFalse($this->getValueFromOptionTable()); - } - - public function test_getOptionKey_shouldContainThePluginName() - { - $this->assertEquals('Plugin_PluginName_Settings', $this->storage->getOptionKey()); - } - - protected function buildStorage() - { - return new Storage('PluginName'); - } - - protected function getValueFromOptionTable() - { - return Option::get($this->storage->getOptionKey()); - } - -} diff --git a/tests/PHPUnit/Integration/Settings/UserSettingTest.php b/tests/PHPUnit/Integration/Settings/UserSettingTest.php deleted file mode 100644 index fa49d96ab3..0000000000 --- a/tests/PHPUnit/Integration/Settings/UserSettingTest.php +++ /dev/null @@ -1,249 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Tests\Integration\Settings; - -use Piwik\Db; -use Piwik\Settings\UserSetting; -use Piwik\Plugin\Settings as PluginSettings; - -/** - * @group PluginSettings - * @group Settings - * @group UserSetting - */ -class UserSettingTest extends IntegrationTestCase -{ - public function test_constructor_shouldNotEstablishADatabaseConnection() - { - $this->assertNotDbConnectionCreated(); - - new UserSetting('name', 'title'); - - $this->assertNotDbConnectionCreated(); - } - - public function test_constructor_shouldEstablishADatabaseConnection_AsSoonAsWeGetAValue() - { - $this->setSuperUser(); - Db::destroyDatabaseObject(); - - $setting = $this->buildUserSetting('testSetting', 'Test Setting'); - - $this->assertNotDbConnectionCreated(); - - $setting->getValue($setting); - - $this->assertDbConnectionCreated(); - } - - public function test_constructor_shouldEstablishADatabaseConnection_AsSoonAsWeSetAValue() - { - $this->setSuperUser(); - Db::destroyDatabaseObject(); - - $setting = $this->buildUserSetting('testSetting', 'Test Setting'); - $settings = $this->createSettingsInstance(); - $settings->addSetting($setting); - - $this->assertNotDbConnectionCreated(); - - $setting->setValue('5'); - - $this->assertDbConnectionCreated(); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed - */ - public function test_setSettingValue_shouldThrowException_IfAnonymousIsTryingToSetASettingWhichNeedsUserPermission() - { - $this->setAnonymousUser(); - $setting = $this->addUserSetting('mysystem', 'mytitle'); - - $setting->setValue(2); - } - - public function test_setSettingValue_shouldSucceed_IfUserIsTryingToSetASettingWhichNeedsUserPermission() - { - $this->setUser(); - $setting = $this->addUserSetting('mysystem', 'mytitle'); - $setting->setValue(2); - - $this->assertSettingHasValue($setting, 2); - } - - public function test_setSettingValue_shouldCastValue_IfTypeIsSetButNoFilter() - { - $this->setUser(); - - // cast to INT - $setting = $this->addUserSetting('mysystem', 'mytitle'); - $setting->type = PluginSettings::TYPE_INT; - $setting->setValue('31xm42'); - $this->assertSettingHasValue($setting, 31, 'integer'); - - // ARRAY - $setting->type = PluginSettings::TYPE_ARRAY; - $setting->setValue('31xm42'); - $this->assertSettingHasValue($setting, array('31xm42'), 'array'); - - // BOOL - $setting->type = PluginSettings::TYPE_BOOL; - $setting->setValue('1'); - $this->assertSettingHasValue($setting, true, 'boolean'); - - // FLOAT - $setting->type = PluginSettings::TYPE_FLOAT; - $setting->setValue('1.21'); - $this->assertSettingHasValue($setting, 1.21, 'float'); - - // STRING - $setting->type = PluginSettings::TYPE_STRING; - $setting->setValue('31xm42'); - $this->assertSettingHasValue($setting, '31xm42'); - } - - public function test_setSettingValue_shouldApplyFilterAndNotCast_IfAFilterIsSet() - { - $this->setUser(); - - $setting = $this->buildUserSetting('mysystem', 'mytitle'); - $setting->type = PluginSettings::TYPE_INT; - - $self = $this; - $setting->transform = function ($value, $userSetting) use ($self, $setting) { - $self->assertEquals('31xm42', $value); - $self->assertEquals($setting, $userSetting); - - return '43939kmf3m3'; - }; - - $setting->setValue('31xm42'); - - // should not be casted to int - $this->assertSettingHasValue($setting, '43939kmf3m3', 'string'); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Validation Fail - */ - public function test_setSettingValue_shouldValidateAValue_IfAFilterIsSet() - { - $this->setUser(); - - $setting = $this->buildUserSetting('mysystem', 'mytitle'); - $setting->type = PluginSettings::TYPE_INT; - - $self = $this; - $setting->validate = function ($value, $userSetting) use ($self, $setting) { - $self->assertEquals('31xm42', $value); - $self->assertEquals($setting, $userSetting); - - throw new \Exception('Validation Fail'); - }; - - $setting->setValue('31xm42'); - } - - public function test_getSettingValue_shouldReturnUncastedDefaultValue_IfNoValueIsSet() - { - $this->setUser(); - - $setting = $this->addUserSetting('mydefaultsystem', 'mytitle'); - $setting->type = PluginSettings::TYPE_INT; - $setting->defaultValue ='mytestvalue'; - - // should not be casted to int - $this->assertSettingHasValue($setting, 'mytestvalue', 'string'); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed - */ - public function test_getSettingValue_shouldThrowException_IfGivenSettingDoesNotExist() - { - $this->setAnonymousUser(); - $setting = $this->buildUserSetting('myusersetting', 'mytitle'); - $setting->getValue(); - } - - public function test_getSettingValue_shouldReturnValue_IfValueExistsAndUserHasPermission() - { - $this->setUser(); - $setting = $this->addUserSetting('myusersetting', 'mytitle'); - $setting->type = PluginSettings::TYPE_ARRAY; - $setting->setValue(array(2,3,4)); - - $this->assertSettingHasValue($setting, array(2,3,4)); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed - */ - public function test_removeSettingValue_shouldThrowException_IfUserHasNotEnoughUserPermissions() - { - $this->setAnonymousUser(); - $setting = $this->addUserSetting('myusersetting', 'mytitle'); - $setting->removeValue(); - } - - public function test_removeSettingValue_shouldRemoveValue_IfValueExistsAndHasEnoughPermissions() - { - $this->setUser(); - $setting = $this->addUserSetting('myusersetting', 'mytitle'); - $setting->setValue('12345657'); - $this->assertSettingHasValue($setting, '12345657'); - - $setting->removeValue(); - $this->assertSettingHasValue($setting, null); - } - - public function test_userSetting_shouldGenerateDifferentKey_ForDifferentUsers() - { - $this->setSuperUser(); - - $user1 = $this->buildUserSetting('myname', 'mytitle', 'user1'); - $user2 = $this->buildUserSetting('myname', 'mytitle', '_user2_'); - $user3 = $this->buildUserSetting('myname', 'mytitle'); - - $this->assertEquals('myname#user1#', $user1->getKey()); - $this->assertEquals('myname#_user2_#', $user2->getKey()); - $this->assertEquals('myname#superUserLogin#', $user3->getKey()); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage You do not have the permission to read the settings of a different user - */ - public function test_userSetting_shouldThrowException_IfSomeoneTriesToReadSettingsFromAnotherUserAndIsNotSuperuser() - { - $this->setUser(); - - $this->buildUserSetting('myname', 'mytitle', 'myRandomName'); - } - - public function test_userSetting_shouldBeAbleToSetLoginAndChangeValues_IfUserHasSuperUserAccess() - { - $this->setSuperUser(); - - $setting = $this->buildUserSetting('myname', 'mytitle', 'myRandomName'); - $this->settings->addSetting($setting); - - $setting->setValue(5); - $this->assertSettingHasValue($setting, 5); - - $setting->removeValue(); - $this->assertSettingHasValue($setting, null); - } - -} diff --git a/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php b/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php deleted file mode 100644 index dd17e67314..0000000000 --- a/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Tests\Integration\Tracker; - -use Piwik\Cache as PiwikCache; -use Piwik\Settings\Storage; -use Piwik\Tests\Integration\Settings\StorageTest; -use Piwik\Tracker\Cache; -use Piwik\Tracker\SettingsStorage; - -/** - * @group PluginSettings - * @group Storage - * @group SettingStorage - */ -class SettingsStorageTest extends StorageTest -{ - - public function test_storageShouldLoadSettingsFromCacheIfPossible() - { - $this->setSettingValueInCache('my0815RandomName'); - - $this->assertEquals('my0815RandomName', $this->storage->getValue($this->setting)); - } - - public function test_storageShouldLoadSettingsFromCache_AndNotCreateADatabaseInstance() - { - $this->setSettingValueInCache('my0815RandomName'); - - $this->storage->getValue($this->setting); - - $this->assertNotDbConnectionCreated(); - } - - public function test_clearCache_shouldActuallyClearTheCacheEntry() - { - $this->setSettingValueInCache('my0815RandomName'); - - $this->assertTrue($this->hasCache()); - - SettingsStorage::clearCache(); - - $this->assertFalse($this->hasCache()); - } - - private function hasCache() - { - return $this->getCache()->contains($this->storage->getOptionKey()); - } - - public function test_storageShouldNotCastAnyCachedValue() - { - $this->setSettingValueInCache(5); - - $this->assertEquals(5, $this->storage->getValue($this->setting)); - } - - public function test_storageShouldFallbackToDatebaseInCaseNoCacheExists() - { - $this->storage->setValue($this->setting, 5); - $this->storage->save(); - - $this->assertFalse($this->hasCache()); - $this->assertNotFalse($this->getValueFromOptionTable()); // make sure saved in db - - $storage = $this->buildStorage(); - $this->assertEquals(5, $storage->getValue($this->setting)); - $this->assertTrue($this->hasCache()); - } - - public function test_storageCreateACacheEntryIfNoCacheExistsYet() - { - $cache = Cache::getCacheGeneral(); - $this->assertArrayNotHasKey('settingsStorage', $cache); // make sure there is no cache entry yet - - $this->setSettingValueAndMakeSureCacheGetsCreated('myVal'); - - $cache = $this->getCache()->fetch($this->storage->getOptionKey()); - - $this->assertEquals(array( - $this->setting->getKey() => 'myVal' - ), $cache); - } - - protected function buildStorage() - { - return new SettingsStorage('PluginName'); - } - - private function getCache() - { - return PiwikCache::getEagerCache(); - } - - private function setSettingValueInCache($value) - { - $cache = $this->getCache(); - $cache->save($this->storage->getOptionKey(), array( - $this->setting->getKey() => $value - )); - } - - private function setSettingValueAndMakeSureCacheGetsCreated($value) - { - $this->storage->setValue($this->setting, $value); - $this->storage->save(); - - $storage = $this->buildStorage(); - $storage->getValue($this->setting); // force creation of cache by loading settings - } - -} diff --git a/tests/PHPUnit/Integration/Tracker/VisitTest.php b/tests/PHPUnit/Integration/Tracker/VisitTest.php index f4bc1fd5ed..92baae429e 100644 --- a/tests/PHPUnit/Integration/Tracker/VisitTest.php +++ b/tests/PHPUnit/Integration/Tracker/VisitTest.php @@ -34,7 +34,10 @@ class VisitTest extends IntegrationTestCase FakeAccess::$superUser = true; Manager::getInstance()->loadTrackerPlugins(); - Manager::getInstance()->loadPlugin('SitesManager'); + $pluginNames = array_keys(Manager::getInstance()->getLoadedPlugins()); + $pluginNames[] = 'SitesManager'; + $pluginNames[] = 'WebsiteMeasurable'; + Manager::getInstance()->loadPlugins($pluginNames); Visit::$dimensions = null; } diff --git a/tests/PHPUnit/System/BackwardsCompatibility1XTest.php b/tests/PHPUnit/System/BackwardsCompatibility1XTest.php index 12a60f3821..79525cd28c 100644 --- a/tests/PHPUnit/System/BackwardsCompatibility1XTest.php +++ b/tests/PHPUnit/System/BackwardsCompatibility1XTest.php @@ -13,7 +13,6 @@ use Piwik\Plugins\VisitFrequency\API as VisitFrequencyApi; use Piwik\Tests\Framework\TestCase\SystemTestCase; use Piwik\Tests\Fixtures\SqlDump; use Piwik\Tests\Framework\Fixture; -use Piwik\Tests\Framework\TestingEnvironmentVariables; /** * Tests that Piwik 2.0 works w/ data from Piwik 1.12. diff --git a/tests/PHPUnit/System/ImportLogsTest.php b/tests/PHPUnit/System/ImportLogsTest.php index 725b2fba16..56ffe1c0b7 100755 --- a/tests/PHPUnit/System/ImportLogsTest.php +++ b/tests/PHPUnit/System/ImportLogsTest.php @@ -8,7 +8,6 @@ namespace Piwik\Tests\System; use Piwik\Access; -use Piwik\Common; use Piwik\Plugins\SitesManager\API; use Piwik\Tests\Framework\Fixture; use Piwik\Tests\Framework\TestCase\SystemTestCase; diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CorePluginsAdmin.getSystemSettings.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CorePluginsAdmin.getSystemSettings.xml new file mode 100644 index 0000000000..5bf91acc3d --- /dev/null +++ b/tests/PHPUnit/System/expected/test_ImportLogs__CorePluginsAdmin.getSystemSettings.xml @@ -0,0 +1,329 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>metric</name> + <title>Metric to display</title> + <value>nb_visits</value> + <defaultValue>nb_visits</defaultValue> + <type>string</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <nb_visits>Visits</nb_visits> + <nb_actions>Actions</nb_actions> + <visitors>Visitors</visitors> + </availableValues> + <description>Choose the metric that should be displayed in the browser tab</description> + <inlineHelp /> + <introduction>Only Super Users can change the following settings:</introduction> + <condition /> + </row> + <row> + <name>browsers</name> + <title>Supported Browsers</title> + <value> + <row>firefox</row> + <row>chromium</row> + <row>safari</row> + </value> + <defaultValue> + <row>firefox</row> + <row>chromium</row> + <row>safari</row> + </defaultValue> + <type>array</type> + <uiControl>multiselect</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <firefox>Firefox</firefox> + <chromium>Chromium</chromium> + <safari>safari</safari> + </availableValues> + <description>The value will be only displayed in the following browsers</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>description</name> + <title>Description for value</title> + <value>This is the value: +Another line</value> + <defaultValue>This is the value: +Another line</defaultValue> + <type>string</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>This description will be displayed next to the value</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>password</name> + <title>API password</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>password</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>Password for the 3rd API where we fetch the value</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>AnonymousPiwikUsageMeasurement</pluginName> + <settings> + <row> + <name>canUserOptOut</name> + <title>Let users disable anonymous tracking</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, logged in users can opt out in their plugin settings. Anonymous users cannot opt out.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>trackToPiwik</name> + <title>Send usage data to Piwik.org</title> + <value>0</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, anonymized usage data will be sent to demo-anonymous.piwik.org and the tracked data can be viewed there (the data is public). The collected data is used to improve Piwik. Thank you for making Piwik better!</description> + <inlineHelp /> + <introduction>Send anonmyized usage data to the creator of Piwik</introduction> + <condition /> + </row> + <row> + <name>ownPiwikSiteId</name> + <title>Site Id</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If specified, anonymized usage data will be sent to the specified site in this Piwik.</description> + <inlineHelp /> + <introduction>Send anonymize usage data to this Piwik</introduction> + <condition /> + </row> + <row> + <name>anonymizeSelfPiwik</name> + <title>Anonymize tracking requests</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>customSiteUrl</name> + <title>Piwik Url</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <placeHolder>eg. http://example.com/piwik</placeHolder> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction>Send anonymize usage data to a custom Piwik</introduction> + <condition /> + </row> + <row> + <name>customSiteId</name> + <title>Site Id</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <placeHolder>eg. "2"</placeHolder> + </uiControlAttributes> + <availableValues /> + <description>If a URL and Site Id is specified, usage data will be sent to the custom Piwik instance.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>anonymizeCustomPiwik</name> + <title>Anonymize tracking requests</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>QueuedTracking</pluginName> + <settings> + <row> + <name>redisHost</name> + <title>Redis host</title> + <value>127.0.0.1</value> + <defaultValue>127.0.0.1</defaultValue> + <type>string</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>300</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Remote host of the Redis server. Max 300 characters are allowed.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisPort</name> + <title>Redis port</title> + <value>6379</value> + <defaultValue>6379</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Port the Redis server is running on. Value should be between 1 and 65535.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisDatabase</name> + <title>Redis database</title> + <value>15</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>In case you are using Redis for caching make sure to use a different database.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisPassword</name> + <title>Redis password</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>password</uiControl> + <uiControlAttributes> + <size>100</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Password set on the Redis server, if any. Redis can be instructed to require a password before allowing clients to execute commands.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>queueEnabled</name> + <title>Queue enabled</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>If enabled, all tracking requests will be written into a queue instead of the directly into the database. Requires a Redis server and phpredis PHP extension.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>numQueueWorkers</name> + <title>Number of queue workers</title> + <value>4</value> + <defaultValue>1</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Number of allowed maximum queue workers. Accepts a number between 1 and 16. Best practice is to set the number of CPUs you want to make available for queue processing. Be aware you need to make sure to start the workers manually. We recommend to not use 9-15 workers, rather use 8 or 16 as the queue might not be distributed evenly into different queues. DO NOT USE more than 1 worker if you make use the UserId feature when tracking see https://github.com/piwik/piwik/issues/7691</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>numRequestsToProcess</name> + <title>Number of requests that are processed in one batch</title> + <value>25</value> + <defaultValue>25</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>3</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Defines how many requests will be picked out of the queue and processed at once. Enter a number which is >= 1.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>processDuringTrackingRequest</name> + <title>Process during tracking request</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>If enabled, we will process all requests within a queue during a normal tracking request once there are enough requests in the queue. This will not slow down the tracking request. If disabled, you have to setup a cronjob that executes the "./console queuedtracking:process" console command eg every minute to process the queue.</inlineHelp> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CorePluginsAdmin.getUserSettings.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CorePluginsAdmin.getUserSettings.xml new file mode 100644 index 0000000000..0d18c736bb --- /dev/null +++ b/tests/PHPUnit/System/expected/test_ImportLogs__CorePluginsAdmin.getUserSettings.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>autoRefresh</name> + <title>Auto refresh</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, the value will be automatically refreshed depending on the specified interval</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>refreshInterval</name> + <title>Refresh Interval</title> + <value>30</value> + <defaultValue>30</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>3</size> + </uiControlAttributes> + <availableValues /> + <description>Defines how often the value should be updated</description> + <inlineHelp>Enter a number which is >= 15</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>color</name> + <title>Color</title> + <value>red</value> + <defaultValue>red</defaultValue> + <type>string</type> + <uiControl>radio</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <red>Red</red> + <blue>Blue</blue> + <green>Green</green> + </availableValues> + <description>Pick your favourite color</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>AnonymousPiwikUsageMeasurement</pluginName> + <settings> + <row> + <name>userTrackingEnabled</name> + <title>Piwik usage tracking enabled</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, anonymous usage data will be tracked. For example which pages are viewed and which reports are used most often. For more information contact your system administrator.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CorePluginsAdmin.getSystemSettings.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CorePluginsAdmin.getSystemSettings.xml new file mode 100644 index 0000000000..5bf91acc3d --- /dev/null +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CorePluginsAdmin.getSystemSettings.xml @@ -0,0 +1,329 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>metric</name> + <title>Metric to display</title> + <value>nb_visits</value> + <defaultValue>nb_visits</defaultValue> + <type>string</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <nb_visits>Visits</nb_visits> + <nb_actions>Actions</nb_actions> + <visitors>Visitors</visitors> + </availableValues> + <description>Choose the metric that should be displayed in the browser tab</description> + <inlineHelp /> + <introduction>Only Super Users can change the following settings:</introduction> + <condition /> + </row> + <row> + <name>browsers</name> + <title>Supported Browsers</title> + <value> + <row>firefox</row> + <row>chromium</row> + <row>safari</row> + </value> + <defaultValue> + <row>firefox</row> + <row>chromium</row> + <row>safari</row> + </defaultValue> + <type>array</type> + <uiControl>multiselect</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <firefox>Firefox</firefox> + <chromium>Chromium</chromium> + <safari>safari</safari> + </availableValues> + <description>The value will be only displayed in the following browsers</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>description</name> + <title>Description for value</title> + <value>This is the value: +Another line</value> + <defaultValue>This is the value: +Another line</defaultValue> + <type>string</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>This description will be displayed next to the value</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>password</name> + <title>API password</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>password</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>Password for the 3rd API where we fetch the value</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>AnonymousPiwikUsageMeasurement</pluginName> + <settings> + <row> + <name>canUserOptOut</name> + <title>Let users disable anonymous tracking</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, logged in users can opt out in their plugin settings. Anonymous users cannot opt out.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>trackToPiwik</name> + <title>Send usage data to Piwik.org</title> + <value>0</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, anonymized usage data will be sent to demo-anonymous.piwik.org and the tracked data can be viewed there (the data is public). The collected data is used to improve Piwik. Thank you for making Piwik better!</description> + <inlineHelp /> + <introduction>Send anonmyized usage data to the creator of Piwik</introduction> + <condition /> + </row> + <row> + <name>ownPiwikSiteId</name> + <title>Site Id</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If specified, anonymized usage data will be sent to the specified site in this Piwik.</description> + <inlineHelp /> + <introduction>Send anonymize usage data to this Piwik</introduction> + <condition /> + </row> + <row> + <name>anonymizeSelfPiwik</name> + <title>Anonymize tracking requests</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>customSiteUrl</name> + <title>Piwik Url</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <placeHolder>eg. http://example.com/piwik</placeHolder> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction>Send anonymize usage data to a custom Piwik</introduction> + <condition /> + </row> + <row> + <name>customSiteId</name> + <title>Site Id</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <placeHolder>eg. "2"</placeHolder> + </uiControlAttributes> + <availableValues /> + <description>If a URL and Site Id is specified, usage data will be sent to the custom Piwik instance.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>anonymizeCustomPiwik</name> + <title>Anonymize tracking requests</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>QueuedTracking</pluginName> + <settings> + <row> + <name>redisHost</name> + <title>Redis host</title> + <value>127.0.0.1</value> + <defaultValue>127.0.0.1</defaultValue> + <type>string</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>300</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Remote host of the Redis server. Max 300 characters are allowed.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisPort</name> + <title>Redis port</title> + <value>6379</value> + <defaultValue>6379</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Port the Redis server is running on. Value should be between 1 and 65535.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisDatabase</name> + <title>Redis database</title> + <value>15</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>In case you are using Redis for caching make sure to use a different database.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisPassword</name> + <title>Redis password</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>password</uiControl> + <uiControlAttributes> + <size>100</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Password set on the Redis server, if any. Redis can be instructed to require a password before allowing clients to execute commands.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>queueEnabled</name> + <title>Queue enabled</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>If enabled, all tracking requests will be written into a queue instead of the directly into the database. Requires a Redis server and phpredis PHP extension.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>numQueueWorkers</name> + <title>Number of queue workers</title> + <value>4</value> + <defaultValue>1</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Number of allowed maximum queue workers. Accepts a number between 1 and 16. Best practice is to set the number of CPUs you want to make available for queue processing. Be aware you need to make sure to start the workers manually. We recommend to not use 9-15 workers, rather use 8 or 16 as the queue might not be distributed evenly into different queues. DO NOT USE more than 1 worker if you make use the UserId feature when tracking see https://github.com/piwik/piwik/issues/7691</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>numRequestsToProcess</name> + <title>Number of requests that are processed in one batch</title> + <value>25</value> + <defaultValue>25</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>3</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Defines how many requests will be picked out of the queue and processed at once. Enter a number which is >= 1.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>processDuringTrackingRequest</name> + <title>Process during tracking request</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>If enabled, we will process all requests within a queue during a normal tracking request once there are enough requests in the queue. This will not slow down the tracking request. If disabled, you have to setup a cronjob that executes the "./console queuedtracking:process" console command eg every minute to process the queue.</inlineHelp> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CorePluginsAdmin.getUserSettings.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CorePluginsAdmin.getUserSettings.xml new file mode 100644 index 0000000000..0d18c736bb --- /dev/null +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CorePluginsAdmin.getUserSettings.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>autoRefresh</name> + <title>Auto refresh</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, the value will be automatically refreshed depending on the specified interval</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>refreshInterval</name> + <title>Refresh Interval</title> + <value>30</value> + <defaultValue>30</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>3</size> + </uiControlAttributes> + <availableValues /> + <description>Defines how often the value should be updated</description> + <inlineHelp>Enter a number which is >= 15</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>color</name> + <title>Color</title> + <value>red</value> + <defaultValue>red</defaultValue> + <type>string</type> + <uiControl>radio</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <red>Red</red> + <blue>Blue</blue> + <green>Green</green> + </availableValues> + <description>Pick your favourite color</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>AnonymousPiwikUsageMeasurement</pluginName> + <settings> + <row> + <name>userTrackingEnabled</name> + <title>Piwik usage tracking enabled</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, anonymous usage data will be tracked. For example which pages are viewed and which reports are used most often. For more information contact your system administrator.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getAvailableMeasurableTypes.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getAvailableMeasurableTypes.xml index b4433cb0f2..eb12eef5d2 100644 --- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getAvailableMeasurableTypes.xml +++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getAvailableMeasurableTypes.xml @@ -5,5 +5,233 @@ <name>Website</name> <description>A website consists of web pages typically served from a single web domain.</description> <howToSetupUrl>?module=CoreAdminHome&action=trackingCodeGenerator</howToSetupUrl> + <settings> + <row> + <pluginName>WebsiteMeasurable</pluginName> + <settings> + <row> + <name>urls</name> + <title>URLs</title> + <value> + <row>http://siteUrl.com/</row> + <row>http://siteUrl2.com/</row> + </value> + <defaultValue> + <row>http://siteUrl.com/</row> + <row>http://siteUrl2.com/</row> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + <cols>25</cols> + <rows>3</rows> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>It is recommended, but not required, to specify the various URLs, one per line, that your visitors use to access this website. Alias URLs for a website will not appear in the Referrers > Websites report. Note that it is not necessary to specify the URLs with and without 'www' as Piwik automatically considers both.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>exclude_unknown_urls</name> + <title>Only track visits and actions when the action URL starts with one of the above URLs.</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>When enabled, Piwik will only track internal actions when the Page URL is one of the known URLs for your website. This prevents people from spamming your analytics with URLs for other websites.<br />The domain and the path has to be an exact match and each valid subdomain has to be specified separately. For example when the known URLs are 'http://example.com/path' and 'http://good.example.com', tracking requests for 'http://example.com/otherpath' or 'http://bad.example.com' are ignored.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>keep_url_fragment</name> + <title>Keep Page URL fragments when tracking Page URLs</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>string</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <row>No (Default)</row> + <row>Yes</row> + <row>No</row> + </availableValues> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>excluded_ips</name> + <title>Excluded IPs</title> + <value> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + <cols>20</cols> + <rows>4</rows> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Enter the list of IPs, one per line, that you wish to exclude from being tracked by Piwik. You can use wildcards, eg. 1.2.3.* or 1.2.*.*<br /><br />Your current IP address is <i>127.0.0.1</i></inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>excluded_parameters</name> + <title>Excluded Parameters</title> + <value> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + <cols>20</cols> + <rows>4</rows> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Enter the list of URL Query Parameters, one per line, to exclude from the Page URLs reports.<br /><br />Piwik will automatically exclude the common session parameters (phpsessid, sessionid, ...).</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>excluded_user_agents</name> + <title>Excluded User Agents</title> + <value> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + <cols>20</cols> + <rows>4</rows> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Enter the list of user agents to exclude from being tracked by Piwik.<br /><br />If the visitor's user agent string contains any of the strings you specify, the visitor will be excluded from Piwik.<br />You can use this to exclude some bots from being tracked.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>sitesearch</name> + <title>Site Search</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>integer</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <row key="1">Site Search tracking enabled</row> + <row key="0">Do not track Site Search</row> + </availableValues> + <description /> + <inlineHelp>You can use Piwik to track and report what visitors are searching in your website's internal search engine.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>use_default_site_search_params</name> + <title>Use <a href='#globalSettings'>default</a> Site Search parameters</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>Query parameter (Default): q,query,s,search,searchword,k,keyword & Category parameter: </description> + <inlineHelp /> + <introduction /> + <condition>1 && sitesearch</condition> + </row> + <row> + <name>sitesearch_keyword_parameters</name> + <title>Query parameter</title> + <value> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>text</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Enter a comma separated list of all query parameter names containing the site search keyword.</inlineHelp> + <introduction /> + <condition>sitesearch && !use_default_site_search_params</condition> + </row> + <row> + <name>sitesearch_category_parameters</name> + <title>Category parameter</title> + <value> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>text</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>(optional)<br /><br />You may enter a comma-separated list of query parameters specifying the search category.</inlineHelp> + <introduction /> + <condition>1 && sitesearch && !use_default_site_search_params</condition> + </row> + <row> + <name>ecommerce</name> + <title>Ecommerce</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <row>Not an Ecommerce site</row> + <row>Ecommerce enabled</row> + </availableValues> + <description /> + <inlineHelp>When enabled, the "Goals" report will have a new "Ecommerce" section.<br />Piwik allows for advanced Ecommerce Analytics tracking & reporting. Learn more about <a href='http://piwik.org/docs/ecommerce-analytics/' target='_blank'> Ecommerce Analytics</a>.</inlineHelp> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>contact_email</name> + <title>Contact email addresses</title> + <value> + </value> + <defaultValue> + </defaultValue> + <type>array</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + </settings> </row> </result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CorePluginsAdmin.getSystemSettings.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CorePluginsAdmin.getSystemSettings.xml new file mode 100644 index 0000000000..5bf91acc3d --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CorePluginsAdmin.getSystemSettings.xml @@ -0,0 +1,329 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>metric</name> + <title>Metric to display</title> + <value>nb_visits</value> + <defaultValue>nb_visits</defaultValue> + <type>string</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <nb_visits>Visits</nb_visits> + <nb_actions>Actions</nb_actions> + <visitors>Visitors</visitors> + </availableValues> + <description>Choose the metric that should be displayed in the browser tab</description> + <inlineHelp /> + <introduction>Only Super Users can change the following settings:</introduction> + <condition /> + </row> + <row> + <name>browsers</name> + <title>Supported Browsers</title> + <value> + <row>firefox</row> + <row>chromium</row> + <row>safari</row> + </value> + <defaultValue> + <row>firefox</row> + <row>chromium</row> + <row>safari</row> + </defaultValue> + <type>array</type> + <uiControl>multiselect</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <firefox>Firefox</firefox> + <chromium>Chromium</chromium> + <safari>safari</safari> + </availableValues> + <description>The value will be only displayed in the following browsers</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>description</name> + <title>Description for value</title> + <value>This is the value: +Another line</value> + <defaultValue>This is the value: +Another line</defaultValue> + <type>string</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>This description will be displayed next to the value</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>password</name> + <title>API password</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>password</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>Password for the 3rd API where we fetch the value</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>AnonymousPiwikUsageMeasurement</pluginName> + <settings> + <row> + <name>canUserOptOut</name> + <title>Let users disable anonymous tracking</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, logged in users can opt out in their plugin settings. Anonymous users cannot opt out.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>trackToPiwik</name> + <title>Send usage data to Piwik.org</title> + <value>0</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, anonymized usage data will be sent to demo-anonymous.piwik.org and the tracked data can be viewed there (the data is public). The collected data is used to improve Piwik. Thank you for making Piwik better!</description> + <inlineHelp /> + <introduction>Send anonmyized usage data to the creator of Piwik</introduction> + <condition /> + </row> + <row> + <name>ownPiwikSiteId</name> + <title>Site Id</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If specified, anonymized usage data will be sent to the specified site in this Piwik.</description> + <inlineHelp /> + <introduction>Send anonymize usage data to this Piwik</introduction> + <condition /> + </row> + <row> + <name>anonymizeSelfPiwik</name> + <title>Anonymize tracking requests</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>customSiteUrl</name> + <title>Piwik Url</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <placeHolder>eg. http://example.com/piwik</placeHolder> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction>Send anonymize usage data to a custom Piwik</introduction> + <condition /> + </row> + <row> + <name>customSiteId</name> + <title>Site Id</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <placeHolder>eg. "2"</placeHolder> + </uiControlAttributes> + <availableValues /> + <description>If a URL and Site Id is specified, usage data will be sent to the custom Piwik instance.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>anonymizeCustomPiwik</name> + <title>Anonymize tracking requests</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>QueuedTracking</pluginName> + <settings> + <row> + <name>redisHost</name> + <title>Redis host</title> + <value>127.0.0.1</value> + <defaultValue>127.0.0.1</defaultValue> + <type>string</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>300</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Remote host of the Redis server. Max 300 characters are allowed.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisPort</name> + <title>Redis port</title> + <value>6379</value> + <defaultValue>6379</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Port the Redis server is running on. Value should be between 1 and 65535.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisDatabase</name> + <title>Redis database</title> + <value>15</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>In case you are using Redis for caching make sure to use a different database.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisPassword</name> + <title>Redis password</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>password</uiControl> + <uiControlAttributes> + <size>100</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Password set on the Redis server, if any. Redis can be instructed to require a password before allowing clients to execute commands.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>queueEnabled</name> + <title>Queue enabled</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>If enabled, all tracking requests will be written into a queue instead of the directly into the database. Requires a Redis server and phpredis PHP extension.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>numQueueWorkers</name> + <title>Number of queue workers</title> + <value>4</value> + <defaultValue>1</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Number of allowed maximum queue workers. Accepts a number between 1 and 16. Best practice is to set the number of CPUs you want to make available for queue processing. Be aware you need to make sure to start the workers manually. We recommend to not use 9-15 workers, rather use 8 or 16 as the queue might not be distributed evenly into different queues. DO NOT USE more than 1 worker if you make use the UserId feature when tracking see https://github.com/piwik/piwik/issues/7691</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>numRequestsToProcess</name> + <title>Number of requests that are processed in one batch</title> + <value>25</value> + <defaultValue>25</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>3</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Defines how many requests will be picked out of the queue and processed at once. Enter a number which is >= 1.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>processDuringTrackingRequest</name> + <title>Process during tracking request</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>If enabled, we will process all requests within a queue during a normal tracking request once there are enough requests in the queue. This will not slow down the tracking request. If disabled, you have to setup a cronjob that executes the "./console queuedtracking:process" console command eg every minute to process the queue.</inlineHelp> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CorePluginsAdmin.getUserSettings.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CorePluginsAdmin.getUserSettings.xml new file mode 100644 index 0000000000..0d18c736bb --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CorePluginsAdmin.getUserSettings.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>autoRefresh</name> + <title>Auto refresh</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, the value will be automatically refreshed depending on the specified interval</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>refreshInterval</name> + <title>Refresh Interval</title> + <value>30</value> + <defaultValue>30</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>3</size> + </uiControlAttributes> + <availableValues /> + <description>Defines how often the value should be updated</description> + <inlineHelp>Enter a number which is >= 15</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>color</name> + <title>Color</title> + <value>red</value> + <defaultValue>red</defaultValue> + <type>string</type> + <uiControl>radio</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <red>Red</red> + <blue>Blue</blue> + <green>Green</green> + </availableValues> + <description>Pick your favourite color</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>AnonymousPiwikUsageMeasurement</pluginName> + <settings> + <row> + <name>userTrackingEnabled</name> + <title>Piwik usage tracking enabled</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, anonymous usage data will be tracked. For example which pages are viewed and which reports are used most often. For more information contact your system administrator.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit__CorePluginsAdmin.getSystemSettings.xml b/tests/PHPUnit/System/expected/test_noVisit__CorePluginsAdmin.getSystemSettings.xml new file mode 100644 index 0000000000..5bf91acc3d --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit__CorePluginsAdmin.getSystemSettings.xml @@ -0,0 +1,329 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>metric</name> + <title>Metric to display</title> + <value>nb_visits</value> + <defaultValue>nb_visits</defaultValue> + <type>string</type> + <uiControl>select</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <nb_visits>Visits</nb_visits> + <nb_actions>Actions</nb_actions> + <visitors>Visitors</visitors> + </availableValues> + <description>Choose the metric that should be displayed in the browser tab</description> + <inlineHelp /> + <introduction>Only Super Users can change the following settings:</introduction> + <condition /> + </row> + <row> + <name>browsers</name> + <title>Supported Browsers</title> + <value> + <row>firefox</row> + <row>chromium</row> + <row>safari</row> + </value> + <defaultValue> + <row>firefox</row> + <row>chromium</row> + <row>safari</row> + </defaultValue> + <type>array</type> + <uiControl>multiselect</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <firefox>Firefox</firefox> + <chromium>Chromium</chromium> + <safari>safari</safari> + </availableValues> + <description>The value will be only displayed in the following browsers</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>description</name> + <title>Description for value</title> + <value>This is the value: +Another line</value> + <defaultValue>This is the value: +Another line</defaultValue> + <type>string</type> + <uiControl>textarea</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>This description will be displayed next to the value</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>password</name> + <title>API password</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>password</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>Password for the 3rd API where we fetch the value</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>AnonymousPiwikUsageMeasurement</pluginName> + <settings> + <row> + <name>canUserOptOut</name> + <title>Let users disable anonymous tracking</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, logged in users can opt out in their plugin settings. Anonymous users cannot opt out.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>trackToPiwik</name> + <title>Send usage data to Piwik.org</title> + <value>0</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, anonymized usage data will be sent to demo-anonymous.piwik.org and the tracked data can be viewed there (the data is public). The collected data is used to improve Piwik. Thank you for making Piwik better!</description> + <inlineHelp /> + <introduction>Send anonmyized usage data to the creator of Piwik</introduction> + <condition /> + </row> + <row> + <name>ownPiwikSiteId</name> + <title>Site Id</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If specified, anonymized usage data will be sent to the specified site in this Piwik.</description> + <inlineHelp /> + <introduction>Send anonymize usage data to this Piwik</introduction> + <condition /> + </row> + <row> + <name>anonymizeSelfPiwik</name> + <title>Anonymize tracking requests</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>customSiteUrl</name> + <title>Piwik Url</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <placeHolder>eg. http://example.com/piwik</placeHolder> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction>Send anonymize usage data to a custom Piwik</introduction> + <condition /> + </row> + <row> + <name>customSiteId</name> + <title>Site Id</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <placeHolder>eg. "2"</placeHolder> + </uiControlAttributes> + <availableValues /> + <description>If a URL and Site Id is specified, usage data will be sent to the custom Piwik instance.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>anonymizeCustomPiwik</name> + <title>Anonymize tracking requests</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>QueuedTracking</pluginName> + <settings> + <row> + <name>redisHost</name> + <title>Redis host</title> + <value>127.0.0.1</value> + <defaultValue>127.0.0.1</defaultValue> + <type>string</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>300</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Remote host of the Redis server. Max 300 characters are allowed.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisPort</name> + <title>Redis port</title> + <value>6379</value> + <defaultValue>6379</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Port the Redis server is running on. Value should be between 1 and 65535.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisDatabase</name> + <title>Redis database</title> + <value>15</value> + <defaultValue>0</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>In case you are using Redis for caching make sure to use a different database.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>redisPassword</name> + <title>Redis password</title> + <value /> + <defaultValue /> + <type>string</type> + <uiControl>password</uiControl> + <uiControlAttributes> + <size>100</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Password set on the Redis server, if any. Redis can be instructed to require a password before allowing clients to execute commands.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>queueEnabled</name> + <title>Queue enabled</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>If enabled, all tracking requests will be written into a queue instead of the directly into the database. Requires a Redis server and phpredis PHP extension.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>numQueueWorkers</name> + <title>Number of queue workers</title> + <value>4</value> + <defaultValue>1</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>5</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Number of allowed maximum queue workers. Accepts a number between 1 and 16. Best practice is to set the number of CPUs you want to make available for queue processing. Be aware you need to make sure to start the workers manually. We recommend to not use 9-15 workers, rather use 8 or 16 as the queue might not be distributed evenly into different queues. DO NOT USE more than 1 worker if you make use the UserId feature when tracking see https://github.com/piwik/piwik/issues/7691</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>numRequestsToProcess</name> + <title>Number of requests that are processed in one batch</title> + <value>25</value> + <defaultValue>25</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>3</size> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>Defines how many requests will be picked out of the queue and processed at once. Enter a number which is >= 1.</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>processDuringTrackingRequest</name> + <title>Process during tracking request</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description /> + <inlineHelp>If enabled, we will process all requests within a queue during a normal tracking request once there are enough requests in the queue. This will not slow down the tracking request. If disabled, you have to setup a cronjob that executes the "./console queuedtracking:process" console command eg every minute to process the queue.</inlineHelp> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit__CorePluginsAdmin.getUserSettings.xml b/tests/PHPUnit/System/expected/test_noVisit__CorePluginsAdmin.getUserSettings.xml new file mode 100644 index 0000000000..0d18c736bb --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit__CorePluginsAdmin.getUserSettings.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <pluginName>ExampleSettingsPlugin</pluginName> + <settings> + <row> + <name>autoRefresh</name> + <title>Auto refresh</title> + <value>0</value> + <defaultValue>0</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, the value will be automatically refreshed depending on the specified interval</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + <row> + <name>refreshInterval</name> + <title>Refresh Interval</title> + <value>30</value> + <defaultValue>30</defaultValue> + <type>integer</type> + <uiControl>text</uiControl> + <uiControlAttributes> + <size>3</size> + </uiControlAttributes> + <availableValues /> + <description>Defines how often the value should be updated</description> + <inlineHelp>Enter a number which is >= 15</inlineHelp> + <introduction /> + <condition /> + </row> + <row> + <name>color</name> + <title>Color</title> + <value>red</value> + <defaultValue>red</defaultValue> + <type>string</type> + <uiControl>radio</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues> + <red>Red</red> + <blue>Blue</blue> + <green>Green</green> + </availableValues> + <description>Pick your favourite color</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> + <row> + <pluginName>AnonymousPiwikUsageMeasurement</pluginName> + <settings> + <row> + <name>userTrackingEnabled</name> + <title>Piwik usage tracking enabled</title> + <value>1</value> + <defaultValue>1</defaultValue> + <type>boolean</type> + <uiControl>checkbox</uiControl> + <uiControlAttributes> + </uiControlAttributes> + <availableValues /> + <description>If enabled, anonymous usage data will be tracked. For example which pages are viewed and which reports are used most often. For more information contact your system administrator.</description> + <inlineHelp /> + <introduction /> + <condition /> + </row> + </settings> + </row> +</result>
\ No newline at end of file diff --git a/tests/UI/expected-ui-screenshots b/tests/UI/expected-ui-screenshots -Subproject 6d644223c2094370677b11a0ee7ca2e474969d8 +Subproject 6101bf6344dc3efbbd7d38fa814e3ff3ebc132b diff --git a/tests/UI/specs/UIIntegration_spec.js b/tests/UI/specs/UIIntegration_spec.js index 3c108061b3..085081e9f0 100644 --- a/tests/UI/specs/UIIntegration_spec.js +++ b/tests/UI/specs/UIIntegration_spec.js @@ -487,13 +487,13 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik? it('should load the plugin settings admin page correctly', function (done) { expect.screenshot('admin_plugin_settings').to.be.captureSelector('.pageWrap', function (page) { - page.load("?" + generalParams + "&module=CoreAdminHome&action=adminPluginSettings"); + page.load("?" + generalParams + "&module=CoreAdminHome&action=generalSettings"); }, done); }); it('should load the plugin settings user page correctly', function (done) { expect.screenshot('user_plugin_settings').to.be.captureSelector('.pageWrap', function (page) { - page.load("?" + generalParams + "&module=CoreAdminHome&action=userPluginSettings"); + page.load("?" + generalParams + "&module=UsersManager&action=userSettings"); }, done); }); |