API Docs for: 0.8.0
Show:

File: include/system/settings.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';

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

module.exports = function SettingsModule(pb) {

    /**
     * SettingServiceFactory - Creates a service that will provide access to settings
     * @class SettingsServiceFactory
     * @constructor
     */
    function SettingServiceFactory(){}

    /**
     * Tracks the number of instances created
     * @private
     * @static
     * @property count
     * @type {Integer}
     */
    var count = 1;

    /**
     * The collection that contains settings
     * @private
     * @static
     * @readonly
     * @property TYPE
     * @type {String}
     */
    var TYPE = 'setting';

    /**
     * Creates a new instance of settings service with specified site, using the memory and cache settings of pb config
     *
     * @static
     * @method getServiceBySite
     * @param {String} site
     * @param {Boolean=} onlyThisSite
     */
    SettingServiceFactory.getServiceBySite = function (site, onlyThisSite) {
        if (pb.config.multisite.enabled) {
            return SettingServiceFactory.getService(pb.config.settings.use_memory, pb.config.settings.use_cache, site, onlyThisSite);
        }
        return SettingServiceFactory.getService(pb.config.settings.use_memory, pb.config.settings.use_cache);
    };

    /**
     * Creates a new instance of the settings service
     * @static
     * @method getService
     * @param {Boolean} useMemory
     * @param {Boolean} useCache
     * @return {SimpleLayeredService}
     * @param site {String} siteId
     * @param onlyThisSite {Boolean} whether this service should only return setting specified by site
     */
    SettingServiceFactory.getService = function(useMemory, useCache, site, onlyThisSite) {
        var keyField   = 'key';
        var valueField = 'value';
        var services = [];

        var options = {
            objType: TYPE,
            valueField: valueField,
            keyField: keyField,
            timeout: pb.config.settings.memory_timeout,
            site: site,
            onlyThisSite: onlyThisSite
        };

        //add in-memory service
        if (useMemory){
            services.push(new pb.MemoryEntityService(options));
        }

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

        //always add db service
        services.push(new pb.DBEntityService(options));

        return new pb.SimpleLayeredService(services, 'SettingService' + count++);
    };

    /**
     * @method getBaseObjectService
     * @param {Object} options
     * @param {String} options.site
     * @param {Boolean} options.onlyThisSite
     */
    SettingServiceFactory.getBaseObjectService = function(options) {
        return new SettingService(options);
    };

    /**
     * @class SettingService
     * @constructor
     * @param {Object} options
     * @param {String} options.site
     * @param {Boolean} options.onlyThisSite
     */
    function SettingService(options) {

        /**
         * @property cacheService
         * @type {SimpleLayeredService}
         */
        this.cacheService = SettingServiceFactory.getServiceBySite(options.site, options.onlyThisSite);

        options.type = TYPE;
        SettingService.super_.call(this, options);
    }
    util.inherits(SettingService, pb.BaseObjectService);

    /**
     * @protected
     * @method _get
     * @param {String} id
     * @param {Object} options
     * @param {Function} cb
     */
    SettingService.prototype._get = function(id, options, cb) {
        this.cacheService.get(id, function(err, result) {
            cb(err, util.isNullOrUndefined(result) ? null : { key: id, value: result });
        });
    };

    /**
     * Constructs the where condition that uniquely identifies the DTO from the
     * persistence store.  If the clause cannot be constructed the function
     * should return null
     * @method getIdWhere
     * @param {Object} dto
     * @return {Object}
     */
    SettingService.prototype.getIdWhere = function(dto) {
        return dto.key ? { key: dto.key } : null;
    };

    /**
     * Deletes an object by key
     * @method deleteById
     * @param {String} id
     * @param {Object} options
     * @param {Function} cb
     */
    SettingService.prototype.deleteById = function(id, options, cb) {
        if (util.isFunction(options)) {
            cb      = options;
            options = {};
        }
        options.where = { key: id };

        this.deleteSingle(options, cb);
    };

    /**
     * @static
     * @method afterSave
     * @param {Object} context
     * @param {Function} cb
     */
    SettingService.afterSave = function(context, cb) {
        context.service.cacheService.set(context.data.key, context.data.value, cb);
    };

    /**
     * Purges the data from the cache after it is deleted from the persistence
     * store
     * @static
     * @method afterDelete
     * @param {Object} context
     * @param {Function} cb Takes a single error, if exists
     */
    SettingService.afterDelete = function(context, cb) {
        context.service.cacheService.purge(context.data.key, cb);
    };

    /**
     * Formats the data before it is merged
     * @static
     * @method format
     * @param {Object} context
     * @param {Function} cb Takes a single error, if exists
     */
    SettingService.format = function(context, cb) {
        var dto = context.data;
        dto.key = pb.BaseController.sanitize(dto.key);
        if (util.isString(dto.value)) {
            dto.value = pb.BaseController.sanitize(dto.value);
        }
        cb(null);
    };

    /**
     *
     * @static
     * @method
     * @param {Object} context
     * @param {SettingService} service An instance of the service that triggered
     * the event that called this handler
     * @param {Function} cb A callback that takes a single parameter: an error if occurred
     */
    SettingService.merge = function(context, cb) {
        var obj = context.object;
        var dto = context.data;
        if (context.isCreate) {
            obj.key = dto.key;
        }
        obj.value = dto.value;
        cb(null);
    };

    /**
     *
     * @static
     * @method validate
     * @param {Object} context
     * @param {Object} context.data The DTO that was provided for persistence
     * @param {TopicService} context.service An instance of the service that triggered
     * the event that called this handler
     * @param {Function} cb A callback that takes a single parameter: an error if occurred
     */
    SettingService.validate = function(context, cb) {
        var obj = context.data;
        var errors = context.validationErrors;

        if (!pb.ValidationService.isNonEmptyStr(obj.key, true)) {
            errors.push(pb.BaseObjectService.validationFailure('key', 'Key is required'));

            //no need to check the DB.  Short circuit it here
            return cb(null, errors);
        }

        //validate key is not taken
        var where = { key: new RegExp('^' + util.escapeRegExp(obj.key) + '$', 'i') };
        context.service.dao.exists(TYPE, where, function(err, exists) {
            if (exists && context.isCreate) {
                errors.push(pb.BaseObjectService.validationFailure('key', 'key already exists'));
            }
            else if (!exists && context.isUpdate) {
                errors.push(pb.BaseObjectService.validationFailure('key', 'The setting should already exist'));
            }
            cb(err, errors);
        });
    };

    //registrations
    pb.BaseObjectService.on(TYPE + '.' + pb.BaseObjectService.AFTER_DELETE, SettingService.afterDelete);
    pb.BaseObjectService.on(TYPE + '.' + pb.BaseObjectService.AFTER_SAVE, SettingService.afterSave);
    pb.BaseObjectService.on(TYPE + '.' + pb.BaseObjectService.FORMAT, SettingService.format);
    pb.BaseObjectService.on(TYPE + '.' + pb.BaseObjectService.MERGE, SettingService.merge);
    pb.BaseObjectService.on(TYPE + '.' + pb.BaseObjectService.VALIDATE, SettingService.validate);

    //exports
    return SettingServiceFactory;
};