<?php
/**
* @file classes/plugins/PluginSettingsDAO.inc.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PluginSettingsDAO
* @ingroup plugins
* @see Plugin
*
* @brief Operations for retrieving and modifying plugin settings.
*/
import('lib.pkp.classes.xml.PKPXMLParser');
class PluginSettingsDAO extends DAO {
/**
* Get the cache for plugin settings.
* @param $contextId int Context ID
* @param $pluginName string Plugin symbolic name
* @return Cache
*/
function _getCache($contextId, $pluginName) {
static $settingCache;
if (!isset($settingCache)) {
$settingCache = array();
}
if (!isset($settingCache[$contextId])) {
$settingCache[$contextId] = array();
}
if (!isset($settingCache[$contextId][$pluginName])) {
$cacheManager = CacheManager::getManager();
$settingCache[$contextId][$pluginName] = $cacheManager->getCache(
'pluginSettings-' . $contextId, $pluginName,
array($this, '_cacheMiss')
);
}
return $settingCache[$contextId][$pluginName];
}
/**
* Retrieve a plugin setting value.
* @param $contextId int Context ID
* @param $pluginName string Plugin symbolic name
* @param $name Setting name
* @return mixed
*/
function getSetting($contextId, $pluginName, $name) {
// Normalize the plug-in name to lower case.
$pluginName = strtolower_codesafe($pluginName);
// Retrieve the setting.
$cache = $this->_getCache($contextId, $pluginName);
return $cache->get($name);
}
/**
* Does the plugin setting exist.
* @param $contextId int Context ID
* @param $pluginName string Plugin symbolic name
* @param $name Setting name
* @return boolean
*/
function settingExists($contextId, $pluginName, $name) {
$pluginName = strtolower_codesafe($pluginName);
$result = $this->retrieve(
'SELECT COUNT(*) AS row_count FROM plugin_settings WHERE plugin_name = ? AND context_id = ? AND setting_name = ?',
[$pluginName, (int) $contextId, $name]
);
$row = $result->current();
return $row ? (boolean) $row->row_count : false;
}
/**
* Callback for a cache miss.
* @param $cache Cache object
* @param $id string Identifier to look up in cache
* @return mixed
*/
function _cacheMiss($cache, $id) {
$contextParts = explode('-', $cache->getContext());
$contextId = array_pop($contextParts);
$settings = $this->getPluginSettings($contextId, $cache->getCacheId());
if (!isset($settings[$id])) {
// Make sure that even null values are cached
$cache->setCache($id, null);
return null;
}
return $settings[$id];
}
/**
* Retrieve and cache all settings for a plugin.
* @param $contextId int Context ID
* @param $pluginName string Plugin symbolic name
* @return array
*/
function getPluginSettings($contextId, $pluginName) {
// Normalize plug-in name to lower case.
$pluginName = strtolower_codesafe($pluginName);
$result = $this->retrieve(
'SELECT setting_name, setting_value, setting_type FROM plugin_settings WHERE plugin_name = ? AND context_id = ?', array($pluginName, (int) $contextId)
);
$pluginSettings = array();
foreach ($result as $row) {
$pluginSettings[$row->setting_name] = $this->convertFromDB($row->setting_value, $row->setting_type);
}
$cache = $this->_getCache($contextId, $pluginName);
$cache->setEntireCache($pluginSettings);
return $pluginSettings;
}
/**
* Add/update a plugin setting.
* @param $contextId int Context ID
* @param $pluginName string Symbolic plugin name
* @param $name string Setting name
* @param $value mixed Setting value
* @param $type string data type of the setting. If omitted, type will be guessed
* @return int Return value from ADODB's replace() function.
*/
function updateSetting($contextId, $pluginName, $name, $value, $type = null) {
// Normalize the plug-in name to lower case.
$pluginName = strtolower_codesafe($pluginName);
$cache = $this->_getCache($contextId, $pluginName);
$cache->setCache($name, $value);
$value = $this->convertToDB($value, $type);
return $this->replace(
'plugin_settings',
array(
'context_id' => (int) $contextId,
'plugin_name' => $pluginName,
'setting_name' => $name,
'setting_value' => $value,
'setting_type' => $type,
),
array('context_id', 'plugin_name', 'setting_name')
);
}
/**
* Delete a plugin setting.
* @param $contextId int
* @param $pluginName int
* @param $name string
*/
function deleteSetting($contextId, $pluginName, $name) {
// Normalize the plug-in name to lower case.
$pluginName = strtolower_codesafe($pluginName);
$cache = $this->_getCache($contextId, $pluginName);
$cache->setCache($name, null);
return $this->update(
'DELETE FROM plugin_settings WHERE plugin_name = ? AND setting_name = ? AND context_id = ?',
array($pluginName, $name, (int) $contextId)
);
}
/**
* Delete all settings for a plugin.
* @param $contextId int
* @param $pluginName string
*/
function deleteSettingsByPlugin($contextId, $pluginName) {
// Normalize the plug-in name to lower case.
$pluginName = strtolower_codesafe($pluginName);
$cache = $this->_getCache($contextId, $pluginName);
$cache->flush();
return $this->update(
'DELETE FROM plugin_settings WHERE context_id = ? AND plugin_name = ?',
array((int) $contextId, $pluginName)
);
}
/**
* Delete all settings for a context.
* @param $contextId int
*/
function deleteByContextId($contextId) {
return $this->update(
'DELETE FROM plugin_settings WHERE context_id = ?', [(int) $contextId]
);
}
/**
* Used internally by installSettings to perform variable and translation replacements.
* @param $rawInput string contains text including variable and/or translate replacements.
* @param $paramArray array contains variables for replacement
* @return string
*/
function _performReplacement($rawInput, $paramArray = array()) {
$value = preg_replace_callback('{{translate key="([^"]+)"}}', '_installer_plugin_regexp_callback', $rawInput);
foreach ($paramArray as $pKey => $pValue) {
$value = str_replace('{$' . $pKey . '}', $pValue, $value);
}
return $value;
}
/**
* Used internally by installSettings to recursively build nested arrays.
* Deals with translation and variable replacement calls.
* @param $node object XMLNode <array> tag
* @param $paramArray array Parameters to be replaced in key/value contents
*/
function _buildObject ($node, $paramArray = array()) {
$value = array();
foreach ($node->getChildren() as $element) {
$key = $element->getAttribute('key');
$childArray = $element->getChildByName('array');
if (isset($childArray)) {
$content = $this->_buildObject($childArray, $paramArray);
} else {
$content = $this->_performReplacement($element->getValue(), $paramArray);
}
if (!empty($key)) {
$key = $this->_performReplacement($key, $paramArray);
$value[$key] = $content;
} else $value[] = $content;
}
return $value;
}
/**
* Install plugin settings from an XML file.
* @param $pluginName name of plugin for settings to apply to
* @param $filename string Name of XML file to parse and install
* @param $paramArray array Optional parameters for variable replacement in settings
*/
function installSettings($contextId, $pluginName, $filename, $paramArray = array()) {
$xmlParser = new PKPXMLParser();
$tree = $xmlParser->parse($filename);
if (!$tree) return false;
// Check for existing settings and leave them if they are already in place.
$currentSettings = $this->getPluginSettings($contextId, $pluginName);
foreach ($tree->getChildren() as $setting) {
$nameNode = $setting->getChildByName('name');
$valueNode = $setting->getChildByName('value');
if (isset($nameNode) && isset($valueNode)) {
$type = $setting->getAttribute('type');
$name = $nameNode->getValue();
// If the setting already exists, respect it.
if (isset($currentSettings[$name])) continue;
if ($type == 'object') {
$arrayNode = $valueNode->getChildByName('array');
$value = $this->_buildObject($arrayNode, $paramArray);
} else {
$value = $this->_performReplacement($valueNode->getValue(), $paramArray);
}
// Replace translate calls with translated content
$this->updateSetting($contextId, $pluginName, $name, $value, $type);
}
}
}
}
/**
* Used internally by plugin setting installation code to perform translation
* function.
* @param $matches array
* @return string
*/
function _installer_plugin_regexp_callback($matches) {
return __($matches[1]);
}
|