Subversion Repositories oidplus

Rev

Rev 1177 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. namespace ViaThinkSoft\OIDplus;
  21.  
  22. // phpcs:disable PSR1.Files.SideEffects
  23. \defined('INSIDE_OIDPLUS') or die;
  24. // phpcs:enable PSR1.Files.SideEffects
  25.  
  26. // OIDplusConfig contains settings that are stored in the database.
  27. // Not to be confused with OIDplusBaseConfig which is the basic ("static")
  28. // configuration stored in userdata/baseconfig/config.inc.php,
  29. // e.g. database access credentials.
  30. class OIDplusConfig extends OIDplusBaseClass implements OIDplusGetterSetterInterface {
  31.  
  32.         /**
  33.          *
  34.          */
  35.         /*public*/ const PROTECTION_EDITABLE = 0;
  36.  
  37.         /**
  38.          *
  39.          */
  40.         /*public*/ const PROTECTION_READONLY = 1;
  41.  
  42.         /**
  43.          *
  44.          */
  45.         /*public*/ const PROTECTION_HIDDEN   = 2;
  46.  
  47.         /**
  48.          * @var bool
  49.          */
  50.         protected $configTableReadOnce = false; // this ensures that all $values and $descriptions were read
  51.  
  52.         /**
  53.          * @var array
  54.          */
  55.         protected $values = array();
  56.  
  57.         /**
  58.          * @var array
  59.          */
  60.         protected $descriptions = array();
  61.  
  62.         /**
  63.          * @var array
  64.          */
  65.         protected $protectSettings = array();
  66.  
  67.         /**
  68.          * @var array
  69.          */
  70.         protected $visibleSettings = array();
  71.  
  72.         /**
  73.          * @var array
  74.          */
  75.         protected $validateCallbacks = array();
  76.  
  77.         /**
  78.          * @param string $name
  79.          * @param string $description
  80.          * @param string $init_value
  81.          * @param int $protection
  82.          * @param callable|null $validateCallback
  83.          * @return void
  84.          * @throws OIDplusException
  85.          */
  86.         public function prepareConfigKey(string $name, string $description, string $init_value, int $protection, callable $validateCallback=null) {
  87.                 // Check if the protection flag is valid
  88.                 switch ($protection) {
  89.                         case OIDplusConfig::PROTECTION_EDITABLE:
  90.                                 $protected = false;
  91.                                 $visible   = true;
  92.                                 break;
  93.                         case OIDplusConfig::PROTECTION_READONLY:
  94.                                 $protected = true;
  95.                                 $visible   = true;
  96.                                 break;
  97.                         case OIDplusConfig::PROTECTION_HIDDEN:
  98.                                 $protected = true;
  99.                                 $visible   = false;
  100.                                 break;
  101.                         default:
  102.                                 throw new OIDplusException(_L('Invalid protection flag, use OIDplusConfig::PROTECTION_* constants'));
  103.                 }
  104.  
  105.                 // Check length limitations given by the database tables
  106.                 if (strlen($name) > 50) {
  107.                         throw new OIDplusException(_L('Config key name "%1" is too long (max %2).',$name,50));
  108.                 }
  109.                 if (strlen($description) > 255) {
  110.                         throw new OIDplusException(_L('Description for config key "%1" is too long (max %2).',$name,255));
  111.                 }
  112.  
  113.                 // Read all values and descriptions from the database once.
  114.                 $this->buildConfigArray();
  115.  
  116.                 // Figure out if we need to create/update something at database level
  117.                 if (!isset($this->values[$name])) {
  118.                         // Case A: The config setting does not exist in the database. So we create it now.
  119.                         try {
  120.                                 OIDplus::db()->query("insert into ###config (name, description, value, protected, visible) values (?, ?, ?, ?, ?)", array($name, $description, $init_value, $protected, $visible));
  121.                         } catch (\Exception $e) {
  122.                                 // After a software update that introduced a new config setting,
  123.                                 // there will be a race-condition at this place, because
  124.                                 // jsTree and content are loading simultaneously!
  125.                                 // So we ignore the error here.
  126.                         }
  127.                         $this->values[$name] = $init_value;
  128.                         $this->descriptions[$name] = $description;
  129.                         $this->protectSettings[$name] = $protected;
  130.                         $this->visibleSettings[$name] = $visible;
  131.                 } else {
  132.                         // Case B: The config setting exists ...
  133.                         if ($this->descriptions[$name] != $description) {
  134.                                 // ... but the human readable description is different.
  135.                                 // We want to give the plugin authors the possibility to automatically update the config descriptions for their plugins
  136.                                 // So we just edit the description
  137.                                 OIDplus::db()->query("update ###config set description = ? where name = ?", array($description, $name));
  138.                                 $this->descriptions[$name] = $description;
  139.                         }
  140.                         if ($this->protectSettings[$name] != $protected) {
  141.                                 OIDplus::db()->query("update ###config set protected = ? where name = ?", array($protected, $name));
  142.                                 $this->protectSettings[$name] = $protected;
  143.                         }
  144.                         if ($this->visibleSettings[$name] != $visible) {
  145.                                 OIDplus::db()->query("update ###config set visible = ? where name = ?", array($visible, $name));
  146.                                 $this->visibleSettings[$name] = $visible;
  147.                         }
  148.                 }
  149.  
  150.                 // Register the validation callback
  151.                 if (!is_null($validateCallback)) {
  152.                         $this->validateCallbacks[$name] = $validateCallback;
  153.                 }
  154.         }
  155.  
  156.         /**
  157.          * @return void
  158.          * @throws OIDplusException
  159.          */
  160.         public function clearCache() {
  161.                 $this->configTableReadOnce = false;
  162.                 $this->buildConfigArray();
  163.         }
  164.  
  165.         /**
  166.          * @return void
  167.          * @throws OIDplusException
  168.          */
  169.         protected function buildConfigArray() {
  170.                 if ($this->configTableReadOnce) return;
  171.  
  172.                 $this->values = array();
  173.                 $this->descriptions = array();
  174.                 $this->protectSettings = array();
  175.                 $this->visibleSettings = array();
  176.                 $res = OIDplus::db()->query("select name, description, protected, visible, value from ###config");
  177.                 while ($row = $res->fetch_object()) {
  178.                         $this->values[$row->name] = $row->value;
  179.                         $this->descriptions[$row->name] = $row->description;
  180.                         $this->protectSettings[$row->name] = $row->protected;
  181.                         $this->visibleSettings[$row->name] = $row->visible;
  182.                 }
  183.  
  184.                 $this->configTableReadOnce = true;
  185.         }
  186.  
  187.         /**
  188.          * @return string[]
  189.          * @throws OIDplusException
  190.          */
  191.         public function getAllKeys(): array {
  192.                 // TODO: put this method into the interface OIDplusGetterSetterInterface
  193.  
  194.                 // Read all config settings once and write them in array $this->values
  195.                 $this->buildConfigArray();
  196.  
  197.                 // Now we can see if our desired attribute is available
  198.                 return array_keys($this->values);
  199.         }
  200.  
  201.         /**
  202.          * @param string $name
  203.          * @param mixed|null $default
  204.          * @return mixed|null
  205.          * @throws OIDplusException
  206.          */
  207.         public function getValue(string $name, $default=null) {
  208.                 // Read all config settings once and write them in array $this->values
  209.                 $this->buildConfigArray();
  210.  
  211.                 // Now we can see if our desired attribute is available
  212.                 return $this->values[$name] ?? $default;
  213.         }
  214.  
  215.         /**
  216.          * @param string $name
  217.          * @return bool
  218.          * @throws OIDplusException
  219.          */
  220.         public function exists(string $name): bool {
  221.                 return !is_null($this->getValue($name, null));
  222.         }
  223.  
  224.         /**
  225.          * @param string $name
  226.          * @param mixed $value
  227.          * @return void
  228.          * @throws OIDplusException
  229.          */
  230.         public function setValue(string $name, $value) {
  231.                 // Read all config settings once and write them in array $this->values
  232.                 $this->buildConfigArray();
  233.  
  234.                 if (isset($this->values[$name])) {
  235.                         // Avoid unnecessary database writes
  236.                         if ($this->values[$name] == $value) return;
  237.                 } else {
  238.                         throw new OIDplusException(_L('Config value "%1" cannot be written because it was not prepared!', $name));
  239.                 }
  240.  
  241.                 // Give plugins the possibility to stop the process by throwing an Exception (e.g. if the value is invalid)
  242.                 // Required is that the plugin previously prepared the config setting using prepareConfigKey()
  243.                 if (isset($this->validateCallbacks[$name])) {
  244.                         $this->validateCallbacks[$name]($value);
  245.                 }
  246.  
  247.                 // Now change the value in the database
  248.                 OIDplus::db()->query("update ###config set value = ? where name = ?", array("$value", "$name"));
  249.                 $this->values[$name] = $value;
  250.         }
  251.  
  252.         /**
  253.          * @param string $name
  254.          * @param string $value
  255.          * @return void
  256.          * @throws OIDplusException
  257.          */
  258.         public function setValueNoCallback(string $name, string $value) {
  259.                 // Read all config settings once and write them in array $this->values
  260.                 $this->buildConfigArray();
  261.  
  262.                 if (isset($this->values[$name])) {
  263.                         // Avoid unnecessary database writes
  264.                         if ($this->values[$name] == $value) return;
  265.                 } else {
  266.                         throw new OIDplusException(_L('Config value "%1" cannot be written because it was not prepared!', $name));
  267.                 }
  268.  
  269.                 // Now change the value in the database
  270.                 OIDplus::db()->query("update ###config set value = ? where name = ?", array($value, $name));
  271.                 $this->values[$name] = $value;
  272.         }
  273.  
  274.         /**
  275.          * @param string $name
  276.          * @return void
  277.          * @throws OIDplusException
  278.          */
  279.         public function delete(string $name) {
  280.                 if ($this->configTableReadOnce) {
  281.                         if (isset($this->values[$name])) {
  282.                                 OIDplus::db()->query("delete from ###config where name = ?", array($name));
  283.                         }
  284.                 } else {
  285.                         // We do not know if the value exists.
  286.                         // buildConfigArray() would do many reads which are unnecessary.
  287.                         // So we just do a MySQL command to delete the stuff:
  288.                         OIDplus::db()->query("delete from ###config where name = ?", array($name));
  289.                 }
  290.  
  291.                 unset($this->values[$name]);
  292.                 unset($this->descriptions[$name]);
  293.                 unset($this->validateCallbacks[$name]);
  294.                 unset($this->protectSettings[$name]);
  295.                 unset($this->visibleSettings[$name]);
  296.         }
  297.  
  298. }
  299.