API Docs for: 0.8.0
Show:

File: include/service/entities/plugin_setting_service.js

/*
 Copyright (C) 2016  PencilBlue, LLC

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

var util = require('../../util.js');


module.exports = function PluginSettingServiceModule(pb) {

    /**
     * @private
     * @static
     * @readonly
     * @property GLOBAL_SITE
     * @type {String}
     */
    var GLOBAL_SITE = pb.SiteService.GLOBAL_SITE;

    /**
     * @private
     * @static
     * @readonly
     * @property SITE_FIELD
     * @type {String}
     */
    var SITE_FIELD = pb.SiteService.SITE_FIELD;

    /**
     * Constructor for service that retrieves plugin settings from the database.
     * @class PluginSettingService
     * @constructor
     * @param {string} siteUID - site unique id
     */
    function PluginSettingService(siteUID){
		//construct settings services

        /**
         *
         * @property caching
         * @type {Object}
         */
        this.caching = pb.config.plugins.caching;

        /**
         *
         * @property site
         * @type {String}
         */
        this.site = pb.config.multisite.enabled && siteUID ? siteUID : GLOBAL_SITE;

        /**
         *
         * @property pluginService
         * @type {PluginService}
         */
        this.pluginService = new pb.PluginService({site: this.site});

        /**
         * A setting service that sets and retrieves the settings for plugins
         * @property pluginSettingsService
         * @type {SimpleLayeredService}
         */
        this.pluginSettingsService = genSettingsService({
            objType: 'plugin_settings',
            useMemory: this.caching.use_memory,
            useCache: this.caching.use_cache,
            serviceName: 'PluginSettingService',
            site: this.site,
            onlyThisSite: false
        });

        /**
         * A setting service that sets and retrieves the settings for plugins
         * @property pluginSettingsService
         * @type {SimpleLayeredService}
         */
        this.themeSettingsService  = genSettingsService({
            objType: 'theme_settings',
            useMemory: this.caching.use_memory,
            useCache: this.caching.use_cache,
            serviceName: 'ThemeSettingService',
            site: this.site,
            onlyThisSite: false
        });
	}

	/**
     * Retrieves a single setting for the specified plugin.
     *
     * @method getSetting
     * @param {string} settingName The name of the setting to retrieve
     * @param {string} pluginName The name of the plugin who owns the setting
     * @param {function} cb A callback that provides two parameters: cb(error, settingValue).
     * Null is returned if the setting does not exist or the specified plugin is not
     * installed.
     */
    PluginSettingService.prototype.getSetting = function(settingName, pluginName, cb) {
        this.getSettings(pluginName, function(err, settings) {
            if (util.isError(err)) {
                return cb(err, null);
            }

            var val = null;
            if (util.isArray(settings)) {
                for (var i = 0; i < settings.length; i++) {
                    if (settingName === settings[i].name) {
                        val = settings[i].value;
                        break;
                    }
                }
            }
            cb(err, val);
        });
    };

    /**
     * Retrieves all of the settings for the specified plugin.
     *
     * @method getSettings
     * @param {string} pluginName The name of the plugin who's settings are being requested
     * @param {Function} cb A callback that provides two parameters: cb(error, settings).
     * Null is provided in the event that the plugin is not installed.
     */
    PluginSettingService.prototype.getSettings = function(pluginName, cb) {
        this.pluginSettingsService.get(pluginName, cb);
    };

    /**
     * Gets the plugin settings for one site only.
     * Will not default to global plugin settings for given plugin.
     * @method getSettingsBySite
     * @param {String} pluginName - name of plugin to retrieve settings for
     * @param {Function} cb - callback function
     */
    PluginSettingService.prototype.getSettingsBySite = function(pluginName, cb) {
    	var settings = getAdminPluginSettingsService(this);
    	settings.get(pluginName, cb);
    };

    /**
     * Retrieves the settings for a plugin as hash of key/value pairs.  This
     * differs from the getSettings function because the getSettings function
     * provides the settings in their raw form as an array of objects containing
     * multiple properties.  In most circumstances just the k/v pair is needed and
     * not any additional information about the property.  The function takes the
     * raw settings array and transforms it into an object where the setting name
     * is the property and the setting value is the value.
     * @method getSettingsKV
     * @param {String} pluginName The unique ID of the plugin who settings are to be retrieved
     * @param {Function} cb A callback that takes two parameters.  A error, if
     * exists, and a hash of of the plugin's settings' names/values.
     */
    PluginSettingService.prototype.getSettingsKV = function(pluginName, cb) {
        this.pluginSettingsService.get(pluginName, function(err, settings) {
            if (util.isError(err)) {
                return cb(err);
            }
            else if (!util.isArray(settings)) {
                return cb(null, null);
            }

            cb(null, util.arrayToObj(settings, 'name', 'value'));
        });
    };

    /**
     * Replaces a single setting for the specified plugin
     *
     * @method setSetting
     * @param name The name of the setting to change
     * @param value The new value for the setting
     * @param pluginName The plugin who's setting is being changed.
     * @param cb A callback that provides two parameters: cb(error, TRUE/FALSE).
     * TRUE if the setting was persisted successfully, FALSE if not.
     */
    PluginSettingService.prototype.setSetting = function(name, value, pluginName, cb) {
        var self = this;

        //error checking
        if (!pb.PluginService.validateSettingValue(value)) {
            return cb(new Error("PluginService: The setting value is required when modifing a theme setting"), false);
        }
        if (!pb.validation.isNonEmptyStr(name, true)) {
            return cb(new Error("PluginService: The setting name is required when modifing a theme setting"), false);
        }

        //retrieve the settings to modify
        this.getSettingsBySite(pluginName, function(err, settings) {
            if (util.isError(err) || !settings) {
                cb(err, false);
                return;
            }

            var wasFound = false;
            for (var i = 0; i < settings.length; i++) {
                if (name === settings[i].name) {
                    settings[i].value = value;
                    wasFound = true;
                    break;
                }
            }
            if (!wasFound) {
                settings.push({
                    name: name,
                    value: value
                });
            }
            self.setSettings(settings, pluginName, cb);
        });
    };

    /**
     * Replaces the settings for the specified plugin.
     *
     * @method setSettings
     * @param settings The settings object to be validated and persisted
     * @param pluginName The name of the plugin who's settings are being represented
     * @param cb A callback that provides two parameters: cb(error, TRUE/FALSE).
     * TRUE if the settings were persisted successfully, FALSE if not.
     */
    PluginSettingService.prototype.setSettings = function(settings, pluginName, cb) {
        var self = this;

        //error checking
        if (!settings) {
            return cb(new Error("PluginSettingService: The settings object is required when making changes to plugin settings"), false);
        }
        if (!pluginName) {
            return cb(new Error("PluginSettingService: The plugin name is required when making changes to plugin settings"), false);
        }

        this.pluginService.isInstalled(pluginName, function(err, isInstalled) {
            if (util.isError(err) || !isInstalled) {
                return cb(err, false);
            }

            self.pluginSettingsService.set(pluginName, settings, function(err, result) {
                cb(err, !util.isError(err) && result);
            });
        });
    };

    /**
     * Replaces a single theme setting for the specified plugin
     *
     * @method setThemeSetting
     * @param name The name of the setting to change
     * @param value The new value for the setting
     * @param pluginName The plugin who's setting is being changed.
     * @param cb A callback that provides two parameters: cb(error, TRUE/FALSE).
     * TRUE if the setting was persisted successfully, FALSE if not.
     */
    PluginSettingService.prototype.setThemeSetting = function(name, value, pluginName, cb) {
        var self = this;

        //error checking
        if (!pb.PluginService.validateSettingValue(value)) {
            return cb(new Error("PluginService: The setting value is required when modifing a theme setting"), false);
        }
        if (!pb.validation.isNonEmptyStr(name, true)) {
            return cb(new Error("PluginService: The setting name is required when modifing a theme setting"), false);
        }

        //retrieve the settings to modify
        this.getThemeSettingsBySite(pluginName, function(err, settings) {
            if (util.isError(err) || !settings) {
                return cb(err, false);
            }

            var wasFound = false;
            for (var i = 0; i < settings.length; i++) {
                if (name === settings[i].name) {
                    settings[i].value = value;
                    wasFound = true;
                    break;
                }
            }
            if (!wasFound) {
                settings.push({
                    name: name,
                    value: value
                });
            }
            self.setThemeSettings(settings, pluginName, cb);
        });
    };

    /**
     * Replaces the theme settings for the specified plugin.
     *
     * @method setThemeSettings
     * @param settings The settings object to be validated and persisted
     * @param pluginName The uid of the plugin who's settings are being represented
     * @param cb A callback that provides two parameters: cb(error, TRUE/FALSE).
     * TRUE if the settings were persisted successfully, FALSE if not.
     */
    PluginSettingService.prototype.setThemeSettings = function(settings, pluginName, cb) {
        var self = this;

        //error checking
        if (!settings) {
            return cb(new Error("PluginSettingService: The settings object is required when making changes to theme settings"), false);
        }
        if (!pluginName) {
            return cb(new Error("PluginSettingService: The plugin name is required when making changes to theme settings"), false);
        }

        this.pluginService.isInstalled(pluginName, function(err, isInstalled) {
            if (util.isError(err) || !isInstalled) {
                return cb(err, false);
            }

            self.themeSettingsService.set(pluginName, settings, function(err, result) {
                cb(err, !util.isError(err) && result);
            });
        });
    };

    /**
     * Retrieves a single theme setting value.
     *
     * @method getThemeSetting
     * @param settingName The uid of the setting
     * @param pluginName The plugin to retrieve the setting from
     * @param cb A callback that provides two parameters: cb(error, settingValue)
     */
    PluginSettingService.prototype.getThemeSetting = function(settingName, pluginName, cb) {
        this.getThemeSettings(pluginName, function(err, settings) {
            if (util.isError(err)) {
                return cb(err, null);
            }

            var val = null;
            if (util.isArray(settings)) {
                for (var i = 0; i < settings.length; i++) {
                    if (settingName === settings[i].name) {
                        val = settings[i].value;
                        break;
                    }
                }
            }
            cb(err, val);
        });
    };

    /**
     * Retrieves the theme settings for the specified plugin
     *
     * @method getThemeSettings
     * @param pluginName The uid of the plugin
     * @param cb A callback that provides two parameters: cb(err, settingsObject)
     */
    PluginSettingService.prototype.getThemeSettings = function(pluginName, cb) {
        this.themeSettingsService.get(pluginName, cb);
    };

    /**
     * Retrieves theme settings for specified plugin and for only the specified site.
     * Will not default to global theme settings.
     * @method getThemeSettingsBySite
     * @param {String} pluginName - the name of the plugin to get theme settings
     * @param {Function} cb - callback function
     */
    PluginSettingService.prototype.getThemeSettingsBySite = function(pluginName, cb) {
    	var settings = getAdminThemeSettingsService(this);
    	settings.get(pluginName, cb);
    };

    /**
     * Retrieves the theme settings for a plugin as hash of key/value pairs.  This
     * differs from the getThemeSettings function because the getThemeSettings function
     * provides the settings in their raw form as an array of objects containing
     * multiple properties.  In most circumstances just the k/v pair is needed and
     * not any additional information about the property.  The function takes the
     * raw settings array and transforms it into an object where the setting name
     * is the property and the setting value is the value.
     * @method getThemeSettingsKV
     * @param {String} pluginName The unique ID of the plugin who settings are to be retrieved
     * @param {Function} cb A callback that takes two parameters.  A error, if
     * exists, and a hash of of the plugin's settings' names/values.
     */
    PluginSettingService.prototype.getThemeSettingsKV = function(pluginName, cb) {
        this.themeSettingsService.get(pluginName, function(err, settings) {
            if (util.isError(err)) {
                return cb(err);
            }
            else if (!util.isArray(settings)) {
                return cb(null, null);
            }

            cb(null, util.arrayToObj(settings, 'name', 'value'));
        });
    };


    /**
     * Loads the settings from a details object and persists them in the DB.  Any
     * existing settings for the plugin are deleted before the new settings are
     * persisted.
     *
     * @method resetSettings
     * @param details The details object to extract the settings from
     * @param cb A callback that provides two parameters: cb(error, TRUE/FALSE).
     * TRUE if the settings were successfully cleared and reloaded. FALSE if not.
     */
    PluginSettingService.prototype.resetSettings = function(details, cb) {
        var self = this;

        //retrieve plugin to prove it exists (plus we need the id)
        var pluginName = details.uid;
        this.pluginService.getPluginBySite(pluginName, function(err, plugin) {
            if (util.isError(err) || !plugin) {
                return cb(err ? err : new Error("The plugin "+pluginName+" is not installed"), false);
            }

            //remove any existing settings
            self.pluginSettingsService.purge(pluginName, function (err, result) {
                if (util.isError(err) || !result) {
                    return cb(err, false);
                }

                //build the object to persist
                var baseDoc  = {
                    plugin_name: plugin.name,
                    plugin_uid: plugin.uid,
                    plugin_id: plugin[pb.DAO.getIdField()].toString(),
                    settings: details.settings
                };
                baseDoc[SITE_FIELD] = self.site;
                var settings = pb.DocumentCreator.create('plugin_settings', baseDoc);

                //save it
                var dao = new pb.DAO();
                dao.save(settings, function(err/*, result*/) {
                    cb(err, !util.isError(err));
                });
            });
        });
    };

    /**
     * Loads the Theme settings from a details object and persists them in the DB.  Any
     * existing theme settings for the plugin are deleted before the new settings
     * are persisted. If the plugin does not have a theme then false is provided in
     * the callback.
     *
     * @method resetThemeSettings
     * @param details The details object to extract the settings from
     * @param cb A callback that provides two parameters: cb(error, TRUE/FALSE).
     * TRUE if the settings were successfully cleared and reloaded. FALSE if not.
     */
    PluginSettingService.prototype.resetThemeSettings = function(details, cb) {
        var self = this;

        //error checking
        var pluginName = details.uid;
        if (!details.theme || !details.theme.settings) {
            return cb(new Error("PluginService: Settings are required when attempting to reset a plugin's theme settings"), false);
        }

        //retrieve plugin to prove it exists (plus we need the id)
        this.pluginService.getPluginBySite(pluginName, function(err, plugin) {
            if (util.isError(err) || !plugin) {
                return cb(err, false);
            }

            //remove any existing settings
            self.themeSettingsService.purge(pluginName, function (err, result) {
                if (util.isError(err) || !result) {
                    return cb(err, false);
                }

                //build the object to persist
                var baseDoc  = {
                    plugin_name: plugin.name,
                    plugin_uid: plugin.uid,
                    plugin_id: plugin[pb.DAO.getIdField()].toString(),
                    settings: details.theme.settings
                };
                baseDoc[SITE_FIELD] = self.site;
                var settings = pb.DocumentCreator.create('theme_settings', baseDoc);

                //save it
                var dao = new pb.DAO();
                dao.save(settings, function(err/*, result*/) {
                    cb(err, !util.isError(err));
                });
            });
        });
    };

    /**
     * @method purgePluginSettings
     * @param {String} pluginUid
     * @param {Function} cb
     */
    PluginSettingService.prototype.purgePluginSettings = function(pluginUid, cb) {
        this.pluginSettingsService.purge(pluginUid, cb);
    };

    /**
     * @method purgeThemeSettings
     * @param {String} pluginUid
     * @param {Function} cb
     */
    PluginSettingService.prototype.purgeThemeSettings = function(pluginUid, cb) {
        this.themeSettingsService.purge(pluginUid, cb);
    };


    /**
     * Convenience function to generate a service to handle settings for a plugin.
     *
     * @static
     * @method genSettingsService
     * @param {String} opts.objType The type of object that will be dealt with.  (plugin_settings,
     * theme_settings)
     * @param {Boolean} opts.useMemory Indicates if the generated layered service should
     * use an in memory service.
     * @param {Boolean} opts.useCache Indicates if the generated layered service should
     * use a cache service.
     * @param {string} opts.serviceName The name of the service
     * @param {String} opts.site
     * @param {Boolean} opts.onlyThisSite
     * @return {SimpleLayeredService}
     */
    function genSettingsService(opts) {

        //add in-memory service
        var services = [];

        var options = {
            objType: opts.objType,
            site: opts.site,
            onlyThisSite: opts.onlyThisSite
        };

        if (opts.useMemory) {
       	    options.timeout = pb.config.plugins.caching.memory_timeout;
            services.push(new pb.MemoryEntityService(options));
        }

        //add cache service
        if (opts.useCache) {
            services.push(new pb.CacheEntityService(options));
        }

        //always add DB
        options.keyField = 'plugin_uid';
        options.valueField = 'settings';
        services.push(new pb.DBEntityService(options));
        return new pb.SimpleLayeredService(services, opts.serviceName);
    }

    /**
     * @private
     * @static
     * @method getAdminPluginSettingsService
     * @param {PluginSettingService} self
     * @return {SimpleLayeredService}
     */
    function getAdminPluginSettingsService(self) {
    	if(!self.adminPluginSettingsService) {
            var opts = {
                objType: 'plugin_settings',
                useMemory: self.caching.use_memory,
                useCache: self.caching.use_cache,
                serviceName: 'PluginSettingService',
                site: self.site,
                onlyThisSite: true
            };
    		self.adminPluginSettingsService = genSettingsService(opts);
    	}
    	return self.adminPluginSettingsService;
    }

    /**
     * @private
     * @static
     * @method getAdminThemeSettingService
     * @param {PluginSettingService} self
     * @return {SimpleLayeredService}
     */
    function getAdminThemeSettingsService(self) {
    	if(!self.adminThemeSettingsService) {
            var opts = {
                objType: 'theme_settings',
                useMemory: self.caching.use_memory,
                useCache: self.caching.use_cache,
                serviceName: 'ThemeSettingService',
                site: self.site,
                onlyThisSite: true
            };
    		self.adminThemeSettingsService = genSettingsService(opts);
    	}
    	return self.adminThemeSettingsService;
    }

	return PluginSettingService;
};