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 /core | |
parent | 6ba622a68a26792af8cc22131f488f7ff5189d2c (diff) |
refs #7983 let plugins add or remove fields to websites and better settings api
Diffstat (limited to 'core')
56 files changed, 2336 insertions, 1492 deletions
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()) { |