378 lines
12 KiB
PHP
378 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* Piwik - Open source web analytics
|
|
*
|
|
* @link http://piwik.org
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*
|
|
*/
|
|
namespace Piwik\Plugin;
|
|
|
|
use Piwik\Option;
|
|
use Piwik\Piwik;
|
|
use Piwik\Settings\Setting;
|
|
use Piwik\Settings\StorageInterface;
|
|
use Piwik\SettingsServer;
|
|
|
|
/**
|
|
* 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 implements StorageInterface
|
|
{
|
|
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();
|
|
|
|
/**
|
|
* Array containing all plugin settings values: Array( [setting-key] => [setting-value] ).
|
|
*
|
|
* @var array
|
|
*/
|
|
private $settingsValues = array();
|
|
|
|
private $introduction;
|
|
private $pluginName;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $pluginName The name of the plugin these settings are for.
|
|
*/
|
|
public function __construct($pluginName)
|
|
{
|
|
$this->pluginName = $pluginName;
|
|
|
|
$this->init();
|
|
$this->loadSettings();
|
|
}
|
|
|
|
/**
|
|
* 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->canBeDisplayedForCurrentUser();
|
|
});
|
|
|
|
uasort($settings, function ($setting1, $setting2) use ($settings) {
|
|
/** @var Setting $setting1 */ /** @var Setting $setting2 */
|
|
if ($setting1->getOrder() == $setting2->getOrder()) {
|
|
// preserve order for settings having same order
|
|
foreach ($settings 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;
|
|
}
|
|
|
|
/**
|
|
* Saves (persists) the current setting values in the database.
|
|
*/
|
|
public function save()
|
|
{
|
|
Option::set($this->getOptionKey(), serialize($this->settingsValues));
|
|
}
|
|
|
|
/**
|
|
* Removes all settings for this plugin from the database. Useful when uninstalling
|
|
* a plugin.
|
|
*/
|
|
public function removeAllPluginSettings()
|
|
{
|
|
Piwik::checkUserHasSuperUserAccess();
|
|
|
|
Option::delete($this->getOptionKey());
|
|
$this->settingsValues = array();
|
|
}
|
|
|
|
/**
|
|
* 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 getSettingValue(Setting $setting)
|
|
{
|
|
$this->checkIsValidSetting($setting->getName());
|
|
|
|
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 setSettingValue(Setting $setting, $value)
|
|
{
|
|
$this->checkIsValidSetting($setting->getName());
|
|
|
|
if ($setting->validate && $setting->validate instanceof \Closure) {
|
|
call_user_func($setting->validate, $value, $setting);
|
|
}
|
|
|
|
if ($setting->transform && $setting->transform instanceof \Closure) {
|
|
$value = call_user_func($setting->transform, $value, $setting);
|
|
} elseif (isset($setting->type)) {
|
|
settype($value, $setting->type);
|
|
}
|
|
|
|
$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 removeSettingValue(Setting $setting)
|
|
{
|
|
$this->checkHasEnoughPermission($setting);
|
|
|
|
$key = $setting->getKey();
|
|
|
|
if (array_key_exists($key, $this->settingsValues)) {
|
|
unset($this->settingsValues[$key]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
if (!ctype_alnum($setting->getName())) {
|
|
$msg = sprintf('The setting name "%s" in plugin "%s" is not valid. Only alpha and numerical characters are allowed', $setting->getName(), $this->pluginName);
|
|
throw new \Exception($msg);
|
|
}
|
|
|
|
if (array_key_exists($setting->getName(), $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);
|
|
|
|
$this->settings[$setting->getName()] = $setting;
|
|
}
|
|
|
|
private function getOptionKey()
|
|
{
|
|
return 'Plugin_' . $this->pluginName . '_Settings';
|
|
}
|
|
|
|
private function loadSettings()
|
|
{
|
|
$values = Option::get($this->getOptionKey());
|
|
|
|
if (!empty($values)) {
|
|
$this->settingsValues = unserialize($values);
|
|
}
|
|
}
|
|
|
|
private function checkIsValidSetting($name)
|
|
{
|
|
$setting = $this->getSetting($name);
|
|
|
|
if (empty($setting)) {
|
|
throw new \Exception(sprintf('The setting %s does not exist', $name));
|
|
}
|
|
|
|
$this->checkHasEnoughPermission($setting);
|
|
}
|
|
|
|
/**
|
|
* @param $name
|
|
* @return Setting|null
|
|
*/
|
|
private function getSetting($name)
|
|
{
|
|
if (array_key_exists($name, $this->settings)) {
|
|
return $this->settings[$name];
|
|
}
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
/**
|
|
* @param $setting
|
|
* @throws \Exception
|
|
*/
|
|
private function checkHasEnoughPermission(Setting $setting)
|
|
{
|
|
// When the request is a Tracker request, allow plugins to read/write settings
|
|
if(SettingsServer::isTrackerApiRequest()) {
|
|
return;
|
|
}
|
|
|
|
if (!$setting->canBeDisplayedForCurrentUser()) {
|
|
$errorMsg = Piwik::translate('CoreAdminHome_PluginSettingChangeNotAllowed', array($setting->getName(), $this->pluginName));
|
|
throw new \Exception($errorMsg);
|
|
}
|
|
}
|
|
|
|
private function setDefaultTypeAndFieldIfNeeded(Setting $setting)
|
|
{
|
|
if (!is_null($setting->uiControlType) && is_null($setting->type)) {
|
|
$setting->type = $this->getDefaultType($setting->uiControlType);
|
|
} elseif (!is_null($setting->type) && is_null($setting->uiControlType)) {
|
|
$setting->uiControlType = $this->getDefaultCONTROL($setting->type);
|
|
} elseif (is_null($setting->uiControlType) && is_null($setting->type)) {
|
|
$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);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|