PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/stats/core/Settings/Setting.php

https://bitbucket.org/webstar1987923/mycampaignsio
PHP | 342 lines | 180 code | 49 blank | 113 comment | 27 complexity | c3511a307a535755945858fefec0e9d8 MD5 | raw file
Possible License(s): BSD-3-Clause, MPL-2.0-no-copyleft-exception, GPL-3.0, GPL-2.0, WTFPL, BSD-2-Clause, LGPL-2.1, Apache-2.0, MIT, AGPL-3.0
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Settings;
  10. use Piwik\Piwik;
  11. use Piwik\Settings\Storage\Storage;
  12. use Exception;
  13. use Piwik\Validators\BaseValidator;
  14. /**
  15. * Base setting type class.
  16. *
  17. * @api
  18. */
  19. class Setting
  20. {
  21. /**
  22. * The name of the setting
  23. * @var string
  24. */
  25. protected $name;
  26. /**
  27. * Null while not initialized, bool otherwise.
  28. * @var null|bool
  29. */
  30. protected $hasWritePermission = null;
  31. /**
  32. * @var Storage
  33. */
  34. protected $storage;
  35. /**
  36. * @var string
  37. */
  38. protected $pluginName;
  39. /**
  40. * @var FieldConfig
  41. */
  42. protected $config;
  43. /**
  44. * @var \Closure|null
  45. */
  46. protected $configureCallback;
  47. /**
  48. * @var mixed
  49. */
  50. protected $defaultValue;
  51. /**
  52. * @var string
  53. */
  54. protected $type;
  55. /**
  56. * Constructor.
  57. *
  58. * @param string $name The setting's persisted name. Only alphanumeric characters are allowed, eg,
  59. * `'refreshInterval'`.
  60. * @param mixed $defaultValue Default value for this setting if no value was specified.
  61. * @param string $type Eg an array, int, ... see SettingConfig::TYPE_* constants
  62. * @param string $pluginName The name of the plugin the setting belongs to
  63. * @throws Exception
  64. */
  65. public function __construct($name, $defaultValue, $type, $pluginName)
  66. {
  67. if (!ctype_alnum(str_replace('_', '', $name))) {
  68. $msg = sprintf('The setting name "%s" in plugin "%s" is invalid. Only underscores, alpha and numerical characters are allowed', $name, $pluginName);
  69. throw new Exception($msg);
  70. }
  71. $this->name = $name;
  72. $this->type = $type;
  73. $this->pluginName = $pluginName;
  74. $this->setDefaultValue($defaultValue);
  75. }
  76. /**
  77. * Get the name of the setting.
  78. * @return string
  79. */
  80. public function getName()
  81. {
  82. return $this->name;
  83. }
  84. /**
  85. * Get the PHP type of the setting.
  86. * @return string
  87. */
  88. public function getType()
  89. {
  90. return $this->type;
  91. }
  92. /**
  93. * @internal
  94. * @ignore
  95. * @param $callback
  96. */
  97. public function setConfigureCallback($callback)
  98. {
  99. $this->configureCallback = $callback;
  100. $this->config = null;
  101. }
  102. /**
  103. * @return mixed
  104. */
  105. public function getDefaultValue()
  106. {
  107. return $this->defaultValue;
  108. }
  109. /**
  110. * Sets/overwrites the current default value
  111. * @param string $defaultValue
  112. */
  113. public function setDefaultValue($defaultValue)
  114. {
  115. $this->defaultValue = $defaultValue;
  116. }
  117. /**
  118. * @internal
  119. * @param Storage $storage
  120. */
  121. public function setStorage(Storage $storage)
  122. {
  123. $this->storage = $storage;
  124. }
  125. /**
  126. * @internal
  127. * @ignore
  128. * @return FieldConfig
  129. * @throws Exception
  130. */
  131. public function configureField()
  132. {
  133. if (!$this->config) {
  134. $this->config = new FieldConfig();
  135. if ($this->configureCallback) {
  136. call_user_func($this->configureCallback, $this->config);
  137. }
  138. $this->setUiControlIfNeeded($this->config);
  139. $this->checkType($this->config);
  140. }
  141. return $this->config;
  142. }
  143. /**
  144. * Set whether setting is writable or not. For example to hide setting from the UI set it to false.
  145. *
  146. * @param bool $isWritable
  147. */
  148. public function setIsWritableByCurrentUser($isWritable)
  149. {
  150. $this->hasWritePermission = (bool) $isWritable;
  151. }
  152. /**
  153. * Returns `true` if this setting is writable for the current user, `false` if otherwise. In case it returns
  154. * writable for the current user it will be visible in the Plugin settings UI.
  155. *
  156. * @return bool
  157. */
  158. public function isWritableByCurrentUser()
  159. {
  160. return (bool) $this->hasWritePermission;
  161. }
  162. /**
  163. * Saves (persists) the value for this setting in the database if a value has been actually set.
  164. */
  165. public function save()
  166. {
  167. $this->storage->save();
  168. }
  169. /**
  170. * Returns the previously persisted setting value. If no value was set, the default value
  171. * is returned.
  172. *
  173. * @return mixed
  174. */
  175. public function getValue()
  176. {
  177. return $this->storage->getValue($this->name, $this->defaultValue, $this->type);
  178. }
  179. /**
  180. * Sets and persists this setting's value overwriting any existing value.
  181. *
  182. * Before a value is actually set it will be made sure the current user is allowed to change the value. The value
  183. * will be first validated either via a system built-in validate method or via a set {@link FieldConfig::$validate}
  184. * custom method. Afterwards the value will be transformed via a possibly specified {@link FieldConfig::$transform}
  185. * method. Before storing the actual value, the value will be converted to the actually specified {@link $type}.
  186. *
  187. * @param mixed $value
  188. * @throws \Exception If the current user is not allowed to change the value of this setting.
  189. */
  190. public function setValue($value)
  191. {
  192. $this->checkHasEnoughWritePermission();
  193. $config = $this->configureField();
  194. $this->validateValue($value);
  195. if ($config->transform && $config->transform instanceof \Closure) {
  196. $value = call_user_func($config->transform, $value, $this);
  197. }
  198. if (isset($this->type) && !is_null($value)) {
  199. settype($value, $this->type);
  200. }
  201. $this->storage->setValue($this->name, $value);
  202. }
  203. private function validateValue($value)
  204. {
  205. $config = $this->configureField();
  206. if (!empty($config->validators)) {
  207. BaseValidator::check($config->title, $value, $config->validators);
  208. }
  209. if ($config->validate && $config->validate instanceof \Closure) {
  210. call_user_func($config->validate, $value, $this);
  211. } elseif (is_array($config->availableValues)) {
  212. if (is_bool($value) && $value) {
  213. $value = '1';
  214. } elseif (is_bool($value)) {
  215. $value = '0';
  216. }
  217. // TODO move error message creation to a subclass, eg in MeasurableSettings we do not want to mention plugin name
  218. $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed',
  219. array(strip_tags($config->title), $this->pluginName));
  220. if (is_array($value) && $this->type === FieldConfig::TYPE_ARRAY) {
  221. foreach ($value as $val) {
  222. if (!array_key_exists($val, $config->availableValues)) {
  223. throw new \Exception($errorMsg);
  224. }
  225. }
  226. } else {
  227. if (!array_key_exists($value, $config->availableValues)) {
  228. throw new \Exception($errorMsg);
  229. }
  230. }
  231. } elseif ($this->type === FieldConfig::TYPE_INT || $this->type === FieldConfig::TYPE_FLOAT) {
  232. if (!is_numeric($value)) {
  233. $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed',
  234. array(strip_tags($config->title), $this->pluginName));
  235. throw new \Exception($errorMsg);
  236. }
  237. } elseif ($this->type === FieldConfig::TYPE_BOOL) {
  238. if (!in_array($value, array(true, false, '0', '1', 0, 1), true)) {
  239. $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed',
  240. array(strip_tags($config->title), $this->pluginName));
  241. throw new \Exception($errorMsg);
  242. }
  243. }
  244. }
  245. /**
  246. * @throws \Exception
  247. */
  248. private function checkHasEnoughWritePermission()
  249. {
  250. if (!$this->isWritableByCurrentUser()) {
  251. $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingChangeNotAllowed', array($this->name, $this->pluginName));
  252. throw new \Exception($errorMsg);
  253. }
  254. }
  255. private function setUiControlIfNeeded(FieldConfig $field)
  256. {
  257. if (!isset($field->uiControl)) {
  258. $defaultControlTypes = array(
  259. FieldConfig::TYPE_INT => FieldConfig::UI_CONTROL_TEXT,
  260. FieldConfig::TYPE_FLOAT => FieldConfig::UI_CONTROL_TEXT,
  261. FieldConfig::TYPE_STRING => FieldConfig::UI_CONTROL_TEXT,
  262. FieldConfig::TYPE_BOOL => FieldConfig::UI_CONTROL_CHECKBOX,
  263. FieldConfig::TYPE_ARRAY => FieldConfig::UI_CONTROL_MULTI_SELECT,
  264. );
  265. if (isset($defaultControlTypes[$this->type])) {
  266. $field->uiControl = $defaultControlTypes[$this->type];
  267. } else {
  268. $field->uiControl = FieldConfig::UI_CONTROL_TEXT;
  269. }
  270. }
  271. }
  272. private function checkType(FieldConfig $field)
  273. {
  274. if ($field->uiControl === FieldConfig::UI_CONTROL_MULTI_SELECT &&
  275. $this->type !== FieldConfig::TYPE_ARRAY) {
  276. throw new Exception('Type must be an array when using a multi select');
  277. }
  278. if ($field->uiControl === FieldConfig::UI_CONTROL_MULTI_TUPLE &&
  279. $this->type !== FieldConfig::TYPE_ARRAY) {
  280. throw new Exception('Type must be an array when using a multi pair');
  281. }
  282. $types = array(
  283. FieldConfig::TYPE_INT,
  284. FieldConfig::TYPE_FLOAT,
  285. FieldConfig::TYPE_STRING,
  286. FieldConfig::TYPE_BOOL,
  287. FieldConfig::TYPE_ARRAY
  288. );
  289. if (!in_array($this->type, $types)) {
  290. throw new Exception('Type does not exist');
  291. }
  292. }
  293. }