<commit_before>const Promise = require('bluebird');
const _ = require('lodash');
const uuid = require('uuid');
const crypto = require('crypto');
const keypair = require('keypair');
const ObjectID = require('bson-objectid');
const ghostBookshelf = require('./base');
const tpl = require('@tryghost/tpl');
const errors = require('@tryghost/errors');
const validator = require('@tryghost/validator');
const urlUtils = require('../../shared/url-utils');
const {WRITABLE_KEYS_ALLOWLIST} = require('../../shared/labs');

const messages = {
    valueCannotBeBlank: 'Value in [settings.key] cannot be blank.',
    unableToFindSetting: 'Unable to find setting to update: {key}',
    notEnoughPermission: 'You do not have permission to perform this action'
};

const internalContext = {context: {internal: true}};
let Settings;
let defaultSettings;

const doBlock = fn => fn();

const getMembersKey = doBlock(() => {
    let UNO_KEYPAIRINO;
    return function getKey(type) {
        if (!UNO_KEYPAIRINO) {
            UNO_KEYPAIRINO = keypair({bits: 1024});
        }
        return UNO_KEYPAIRINO[type];
    };
});

const getGhostKey = doBlock(() => {
    let UNO_KEYPAIRINO;
    return function getKey(type) {
        if (!UNO_KEYPAIRINO) {
            UNO_KEYPAIRINO = keypair({bits: 1024});
        }
        return UNO_KEYPAIRINO[type];
    };
});

// For neatness, the defaults file is split into categories.
// It's much easier for us to work with it as a single level
// instead of iterating those categories every time
function parseDefaultSettings() {
    const defaultSettingsInCategories = require('../data/schema/').defaultSettings;
    const defaultSettingsFlattened = {};

    const dynamicDefault = {
        db_hash: () => uuid.v4(),
        public_hash: () => crypto.randomBytes(15).toString('hex'),
        // @TODO: session_secret would ideally be named "admin_session_secret"
        session_secret: () => crypto.randomBytes(32).toString('hex'),
        theme_session_secret: () => crypto.randomBytes(32).toString('hex'),
        members_public_key: () => getMembersKey('public'),
        members_private_key: () => getMembersKey('private'),
        members_email_auth_secret: () => crypto.randomBytes(64).toString('hex'),
        ghost_public_key: () => getGhostKey('public'),
        ghost_private_key: () => getGhostKey('private')
    };

    _.each(defaultSettingsInCategories, function each(settings, categoryName) {
        _.each(settings, function eachSetting(setting, settingName) {
            setting.group = categoryName;
            setting.key = settingName;

            setting.getDefaultValue = function getDefaultValue() {
                const getDynamicDefault = dynamicDefault[setting.key];
                if (getDynamicDefault) {
                    return getDynamicDefault();
                } else {
                    return setting.defaultValue;
                }
            };

            defaultSettingsFlattened[settingName] = setting;
        });
    });

    return defaultSettingsFlattened;
}

function getDefaultSettings() {
    if (!defaultSettings) {
        defaultSettings = parseDefaultSettings();
    }

    return defaultSettings;
}

// Each setting is saved as a separate row in the database,
// but the overlying API treats them as a single key:value mapping
Settings = ghostBookshelf.Model.extend({

    tableName: 'settings',

    emitChange: function emitChange(event, options) {
        const eventToTrigger = 'settings' + '.' + event;
        ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
    },

    onDestroyed: function onDestroyed(model, options) {
        ghostBookshelf.Model.prototype.onDestroyed.apply(this, arguments);

        model.emitChange('deleted', options);
        model.emitChange(model._previousAttributes.key + '.' + 'deleted', options);
    },

    onCreated: function onCreated(model, options) {
        ghostBookshelf.Model.prototype.onCreated.apply(this, arguments);

        model.emitChange('added', options);
        model.emitChange(model.attributes.key + '.' + 'added', options);
    },

    onUpdated: function onUpdated(model, options) {
        ghostBookshelf.Model.prototype.onUpdated.apply(this, arguments);

        model.emitChange('edited', options);
        model.emitChange(model.attributes.key + '.' + 'edited', options);
    },

    async onValidate(model, attr, options) {
        await ghostBookshelf.Model.prototype.onValidate.call(this, model, attr, options);

        await Settings.validators.all(model, options);

        if (typeof Settings.validators[model.get('key')] === 'function') {
            await Settings.validators[model.get('key')](model, options);
        }
    },

    format() {
        const attrs = ghostBookshelf.Model.prototype.format.apply(this, arguments);
        const settingType = attrs.type;

        if (settingType === 'boolean') {
            // CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
            if (attrs.value === '0' || attrs.value === '1') {
                attrs.value = !!+attrs.value;
            }

            // CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
            if (attrs.value === 'false' || attrs.value === 'true') {
                attrs.value = JSON.parse(attrs.value);
            }

            if (_.isBoolean(attrs.value)) {
                attrs.value = attrs.value.toString();
            }
        }

        return attrs;
    },

    formatOnWrite(attrs) {
        if (attrs.value && ['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image'].includes(attrs.key)) {
            attrs.value = urlUtils.toTransformReady(attrs.value);
        }

        return attrs;
    },

    parse() {
        const attrs = ghostBookshelf.Model.prototype.parse.apply(this, arguments);

        // transform "0" to false for boolean type
        const settingType = attrs.type;
        if (settingType === 'boolean' && (attrs.value === '0' || attrs.value === '1')) {
            attrs.value = !!+attrs.value;
        }

        // transform "false" to false for boolean type
        if (settingType === 'boolean' && (attrs.value === 'false' || attrs.value === 'true')) {
            attrs.value = JSON.parse(attrs.value);
        }

        // transform URLs from __GHOST_URL__ to absolute
        if (['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image'].includes(attrs.key)) {
            attrs.value = urlUtils.transformReadyToAbsolute(attrs.value);
        }

        return attrs;
    }
}, {
    findOne: function (data, options) {
        if (_.isEmpty(data)) {
            options = data;
        }

        // Allow for just passing the key instead of attributes
        if (!_.isObject(data)) {
            data = {key: data};
        }

        return Promise.resolve(ghostBookshelf.Model.findOne.call(this, data, options));
    },

    edit: function (data, unfilteredOptions) {
        const options = this.filterOptions(unfilteredOptions, 'edit');
        const self = this;

        if (!Array.isArray(data)) {
            data = [data];
        }

        return Promise.map(data, function (item) {
            // Accept an array of models as input
            if (item.toJSON) {
                item = item.toJSON();
            }
            if (!(_.isString(item.key) && item.key.length > 0)) {
                return Promise.reject(new errors.ValidationError({message: tpl(messages.valueCannotBeBlank)}));
            }

            item = self.filterData(item);

            return Settings.forge({key: item.key}).fetch(options).then(function then(setting) {
                if (setting) {
                    // it's allowed to edit all attributes in case of importing/migrating
                    if (options.importing) {
                        return setting.save(item, options);
                    } else {
                        // If we have a value, set it.
                        if (Object.prototype.hasOwnProperty.call(item, 'value')) {
                            setting.set('value', item.value);
                        }
                        // Internal context can overwrite type (for fixture migrations)
                        if (options.context && options.context.internal && Object.prototype.hasOwnProperty.call(item, 'type')) {
                            setting.set('type', item.type);
                        }

                        // If anything has changed, save the updated model
                        if (setting.hasChanged()) {
                            return setting.save(null, options);
                        }

                        return setting;
                    }
                }

                return Promise.reject(new errors.NotFoundError({message: tpl(messages.unableToFindSetting, {key: item.key})}));
            });
        });
    },

    populateDefaults: async function populateDefaults(unfilteredOptions) {
        const options = this.filterOptions(unfilteredOptions, 'populateDefaults');
        const self = this;

        if (!options.context) {
            options.context = internalContext.context;
        }

        // this is required for sqlite to pick up the columns after db init
        await ghostBookshelf.knex.destroy();
        await ghostBookshelf.knex.initialize();

        // fetch available columns to avoid populating columns not yet created by migrations
        const columnInfo = await ghostBookshelf.knex.table('settings').columnInfo();
        const columns = Object.keys(columnInfo);

        // fetch other data that is used when inserting new settings
        const date = ghostBookshelf.knex.raw('CURRENT_TIMESTAMP');
        let owner;
        try {
            owner = await ghostBookshelf.model('User').getOwnerUser();
        } catch (e) {
            // in some tests the owner is deleted and not recreated before setup
            if (e.errorType === 'NotFoundError') {
                owner = {id: 1};
            } else {
                throw e;
            }
        }

        return this
            .findAll(options)
            .then(function checkAllSettings(allSettings) {
                const usedKeys = allSettings.models.map(function mapper(setting) {
                    return setting.get('key');
                });

                const insertOperations = [];

                _.each(getDefaultSettings(), function forEachDefault(defaultSetting, defaultSettingKey) {
                    const isMissingFromDB = usedKeys.indexOf(defaultSettingKey) === -1;
                    if (isMissingFromDB) {
                        defaultSetting.value = defaultSetting.getDefaultValue();

                        const settingValues = Object.assign({}, defaultSetting, {
                            id: ObjectID().toHexString(),
                            created_at: date,
                            created_by: owner.id,
                            updated_at: date,
                            updated_by: owner.id
                        });

                        insertOperations.push(
                            ghostBookshelf.knex
                                .table('settings')
                                .insert(_.pick(settingValues, columns))
                        );
                    }
                });

                if (insertOperations.length > 0) {
                    return Promise.all(insertOperations).then(function fetchAllToReturn() {
                        return self.findAll(options);
                    });
                }

                return allSettings;
            });
    },

    permissible: function permissible(modelId, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission) {
        if (hasUserPermission && hasApiKeyPermission) {
            return Promise.resolve();
        }

        return Promise.reject(new errors.NoPermissionError({
            message: tpl(messages.notEnoughPermission)
        }));
    },

    validators: {
        async all(model) {
            const settingName = model.get('key');
            const settingDefault = getDefaultSettings()[settingName];

            if (!settingDefault) {
                return;
            }

            // Basic validations from default-settings.json
            const validationErrors = validator.validate(
                model.get('value'),
                model.get('key'),
                settingDefault.validations,
                'settings'
            );

            if (validationErrors.length) {
                throw new errors.ValidationError({message: validationErrors.join('\n')});
            }
        },
        async labs(model) {
            const flags = JSON.parse(model.get('value'));

            for (const flag in flags) {
                if (!WRITABLE_KEYS_ALLOWLIST.includes(flag)) {
                    throw new errors.ValidationError({
                        message: `Settings lab value cannot have value other then ${WRITABLE_KEYS_ALLOWLIST.join(', ')}`
                    });
                }
            }
        },
        async stripe_plans(model, options) {
            const plans = JSON.parse(model.get('value'));
            for (const plan of plans) {
                // Stripe plans used to be allowed (and defaulted to!) 0 amount plans
                // this causes issues to people importing from older versions of Ghost
                // even if they don't use Members/Stripe
                // issue: https://github.com/TryGhost/Ghost/issues/12049
                if (!options.importing) {
                    // We check 100, not 1, because amounts are in fractional units
                    if (plan.amount < 100 && plan.name !== 'Complimentary') {
                        throw new errors.ValidationError({
                            message: 'Plans cannot have an amount less than 1'
                        });
                    }
                }

                if (typeof plan.name !== 'string') {
                    throw new errors.ValidationError({
                        message: 'Plan must have a name'
                    });
                }

                if (typeof plan.currency !== 'string') {
                    throw new errors.ValidationError({
                        message: 'Plan must have a currency'
                    });
                }

                if (!['year', 'month', 'week', 'day'].includes(plan.interval)) {
                    throw new errors.ValidationError({
                        message: 'Plan interval must be one of: year, month, week or day'
                    });
                }
            }
        },
        // @TODO: Maybe move some of the logic into the members service, exporting an isValidStripeKey
        // method which can be called here, cleaning up the duplication, but not removing control
        async stripe_secret_key(model) {
            const value = model.get('value');
            if (value === null) {
                return;
            }

            const secretKeyRegex = /(?:sk|rk)_(?:test|live)_[\da-zA-Z]{1,247}$/;

            if (!secretKeyRegex.test(value)) {
                throw new errors.ValidationError({
                    message: `stripe_secret_key did not match ${secretKeyRegex}`
                });
            }
        },
        async stripe_publishable_key(model) {
            const value = model.get('value');
            if (value === null) {
                return;
            }

            const publishableKeyRegex = /pk_(?:test|live)_[\da-zA-Z]{1,247}$/;

            if (!publishableKeyRegex.test(value)) {
                throw new errors.ValidationError({
                    message: `stripe_publishable_key did not match ${publishableKeyRegex}`
                });
            }
        },
        async stripe_connect_secret_key(model) {
            const value = model.get('value');
            if (value === null) {
                return;
            }

            const secretKeyRegex = /(?:sk|rk)_(?:test|live)_[\da-zA-Z]{1,247}$/;

            if (!secretKeyRegex.test(value)) {
                throw new errors.ValidationError({
                    message: `stripe_secret_key did not match ${secretKeyRegex}`
                });
            }
        },
        async stripe_connect_publishable_key(model) {
            const value = model.get('value');
            if (value === null) {
                return;
            }

            const publishableKeyRegex = /pk_(?:test|live)_[\da-zA-Z]{1,247}$/;

            if (!publishableKeyRegex.test(value)) {
                throw new errors.ValidationError({
                    message: `stripe_publishable_key did not match ${publishableKeyRegex}`
                });
            }
        }
    }
});

module.exports = {
    Settings: ghostBookshelf.model('Settings', Settings)
};
<commit_msg>Refactored default settings population to reduce unnecessary DB queries<commit_after>const Promise = require('bluebird');
const _ = require('lodash');
const uuid = require('uuid');
const crypto = require('crypto');
const keypair = require('keypair');
const ObjectID = require('bson-objectid');
const ghostBookshelf = require('./base');
const tpl = require('@tryghost/tpl');
const errors = require('@tryghost/errors');
const validator = require('@tryghost/validator');
const urlUtils = require('../../shared/url-utils');
const {WRITABLE_KEYS_ALLOWLIST} = require('../../shared/labs');

const messages = {
    valueCannotBeBlank: 'Value in [settings.key] cannot be blank.',
    unableToFindSetting: 'Unable to find setting to update: {key}',
    notEnoughPermission: 'You do not have permission to perform this action'
};

const internalContext = {context: {internal: true}};
let Settings;
let defaultSettings;

const doBlock = fn => fn();

const getMembersKey = doBlock(() => {
    let UNO_KEYPAIRINO;
    return function getKey(type) {
        if (!UNO_KEYPAIRINO) {
            UNO_KEYPAIRINO = keypair({bits: 1024});
        }
        return UNO_KEYPAIRINO[type];
    };
});

const getGhostKey = doBlock(() => {
    let UNO_KEYPAIRINO;
    return function getKey(type) {
        if (!UNO_KEYPAIRINO) {
            UNO_KEYPAIRINO = keypair({bits: 1024});
        }
        return UNO_KEYPAIRINO[type];
    };
});

// For neatness, the defaults file is split into categories.
// It's much easier for us to work with it as a single level
// instead of iterating those categories every time
function parseDefaultSettings() {
    const defaultSettingsInCategories = require('../data/schema/').defaultSettings;
    const defaultSettingsFlattened = {};

    const dynamicDefault = {
        db_hash: () => uuid.v4(),
        public_hash: () => crypto.randomBytes(15).toString('hex'),
        // @TODO: session_secret would ideally be named "admin_session_secret"
        session_secret: () => crypto.randomBytes(32).toString('hex'),
        theme_session_secret: () => crypto.randomBytes(32).toString('hex'),
        members_public_key: () => getMembersKey('public'),
        members_private_key: () => getMembersKey('private'),
        members_email_auth_secret: () => crypto.randomBytes(64).toString('hex'),
        ghost_public_key: () => getGhostKey('public'),
        ghost_private_key: () => getGhostKey('private')
    };

    _.each(defaultSettingsInCategories, function each(settings, categoryName) {
        _.each(settings, function eachSetting(setting, settingName) {
            setting.group = categoryName;
            setting.key = settingName;

            setting.getDefaultValue = function getDefaultValue() {
                const getDynamicDefault = dynamicDefault[setting.key];
                if (getDynamicDefault) {
                    return getDynamicDefault();
                } else {
                    return setting.defaultValue;
                }
            };

            defaultSettingsFlattened[settingName] = setting;
        });
    });

    return defaultSettingsFlattened;
}

function getDefaultSettings() {
    if (!defaultSettings) {
        defaultSettings = parseDefaultSettings();
    }

    return defaultSettings;
}

// Each setting is saved as a separate row in the database,
// but the overlying API treats them as a single key:value mapping
Settings = ghostBookshelf.Model.extend({

    tableName: 'settings',

    emitChange: function emitChange(event, options) {
        const eventToTrigger = 'settings' + '.' + event;
        ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
    },

    onDestroyed: function onDestroyed(model, options) {
        ghostBookshelf.Model.prototype.onDestroyed.apply(this, arguments);

        model.emitChange('deleted', options);
        model.emitChange(model._previousAttributes.key + '.' + 'deleted', options);
    },

    onCreated: function onCreated(model, options) {
        ghostBookshelf.Model.prototype.onCreated.apply(this, arguments);

        model.emitChange('added', options);
        model.emitChange(model.attributes.key + '.' + 'added', options);
    },

    onUpdated: function onUpdated(model, options) {
        ghostBookshelf.Model.prototype.onUpdated.apply(this, arguments);

        model.emitChange('edited', options);
        model.emitChange(model.attributes.key + '.' + 'edited', options);
    },

    async onValidate(model, attr, options) {
        await ghostBookshelf.Model.prototype.onValidate.call(this, model, attr, options);

        await Settings.validators.all(model, options);

        if (typeof Settings.validators[model.get('key')] === 'function') {
            await Settings.validators[model.get('key')](model, options);
        }
    },

    format() {
        const attrs = ghostBookshelf.Model.prototype.format.apply(this, arguments);
        const settingType = attrs.type;

        if (settingType === 'boolean') {
            // CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
            if (attrs.value === '0' || attrs.value === '1') {
                attrs.value = !!+attrs.value;
            }

            // CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
            if (attrs.value === 'false' || attrs.value === 'true') {
                attrs.value = JSON.parse(attrs.value);
            }

            if (_.isBoolean(attrs.value)) {
                attrs.value = attrs.value.toString();
            }
        }

        return attrs;
    },

    formatOnWrite(attrs) {
        if (attrs.value && ['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image'].includes(attrs.key)) {
            attrs.value = urlUtils.toTransformReady(attrs.value);
        }

        return attrs;
    },

    parse() {
        const attrs = ghostBookshelf.Model.prototype.parse.apply(this, arguments);

        // transform "0" to false for boolean type
        const settingType = attrs.type;
        if (settingType === 'boolean' && (attrs.value === '0' || attrs.value === '1')) {
            attrs.value = !!+attrs.value;
        }

        // transform "false" to false for boolean type
        if (settingType === 'boolean' && (attrs.value === 'false' || attrs.value === 'true')) {
            attrs.value = JSON.parse(attrs.value);
        }

        // transform URLs from __GHOST_URL__ to absolute
        if (['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image'].includes(attrs.key)) {
            attrs.value = urlUtils.transformReadyToAbsolute(attrs.value);
        }

        return attrs;
    }
}, {
    findOne: function (data, options) {
        if (_.isEmpty(data)) {
            options = data;
        }

        // Allow for just passing the key instead of attributes
        if (!_.isObject(data)) {
            data = {key: data};
        }

        return Promise.resolve(ghostBookshelf.Model.findOne.call(this, data, options));
    },

    edit: function (data, unfilteredOptions) {
        const options = this.filterOptions(unfilteredOptions, 'edit');
        const self = this;

        if (!Array.isArray(data)) {
            data = [data];
        }

        return Promise.map(data, function (item) {
            // Accept an array of models as input
            if (item.toJSON) {
                item = item.toJSON();
            }
            if (!(_.isString(item.key) && item.key.length > 0)) {
                return Promise.reject(new errors.ValidationError({message: tpl(messages.valueCannotBeBlank)}));
            }

            item = self.filterData(item);

            return Settings.forge({key: item.key}).fetch(options).then(function then(setting) {
                if (setting) {
                    // it's allowed to edit all attributes in case of importing/migrating
                    if (options.importing) {
                        return setting.save(item, options);
                    } else {
                        // If we have a value, set it.
                        if (Object.prototype.hasOwnProperty.call(item, 'value')) {
                            setting.set('value', item.value);
                        }
                        // Internal context can overwrite type (for fixture migrations)
                        if (options.context && options.context.internal && Object.prototype.hasOwnProperty.call(item, 'type')) {
                            setting.set('type', item.type);
                        }

                        // If anything has changed, save the updated model
                        if (setting.hasChanged()) {
                            return setting.save(null, options);
                        }

                        return setting;
                    }
                }

                return Promise.reject(new errors.NotFoundError({message: tpl(messages.unableToFindSetting, {key: item.key})}));
            });
        });
    },

    populateDefaults: async function populateDefaults(unfilteredOptions) {
        const options = this.filterOptions(unfilteredOptions, 'populateDefaults');
        const self = this;

        if (!options.context) {
            options.context = internalContext.context;
        }

        // this is required for sqlite to pick up the columns after db init
        await ghostBookshelf.knex.destroy();
        await ghostBookshelf.knex.initialize();

        const allSettings = await this.findAll(options);

        const usedKeys = allSettings.models.map(function mapper(setting) {
            return setting.get('key');
        });

        const settingsToInsert = [];

        _.each(getDefaultSettings(), function forEachDefault(defaultSetting, defaultSettingKey) {
            const isMissingFromDB = usedKeys.indexOf(defaultSettingKey) === -1;
            if (isMissingFromDB) {
                defaultSetting.value = defaultSetting.getDefaultValue();
                settingsToInsert.push(defaultSetting);
            }
        });

        if (settingsToInsert.length > 0) {
            // fetch available columns to avoid populating columns not yet created by migrations
            const columnInfo = await ghostBookshelf.knex.table('settings').columnInfo();
            const columns = Object.keys(columnInfo);

            // fetch other data that is used when inserting new settings
            const date = ghostBookshelf.knex.raw('CURRENT_TIMESTAMP');
            let owner;
            try {
                owner = await ghostBookshelf.model('User').getOwnerUser();
            } catch (e) {
                // in some tests the owner is deleted and not recreated before setup
                if (e.errorType === 'NotFoundError') {
                    owner = {id: 1};
                } else {
                    throw e;
                }
            }

            const settingsDataToInsert = settingsToInsert.map((setting) => {
                const settingValues = Object.assign({}, setting, {
                    id: ObjectID().toHexString(),
                    created_at: date,
                    created_by: owner.id,
                    updated_at: date,
                    updated_by: owner.id
                });

                return _.pick(settingValues, columns);
            });

            await ghostBookshelf.knex
                .batchInsert('settings', settingsDataToInsert);

            return self.findAll(options);
        }

        return allSettings;
    },

    permissible: function permissible(modelId, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission) {
        if (hasUserPermission && hasApiKeyPermission) {
            return Promise.resolve();
        }

        return Promise.reject(new errors.NoPermissionError({
            message: tpl(messages.notEnoughPermission)
        }));
    },

    validators: {
        async all(model) {
            const settingName = model.get('key');
            const settingDefault = getDefaultSettings()[settingName];

            if (!settingDefault) {
                return;
            }

            // Basic validations from default-settings.json
            const validationErrors = validator.validate(
                model.get('value'),
                model.get('key'),
                settingDefault.validations,
                'settings'
            );

            if (validationErrors.length) {
                throw new errors.ValidationError({message: validationErrors.join('\n')});
            }
        },
        async labs(model) {
            const flags = JSON.parse(model.get('value'));

            for (const flag in flags) {
                if (!WRITABLE_KEYS_ALLOWLIST.includes(flag)) {
                    throw new errors.ValidationError({
                        message: `Settings lab value cannot have value other then ${WRITABLE_KEYS_ALLOWLIST.join(', ')}`
                    });
                }
            }
        },
        async stripe_plans(model, options) {
            const plans = JSON.parse(model.get('value'));
            for (const plan of plans) {
                // Stripe plans used to be allowed (and defaulted to!) 0 amount plans
                // this causes issues to people importing from older versions of Ghost
                // even if they don't use Members/Stripe
                // issue: https://github.com/TryGhost/Ghost/issues/12049
                if (!options.importing) {
                    // We check 100, not 1, because amounts are in fractional units
                    if (plan.amount < 100 && plan.name !== 'Complimentary') {
                        throw new errors.ValidationError({
                            message: 'Plans cannot have an amount less than 1'
                        });
                    }
                }

                if (typeof plan.name !== 'string') {
                    throw new errors.ValidationError({
                        message: 'Plan must have a name'
                    });
                }

                if (typeof plan.currency !== 'string') {
                    throw new errors.ValidationError({
                        message: 'Plan must have a currency'
                    });
                }

                if (!['year', 'month', 'week', 'day'].includes(plan.interval)) {
                    throw new errors.ValidationError({
                        message: 'Plan interval must be one of: year, month, week or day'
                    });
                }
            }
        },
        // @TODO: Maybe move some of the logic into the members service, exporting an isValidStripeKey
        // method which can be called here, cleaning up the duplication, but not removing control
        async stripe_secret_key(model) {
            const value = model.get('value');
            if (value === null) {
                return;
            }

            const secretKeyRegex = /(?:sk|rk)_(?:test|live)_[\da-zA-Z]{1,247}$/;

            if (!secretKeyRegex.test(value)) {
                throw new errors.ValidationError({
                    message: `stripe_secret_key did not match ${secretKeyRegex}`
                });
            }
        },
        async stripe_publishable_key(model) {
            const value = model.get('value');
            if (value === null) {
                return;
            }

            const publishableKeyRegex = /pk_(?:test|live)_[\da-zA-Z]{1,247}$/;

            if (!publishableKeyRegex.test(value)) {
                throw new errors.ValidationError({
                    message: `stripe_publishable_key did not match ${publishableKeyRegex}`
                });
            }
        },
        async stripe_connect_secret_key(model) {
            const value = model.get('value');
            if (value === null) {
                return;
            }

            const secretKeyRegex = /(?:sk|rk)_(?:test|live)_[\da-zA-Z]{1,247}$/;

            if (!secretKeyRegex.test(value)) {
                throw new errors.ValidationError({
                    message: `stripe_secret_key did not match ${secretKeyRegex}`
                });
            }
        },
        async stripe_connect_publishable_key(model) {
            const value = model.get('value');
            if (value === null) {
                return;
            }

            const publishableKeyRegex = /pk_(?:test|live)_[\da-zA-Z]{1,247}$/;

            if (!publishableKeyRegex.test(value)) {
                throw new errors.ValidationError({
                    message: `stripe_publishable_key did not match ${publishableKeyRegex}`
                });
            }
        }
    }
});

module.exports = {
    Settings: ghostBookshelf.model('Settings', Settings)
};
<|endoftext|>


<commit_before>/**
 * Mapsel.js - Map Coordinates Selector
 * Widget for selecting latitude and longitude from a map.
 *
 * Norsk Polarinstutt 2014, http://npolar.no/
 */

Mapsel.prototype = {
    on: function(event, callback) {
        if(typeof callback == 'function') {
            this.events[event] = callback;
        } else if(this.events[event]) {
            delete this.events[event];
        }
    },

    hide: function() {
        if(this.elements.root) {
            this.elements.root.style.display = 'none';
        }

        this.visible = false;
    },

    focus: function(element) {
        if(this.elements) {
            switch(element) {
            case 'latitude':
                if(this.elements.latInput) {
                    this.elements.latInput.focus();
                }
                break;

            case 'longitude':
                if(this.elements.lngInput) {
                    this.elements.lngInput.focus();
                }
                break;

            case 'radius':
                if(this.elements.radInput) {
                    this.elements.radInput.focus();
                }
                break;
            }
        }
    },

    init: function(api) {
        var self = this;

        for(var a in Mapsel.api) {
            if(api.toLowerCase() == a.toLowerCase()) {
                this.api = new Mapsel.api[a](self);
                return true;
            }
        }

        console.error('Unsupported map API: ' + api);
        return false;
    },

    move: function(x, y, relative) {
        if(this.elements.root) {
            this.elements.root.style.left = (relative ? this.x += x : this.x = Math.max(x, 0)) + 'px';
            this.elements.root.style.top = (relative ? this.y += y : this.y = Math.max(y, 0)) + 'px';
        }
    },

    resize: function(width, height, relative) {
        this.width = Math.max((relative ? this.width += width : width), 200);
        this.height = Math.max((relative ? this.height += height : height), 200);

        // Obtain the pixel width of the longest printable label
        var labelWidth = 0, widthTestElem = document.createElement('span'), testStrings = [];
        widthTestElem.style.fontSize = this.font.size;
        widthTestElem.style.visibility = 'hidden';

        if(this.northeast !== null && this.southwest !== null) {
            testStrings.push('NORTHEAST', 'SOUTHWEST');
        } else if(this.latitude !== null && this.longitude !== null) {
            testStrings.push('LATITUDE', 'LONGITUDE');

            if(this.radius !== null) {
                testStrings.push('RADIUS');
            }
        }

        document.body.appendChild(widthTestElem);
        for(var i in testStrings) {
            widthTestElem.innerHTML = Mapsel.i18n(this.language, testStrings[i]);
            labelWidth = Math.max(labelWidth, widthTestElem.offsetWidth);
        }
        document.body.removeChild(widthTestElem);

        var labelWidthPercent = Math.ceil(((labelWidth + 15) / this.width) * 100),
            labelWidthString = labelWidthPercent + '%',
            inputWidthString = (100 - labelWidthPercent) + '%';

        // Calculate the map-container height
        var mapHeight = this.height, baseStyle = window.getComputedStyle(this.elements.root, null);
        mapHeight -= (parseInt(baseStyle.borderTopWidth) + parseInt(baseStyle.borderBottomWidth));
        mapHeight -= (parseInt(baseStyle.paddingTop) + parseInt(baseStyle.paddingBottom));
        mapHeight -= this.elements.titleBar.offsetHeight;
        mapHeight -= this.elements.fieldset.offsetHeight;

        var labelElements = [ 'latLabel', 'lngLabel', 'radLabel', 'neLabel', 'swLabel' ];
        for(var le in labelElements) {
            if(this.elements[labelElements[le]]) {
                this.elements[labelElements[le]].style.width = labelWidthString;
            }
        }

        var inputElements = [ 'latInput', 'lngInput', 'radInput', 'neGroup', 'swGroup' ];
        for(var ie in inputElements) {
            if(this.elements[inputElements[ie]]) {
                this.elements[inputElements[ie]].style.width = inputWidthString;
            }
        }

        this.elements.root.style.width = this.width + 'px';
        this.elements.root.style.height = this.height + 'px';
        this.elements.mapContainer.style.height = mapHeight + 'px';
    },

    setNortheast: function(ne) {
        if(ne instanceof Array) {
            this.northeast = { latitude: ne[0], longitude: ne[1] };
        } else if(ne.hasOwnProperty('latitude') && ne.hasOwnProperty('longitude')) {
            this.northeast = ne;
        }

        this.elements.neLatInput = this.northeast.latitude;
        this.elements.neLngInput = this.northeast.longitude;
    },

    setSouthwest: function(sw) {
        if(ne instanceof Array) {
            this.southwest = { latitude: sw[0], longitude: sw[1] };
        } else if(sw.hasOwnProperty('latitude') && sw.hasOwnProperty('longitude')) {
            this.southwest = sw;
        }

        this.elements.swLatInput = this.southwest.latitude;
        this.elements.swLngInput = this.southwest.longitude;
    },

    show: function() {
        if(this.elements.root) {
            this.elements.root.style.display = 'block';

            if(typeof this.api == 'object') {
                this.api.center(this.latitude, this.longitude);
            } else {
                this.init(this.api);
            }
        }

        this.visible = true;
    }
};
<commit_msg>Fixed a problem  with setNorteast and setSouthwest methods<commit_after>/**
 * Mapsel.js - Map Coordinates Selector
 * Widget for selecting latitude and longitude from a map.
 *
 * Norsk Polarinstutt 2014, http://npolar.no/
 */

Mapsel.prototype = {
    on: function(event, callback) {
        if(typeof callback == 'function') {
            this.events[event] = callback;
        } else if(this.events[event]) {
            delete this.events[event];
        }
    },

    hide: function() {
        if(this.elements.root) {
            this.elements.root.style.display = 'none';
        }

        this.visible = false;
    },

    focus: function(element) {
        if(this.elements) {
            switch(element) {
            case 'latitude':
                if(this.elements.latInput) {
                    this.elements.latInput.focus();
                }
                break;

            case 'longitude':
                if(this.elements.lngInput) {
                    this.elements.lngInput.focus();
                }
                break;

            case 'radius':
                if(this.elements.radInput) {
                    this.elements.radInput.focus();
                }
                break;
            }
        }
    },

    init: function(api) {
        var self = this;

        for(var a in Mapsel.api) {
            if(api.toLowerCase() == a.toLowerCase()) {
                this.api = new Mapsel.api[a](self);
                return true;
            }
        }

        console.error('Unsupported map API: ' + api);
        return false;
    },

    move: function(x, y, relative) {
        if(this.elements.root) {
            this.elements.root.style.left = (relative ? this.x += x : this.x = Math.max(x, 0)) + 'px';
            this.elements.root.style.top = (relative ? this.y += y : this.y = Math.max(y, 0)) + 'px';
        }
    },

    resize: function(width, height, relative) {
        this.width = Math.max((relative ? this.width += width : width), 200);
        this.height = Math.max((relative ? this.height += height : height), 200);

        // Obtain the pixel width of the longest printable label
        var labelWidth = 0, widthTestElem = document.createElement('span'), testStrings = [];
        widthTestElem.style.fontSize = this.font.size;
        widthTestElem.style.visibility = 'hidden';

        if(this.northeast !== null && this.southwest !== null) {
            testStrings.push('NORTHEAST', 'SOUTHWEST');
        } else if(this.latitude !== null && this.longitude !== null) {
            testStrings.push('LATITUDE', 'LONGITUDE');

            if(this.radius !== null) {
                testStrings.push('RADIUS');
            }
        }

        document.body.appendChild(widthTestElem);
        for(var i in testStrings) {
            widthTestElem.innerHTML = Mapsel.i18n(this.language, testStrings[i]);
            labelWidth = Math.max(labelWidth, widthTestElem.offsetWidth);
        }
        document.body.removeChild(widthTestElem);

        var labelWidthPercent = Math.ceil(((labelWidth + 15) / this.width) * 100),
            labelWidthString = labelWidthPercent + '%',
            inputWidthString = (100 - labelWidthPercent) + '%';

        // Calculate the map-container height
        var mapHeight = this.height, baseStyle = window.getComputedStyle(this.elements.root, null);
        mapHeight -= (parseInt(baseStyle.borderTopWidth) + parseInt(baseStyle.borderBottomWidth));
        mapHeight -= (parseInt(baseStyle.paddingTop) + parseInt(baseStyle.paddingBottom));
        mapHeight -= this.elements.titleBar.offsetHeight;
        mapHeight -= this.elements.fieldset.offsetHeight;

        var labelElements = [ 'latLabel', 'lngLabel', 'radLabel', 'neLabel', 'swLabel' ];
        for(var le in labelElements) {
            if(this.elements[labelElements[le]]) {
                this.elements[labelElements[le]].style.width = labelWidthString;
            }
        }

        var inputElements = [ 'latInput', 'lngInput', 'radInput', 'neGroup', 'swGroup' ];
        for(var ie in inputElements) {
            if(this.elements[inputElements[ie]]) {
                this.elements[inputElements[ie]].style.width = inputWidthString;
            }
        }

        this.elements.root.style.width = this.width + 'px';
        this.elements.root.style.height = this.height + 'px';
        this.elements.mapContainer.style.height = mapHeight + 'px';
    },

    setNortheast: function(ne) {
        if(ne instanceof Array) {
            this.northeast = { latitude: ne[0], longitude: ne[1] };
        } else if(ne.hasOwnProperty('latitude') && ne.hasOwnProperty('longitude')) {
            this.northeast = ne;
        }

        this.elements.neLatInput.value = this.northeast.latitude;
        this.elements.neLngInput.value = this.northeast.longitude;
    },

    setSouthwest: function(sw) {
        if(ne instanceof Array) {
            this.southwest = { latitude: sw[0], longitude: sw[1] };
        } else if(sw.hasOwnProperty('latitude') && sw.hasOwnProperty('longitude')) {
            this.southwest = sw;
        }

        this.elements.swLatInput.value = this.southwest.latitude;
        this.elements.swLngInput.value = this.southwest.longitude;
    },

    show: function() {
        if(this.elements.root) {
            this.elements.root.style.display = 'block';

            if(typeof this.api == 'object') {
                this.api.center(this.latitude, this.longitude);
            } else {
                this.init(this.api);
            }
        }

        this.visible = true;
    }
};
<|endoftext|>


<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.db = {
	get_list: function (doctype, args) {
		if (!args) {
			args = {};
		}
		args.doctype = doctype;
		if (!args.fields) {
			args.fields = ["name"];
		}
		if (!("limit" in args)) {
			args.limit = 20;
		}
		return new Promise((resolve) => {
			frappe.call({
				method: "frappe.desk.reportview.get_list",
				args: args,
				type: "GET",
				callback: function (r) {
					resolve(r.message);
				},
			});
		});
	},
	exists: function (doctype, name) {
		return new Promise((resolve) => {
			frappe.db.get_value(doctype, { name: name }, "name").then((r) => {
				r.message && r.message.name ? resolve(true) : resolve(false);
			});
		});
	},
	get_value: function (doctype, filters, fieldname, callback, parent_doc) {
		return frappe.call({
			method: "frappe.client.get_value",
			type: "GET",
			args: {
				doctype: doctype,
				fieldname: fieldname,
				filters: filters,
				parent: parent_doc,
			},
			callback: function (r) {
				callback && callback(r.message);
			},
		});
	},
	get_single_value: (doctype, field) => {
		return new Promise((resolve) => {
			frappe
				.call({
					method: "frappe.client.get_single_value",
					args: { doctype, field },
					type: "GET",
				})
				.then((r) => resolve(r ? r.message : null));
		});
	},
	set_value: function (doctype, docname, fieldname, value, callback) {
		return frappe.call({
			method: "frappe.client.set_value",
			args: {
				doctype: doctype,
				name: docname,
				fieldname: fieldname,
				value: value,
			},
			callback: function (r) {
				callback && callback(r.message);
			},
		});
	},
	get_doc: function (doctype, name, filters = null) {
		return new Promise((resolve, reject) => {
			frappe
				.call({
					method: "frappe.client.get",
					type: "GET",
					args: { doctype, name, filters },
					callback: (r) => {
						frappe.model.sync(r.message);
						resolve(r.message);
					},
				})
				.fail(reject);
		});
	},
	insert: function (doc) {
		return frappe.xcall("frappe.client.insert", { doc });
	},
	delete_doc: function (doctype, name) {
		return new Promise((resolve) => {
			frappe.call("frappe.client.delete", { doctype, name }, (r) => resolve(r.message));
		});
	},
	count: function (doctype, args = {}) {
		let filters = args.filters || {};

		// has a filter with childtable?
		const distinct =
			Array.isArray(filters) &&
			filters.some((filter) => {
				return filter[0] !== doctype;
			});

		const fields = [];

		return frappe.xcall("frappe.desk.reportview.get_count", {
			doctype,
			filters,
			fields,
			distinct,
		});
	},
	get_link_options(doctype, txt = "", filters = {}) {
		return new Promise((resolve) => {
			frappe.call({
				type: "GET",
				method: "frappe.desk.search.search_link",
				args: {
					doctype,
					txt,
					filters,
				},
				callback(r) {
					resolve(r.results);
				},
			});
		});
	},
};
<commit_msg>fix: default `filters` to `undefined` instead of `null`<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.db = {
	get_list: function (doctype, args) {
		if (!args) {
			args = {};
		}
		args.doctype = doctype;
		if (!args.fields) {
			args.fields = ["name"];
		}
		if (!("limit" in args)) {
			args.limit = 20;
		}
		return new Promise((resolve) => {
			frappe.call({
				method: "frappe.desk.reportview.get_list",
				args: args,
				type: "GET",
				callback: function (r) {
					resolve(r.message);
				},
			});
		});
	},
	exists: function (doctype, name) {
		return new Promise((resolve) => {
			frappe.db.get_value(doctype, { name: name }, "name").then((r) => {
				r.message && r.message.name ? resolve(true) : resolve(false);
			});
		});
	},
	get_value: function (doctype, filters, fieldname, callback, parent_doc) {
		return frappe.call({
			method: "frappe.client.get_value",
			type: "GET",
			args: {
				doctype: doctype,
				fieldname: fieldname,
				filters: filters,
				parent: parent_doc,
			},
			callback: function (r) {
				callback && callback(r.message);
			},
		});
	},
	get_single_value: (doctype, field) => {
		return new Promise((resolve) => {
			frappe
				.call({
					method: "frappe.client.get_single_value",
					args: { doctype, field },
					type: "GET",
				})
				.then((r) => resolve(r ? r.message : null));
		});
	},
	set_value: function (doctype, docname, fieldname, value, callback) {
		return frappe.call({
			method: "frappe.client.set_value",
			args: {
				doctype: doctype,
				name: docname,
				fieldname: fieldname,
				value: value,
			},
			callback: function (r) {
				callback && callback(r.message);
			},
		});
	},
	get_doc: function (doctype, name, filters) {
		return new Promise((resolve, reject) => {
			frappe
				.call({
					method: "frappe.client.get",
					type: "GET",
					args: { doctype, name, filters },
					callback: (r) => {
						frappe.model.sync(r.message);
						resolve(r.message);
					},
				})
				.fail(reject);
		});
	},
	insert: function (doc) {
		return frappe.xcall("frappe.client.insert", { doc });
	},
	delete_doc: function (doctype, name) {
		return new Promise((resolve) => {
			frappe.call("frappe.client.delete", { doctype, name }, (r) => resolve(r.message));
		});
	},
	count: function (doctype, args = {}) {
		let filters = args.filters || {};

		// has a filter with childtable?
		const distinct =
			Array.isArray(filters) &&
			filters.some((filter) => {
				return filter[0] !== doctype;
			});

		const fields = [];

		return frappe.xcall("frappe.desk.reportview.get_count", {
			doctype,
			filters,
			fields,
			distinct,
		});
	},
	get_link_options(doctype, txt = "", filters = {}) {
		return new Promise((resolve) => {
			frappe.call({
				type: "GET",
				method: "frappe.desk.search.search_link",
				args: {
					doctype,
					txt,
					filters,
				},
				callback(r) {
					resolve(r.results);
				},
			});
		});
	},
};
<|endoftext|>


<commit_before>// @require Auger.Event
window.Auger || (window.Auger = {});
Auger.Dispatcher = {_actions: []};
Auger.Dispatcher._onhashchange = function(e){ 	// gets detached (avoid unnecessary closure)
	if(!e)e=window.event;
	var a=Auger.Dispatcher._actions;
	var h=Auger.Dispatcher.getHash();
	for(var i=0;i<a.length;++i){
		var v = null, t = a[i][1];
		switch(a[i][0]){
			case 0: v=true; break;
			case 1: v=(h.indexOf(t)==0?h.split(t.charAt(t.length-1)):null); break;
			case 2: v=h.match(t); break;
			case 3: v=t(h); break;
		}
		if(v) a[i][2].call(e, h, v); 	// call as a method on the event (for consistency)
	}
};
// prefix string, regex, or boolean function (allows multiple "apps" on the same page)
Auger.Dispatcher._dispatch_types = [null, 'string', RegExp, Function];
Auger.Dispatcher.dispatch = function(c,f){
	var t = this._dispatch_types, d = null;
	if(!f)
		d = [0,null,c];
	else if(typeof(c) == 'string')
		d = [1,c,f];
	else
		for(var i=2; i<t.length; ++i)
			if(c instanceof t[i])
				d = [i,c,f];
	if( d )
		this._actions.push(d);
	else
		throw("Unknown argument: " + c);
	this._lastHash = this.getHash(); 	// requires the page to load the initial hash
	if( this._actions.length == 1) 	// only add it the first time since we roll through all the possibilities
		Auger.Event.add(window, 'hashchange', this._onhashchange);
};
Auger.Dispatcher.getHash = function(){
	//var h = location.hash; return h ? h.replace(/^#+/,'') : location.href.replace(/^[^#]*#/,'');
	return location.hash.replace(/^#+/,'');
};
Auger.Dispatcher.setHash = function(h){
	if( h.charAt(0) != '#' ) h = '#' + h;
	var f;
	if((f = this._iframe)) f.src = f.src.replace(/#.*$/, h);
	location.hash = h;
};
<commit_msg>Define listen method to allow for easily overriding (allowing use of custom event library)<commit_after>// @require Auger.Event
window.Auger || (window.Auger = {});
Auger.Dispatcher = {_actions: []};
Auger.Dispatcher._onhashchange = function(e){ 	// gets detached (avoid unnecessary closure)
	if(!e)e=window.event;
	var a=Auger.Dispatcher._actions;
	var h=Auger.Dispatcher.getHash();
	for(var i=0;i<a.length;++i){
		var v = null, t = a[i][1];
		switch(a[i][0]){
			case 0: v=true; break;
			case 1: v=(h.indexOf(t)==0?h.split(t.charAt(t.length-1)):null); break;
			case 2: v=h.match(t); break;
			case 3: v=t(h); break;
		}
		if(v) a[i][2].call(e, h, v); 	// call as a method on the event (for consistency)
	}
};
// prefix string, regex, or boolean function (allows multiple "apps" on the same page)
Auger.Dispatcher._dispatch_types = [null, 'string', RegExp, Function];
Auger.Dispatcher.dispatch = function(c,f){
	var t = this._dispatch_types, d = null;
	if(!f)
		d = [0,null,c];
	else if(typeof(c) == 'string')
		d = [1,c,f];
	else
		for(var i=2; i<t.length; ++i)
			if(c instanceof t[i])
				d = [i,c,f];
	if( d )
		this._actions.push(d);
	else
		throw("Unknown argument: " + c);
	this._lastHash = this.getHash(); 	// requires the page to load the initial hash
	if( this._actions.length == 1) 	// only add it the first time since we roll through all the possibilities
		this.listen(this._onhashchange);
};
Auger.Dispatcher.getHash = function(){
	//var h = location.hash; return h ? h.replace(/^#+/,'') : location.href.replace(/^[^#]*#/,'');
	return location.hash.replace(/^#+/,'');
};
Auger.Dispatcher.setHash = function(h){
	if( h.charAt(0) != '#' ) h = '#' + h;
	var f;
	if((f = this._iframe)) f.src = f.src.replace(/#.*$/, h);
	location.hash = h;
};
Auger.Dispatcher.listen = function(f){ 			// override with desired event library
	var e = 'hashchange';
	var oe = 'on'+e;
	if(Auger.Event) Auger.Event.add(window, e, f);
	else if(oe in window) window[oe] = f;
	else throw('Event library required if window does not support '+oe);
}
<|endoftext|>


<commit_before>/*jshint unused:false */

var proxy        = null,
    httpProxy    = require('http-proxy'),
    middlewares  = [],
    HttpProxyRules = require('basebuild-proxy-rules');

module.exports = function(options) {

  'use strict';
  var moduleOptions = options.modulesData['proxy'];
  var _             = options.plugins.lodash;
  var chalk         = options.plugins.chalk;

  /*
   * Location of your backend server
   */
  var proxyTarget   = moduleOptions.target;
  var proxyRules    = new HttpProxyRules(moduleOptions.proxyRules || {});

  /**
   * Used to test the context of request
   * @type {Regex, Function}
   */
  var preventWhen = moduleOptions.preventWhen;
  var nextTest    = null;

  /**
   * Contains all middlewares
   * @type {Array}
   */
  middlewares  = moduleOptions.middleware ? moduleOptions.middleware : [];

  var proxyRequest = _.isFunction(moduleOptions.onProxyRequest) ? moduleOptions.onProxyRequest : onProxyRequest;
  var proxyError   = _.isFunction(moduleOptions.onProxyError)   ? moduleOptions.onProxyError   : onProxyError;

  /**
   * Executed only if the module is enabled
   */
  if(moduleOptions.isEnabled){

    proxy = httpProxy.createProxyServer({
      target: proxyTarget
    });

    /**
     * Logs every request
     * @param  {Object} proxyReq Proxy request
     * @param  {Object} req      Request
     * @param  {Object} res      Response
     */
    proxy.on('proxyReq', proxyRequest);

    /**
     * Logs errors
     * @param  {Object} proxyReq Proxy request
     * @param  {Object} req      Request
     * @param  {Object} res      Response
     */
    proxy.on('error', proxyError);

    /**
     * Sets the middleware
     * @type {Array}
     */
    middlewares = [proxyMiddleware];

    /**
     * Checks the type of modulesData.proxy.next
     */
    nextTest = getValidateFunction(preventWhen, 'preventWhen');

  }

  function getValidateFunction(option, propertyLaber){
    var validateFunction = null;

    if(_.isRegExp(option)){
      validateFunction = validateRegexRequest;
    } else if(_.isFunction(option)){
      validateFunction = validateRequestFunction;
    } else {
      throw new Error(chalk.red('[Proxy] ') + 'Iligal type for property "' + propertyLaber + '"');
    }

    return validateFunction;

  }

  function validateRequestFunction(func, req, res){
    return func(req, res)
  }

  function validateRegexRequest(regex, req, res, next){
    return regex.test(req.url)
  }

  function onProxyRequest (proxyReq, req, res, options) {
    proxyReq.setHeader('Access-Control-Allow-Origin', proxyTarget);
    console.log(chalk.green('[Proxy]'), 'Request made to:', chalk.magenta('host: '), options.target.href, chalk.magenta(' url: '), req.url);
    // console.log(options);
  }

  function onProxyError(error, req, res) {
    res.writeHead(500, {
      'Content-Type': 'text/plain'
    });

    console.error(chalk.red('[Proxy]'), error);
  }

  /*
   * The proxy middleware is an Express middleware added to BrowserSync to
   * handle backend request and proxy them to your backend.
   */
  function proxyMiddleware(req, res, next) {
    /*
     * This test is the switch of each request to determine if the request is
     * for a static file to be handled by BrowserSync or a backend request to proxy.
     *
     * The existing test is a standard check on the files extensions but it may fail
     * for your needs. If you can, you could also check on a context in the url which
     * may be more reliable but can't be generic.
     */
    if (nextTest(preventWhen, req, res) || req.url === "/")  {
      next();
    } else {
      var target = proxyRules.match(req) || proxyTarget || null;

      if (target) {
        return proxy.web(req, res, {
          target: target
        });
      } else {
        next(); // Static file
      }
    }
  }

  return {
    middlewares             : middlewares,
    getValidateFunction     : getValidateFunction,
    validateRequestFunction : validateRequestFunction,
    validateRegexRequest    : validateRegexRequest,
    onProxyRequest          : proxyRequest,
    onProxyError            : proxyError,
    defaults: {
      middleware: proxyMiddleware
    }
  };
};
<commit_msg>new syntax for proxy rules<commit_after>/*jshint unused:false */

var proxy        = null,
    httpProxy    = require('http-proxy'),
    middlewares  = [],
    HttpProxyRules = require('basebuild-proxy-rules');

module.exports = function(options) {

  'use strict';
  var moduleOptions = options.modulesData['proxy'];
  var _             = options.plugins.lodash;
  var chalk         = options.plugins.chalk;

  /*
   * Location of your backend server
   */
  var proxyTarget   = moduleOptions.target;
  var proxyRules    = new HttpProxyRules(moduleOptions.proxyRules || {});

  /**
   * Used to test the context of request
   * @type {Regex, Function}
   */
  var preventWhen = moduleOptions.preventWhen;
  var nextTest    = null;

  /**
   * Contains all middlewares
   * @type {Array}
   */
  middlewares  = moduleOptions.middleware ? moduleOptions.middleware : [];

  var proxyRequest = _.isFunction(moduleOptions.onProxyRequest) ? moduleOptions.onProxyRequest : onProxyRequest;
  var proxyError   = _.isFunction(moduleOptions.onProxyError)   ? moduleOptions.onProxyError   : onProxyError;

  /**
   * Executed only if the module is enabled
   */
  if(moduleOptions.isEnabled){

    proxy = httpProxy.createProxyServer({
      target: proxyTarget
    });

    /**
     * Logs every request
     * @param  {Object} proxyReq Proxy request
     * @param  {Object} req      Request
     * @param  {Object} res      Response
     */
    proxy.on('proxyReq', proxyRequest);

    /**
     * Logs errors
     * @param  {Object} proxyReq Proxy request
     * @param  {Object} req      Request
     * @param  {Object} res      Response
     */
    proxy.on('error', proxyError);

    /**
     * Sets the middleware
     * @type {Array}
     */
    middlewares = [proxyMiddleware];

    /**
     * Checks the type of modulesData.proxy.next
     */
    nextTest = getValidateFunction(preventWhen, 'preventWhen');

  }

  function getValidateFunction(option, propertyLaber){
    var validateFunction = null;

    if(_.isRegExp(option)){
      validateFunction = validateRegexRequest;
    } else if(_.isFunction(option)){
      validateFunction = validateRequestFunction;
    } else {
      throw new Error(chalk.red('[Proxy] ') + 'Iligal type for property "' + propertyLaber + '"');
    }

    return validateFunction;

  }

  function validateRequestFunction(func, req, res){
    return func(req, res)
  }

  function validateRegexRequest(regex, req, res, next){
    return regex.test(req.url)
  }

  function onProxyRequest (proxyReq, req, res, options) {
    proxyReq.setHeader('Access-Control-Allow-Origin', options.target.href);
    console.log(chalk.green('[Proxy]'), 'Request made to...', chalk.magenta('host '), options.target.href, chalk.magenta(' url '), req.url);
  }

  function onProxyError(error, req, res) {
    res.writeHead(500, {
      'Content-Type': 'text/plain'
    });

    console.error(chalk.red('[Proxy]'), error);
  }

  /*
   * The proxy middleware is an Express middleware added to BrowserSync to
   * handle backend request and proxy them to your backend.
   */
  function proxyMiddleware(req, res, next) {
    /*
     * This test is the switch of each request to determine if the request is
     * for a static file to be handled by BrowserSync or a backend request to proxy.
     *
     * The existing test is a standard check on the files extensions but it may fail
     * for your needs. If you can, you could also check on a context in the url which
     * may be more reliable but can't be generic.
     */
    if (nextTest(preventWhen, req, res) || req.url === "/")  {
      next();
    } else {
      var proxySettings = proxyRules.match(req) || { target: proxyTarget };
      if (proxySettings && proxySettings.target) {
        proxy.web(req, res, proxySettings);
      } else {
        next(); // Static file
      }
    }
  }

  return {
    middlewares             : middlewares,
    getValidateFunction     : getValidateFunction,
    validateRequestFunction : validateRequestFunction,
    validateRegexRequest    : validateRegexRequest,
    onProxyRequest          : proxyRequest,
    onProxyError            : proxyError,
    defaults: {
      middleware: proxyMiddleware
    }
  };
};
<|endoftext|>


<commit_before>'use strict';

var api = require('../core/api'),
    _ = require('../tools/tools'),
    checkoutNormalizer = require('./checkout-normalizer'),
    http = require('http-browser'),
    cssHack = require('../tools/css-hack'),
    isApp = typeof navigator !== 'undefined' && navigator.app,
    log = require('../tools/log');

function getBaseCheckout(options) {
  var baseCheckout = ( options.host === 'location' ? ( location.protocol + '//' + location.host + '/' ) : options.host ) || ( api.checkoutUrl || api.staticUrl ) + 'checkout/';

  // Append trailing slash if not exists.
  if (!/\/$/.test(baseCheckout)) {
    baseCheckout += '/';
  }

  return baseCheckout;
}

function checkout (options) {

  options = options || {};
  var baseCheckout = getBaseCheckout(options);

  var on = {},
      onError,
      merchant,
      iframeSrc = baseCheckout + 'iframe.html?' + new Date().getTime(),
      errorLoading = false,
      errorMessage = false,
      tmpOverlay = document.createElement('div'),
      cssOverlay = cssHack('overlay'),
      cssBlur = cssHack('blur'),
      cssLogo = cssHack('logo'),
      cssModal = cssHack('modal'),
      viewPortHack = document.createElement('meta');

  viewPortHack.name = 'viewport';
  viewPortHack.content = 'width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';

  document.head.appendChild(viewPortHack);

  onError = options.onError || _.noop;
  delete options.onError;

  try {
    options = checkoutNormalizer(options, location, _.copy(api) );
    merchant = options ? options.merchant : null;
    on.success = merchant.onSuccess;
    on.pending = merchant.onPending;
    on.cancel = merchant.onError;
    on.ko = merchant.onKO;
    on.dismiss = merchant.onDismiss;
  } catch (e) {
    errorMessage = e.message;
  }

  if (merchant) {
    // All functions must be removed as them can't be serialized by postMessage
    delete merchant.onSuccess;
    delete merchant.onPending;
    delete merchant.onError;
    delete merchant.onKO;
    delete merchant.onDismiss;
  }

  if( isApp ) {
    options.meta = options.meta || {};
    options.meta.is_app = true;
  }

  tmpOverlay.className = 'aplazame-overlay aplazame-overlay-show';

  cssOverlay.hack(true);
  cssLogo.hack(true);
  cssBlur.hack(true);

  setTimeout(function () {
    if( !errorLoading ) {
      _.addClass(document.body, 'aplazame-blur');
    }
  }, 0);

  tmpOverlay.innerHTML = '

' +
  require('./loading-svg') + '

Cargando pasarela de pago...

';

  document.body.appendChild(tmpOverlay);

  var loadingText = tmpOverlay.querySelector('.aplazame-overlay-loading-text');

  setTimeout(function () {
    if( !errorLoading ) {
      tmpOverlay.querySelector('.logo-aplazame').className += ' animate';
    }
  }, 200);

  return http( iframeSrc ).then(function (response) {
      var iframe = _.getIFrame({
            top: 0,
            left: 0,
            width: '100%',
            height: '0',
            background: 'transparent'
          }),
          httpCheckout = function () {
            var started = _.now();
            return http.apply(this, arguments).then(function (response) {
              iframe.contentWindow.postMessage({
                aplazame: 'checkout',
                event: 'http-success',
                started: started,
                elapsed: _.now() - started,
                response: http.plainResponse(response)
              }, '*');
              return response;
            }, function (response) {
              iframe.contentWindow.postMessage({
                aplazame: 'checkout',
                event: 'http-error',
                started: started,
                elapsed: _.now() - started,
                response: http.plainResponse(response)
              }, '*');
              throw response;
            });
          };

      iframe.id = 'aplazame-checkout-iframe';
      iframe.className = 'aplazame-modal';

      document.body.appendChild(iframe);
      iframe.src = iframeSrc;

      if (errorMessage) {
        throw new Error(errorMessage);
      }

      var onMessage = function (e, message) {

        switch( message.event ) {
          case 'merchant':
            iframe.style.display = _.remove_style;
            e.source.postMessage({
              checkout: options
            }, '*');
            break;
          case 'show-iframe':
            _.removeClass(iframe, 'hide');
            cssModal.hack(true);
            cssOverlay.hack(false);
            document.body.removeChild(tmpOverlay);
            break;
          case 'loading-text':
            loadingText.textContent = message.text;
            break;
          case 'open-link':
            if( isApp )
              navigator.app.loadUrl(message.href, { openExternal: true });
            else
              window.open(message.href, '_system');
            break;
          case 'drop-blur':
            _.removeClass(document.body, 'aplazame-blur');
            _.addClass(document.body, 'aplazame-unblur');
            setTimeout(function () {
              cssBlur.hack(false);
              _.removeClass(document.body, 'aplazame-blur');
              _.removeClass(document.body, 'aplazame-unblur');
            }, 600);
            break;
          case 'confirm':
          case 'success':
            _.log('aplazame.checkout:confirm', message);

            httpCheckout( options.merchant.confirmation_url, {
              method: 'post',
              contentType: 'application/json',
              data: message.data,
              params: _.extend(message.params || {}, {
                order_id: message.data.checkout_token,
                checkout_token: message.data.checkout_token
              })
            }).then(function (response) {
              e.source.postMessage({
                aplazame: 'checkout',
                event: 'confirmation',
                result: 'success',
                response: http.plainResponse(response)
              }, '*');
            }, function () {
              e.source.postMessage({
                aplazame: 'checkout',
                event: 'confirmation',
                result: 'error',
                response: http.plainResponse(response)
              }, '*');
            });
            // confirmation_url
            break;
          case 'close':
            if( iframe ) {
              document.body.removeChild(iframe);
              cssModal.hack(false);
              iframe = null;
              document.head.removeChild(viewPortHack);

              _.onMessage.off('checkout', onMessage);

              if( on[message.result] ) on[message.result]();
            }
            break;
        }
      };

      _.onMessage('checkout', onMessage);

    }).catch(function (reason) {
      // throw new Error('can not connect to ' + baseCheckout);
      errorLoading = true;

      log('Aplazame ' + reason);

      _.removeClass(tmpOverlay.querySelector('.logo-aplazame'), 'animate');
      loadingText.innerHTML = '
Error cargando pasarela de pago

soporte@aplazame.com
';
      loadingText.style.lineHeight = '1.5';

      onError(reason);
    });

}

module.exports = checkout;
<commit_msg>wip postMessage rewiew<commit_after>'use strict';

var api = require('../core/api'),
    _ = require('../tools/tools'),
    checkoutNormalizer = require('./checkout-normalizer'),
    http = require('http-browser'),
    cssHack = require('../tools/css-hack'),
    isApp = typeof navigator !== 'undefined' && navigator.app,
    log = require('../tools/log');

function getBaseCheckout(options) {
  var baseCheckout = ( options.host === 'location' ? ( location.protocol + '//' + location.host + '/' ) : options.host ) || ( api.checkoutUrl || api.staticUrl ) + 'checkout/';

  // Append trailing slash if not exists.
  if (!/\/$/.test(baseCheckout)) {
    baseCheckout += '/';
  }

  return baseCheckout;
}

function checkout (options) {

  options = options || {};
  var baseCheckout = getBaseCheckout(options);

  var on = {},
      onError,
      merchant,
      iframeSrc = baseCheckout + 'iframe.html?' + new Date().getTime(),
      errorLoading = false,
      errorMessage = false,
      tmpOverlay = document.createElement('div'),
      cssOverlay = cssHack('overlay'),
      cssBlur = cssHack('blur'),
      cssLogo = cssHack('logo'),
      cssModal = cssHack('modal'),
      viewPortHack = document.createElement('meta');

  viewPortHack.name = 'viewport';
  viewPortHack.content = 'width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';

  document.head.appendChild(viewPortHack);

  onError = options.onError || _.noop;
  delete options.onError;

  try {
    options = checkoutNormalizer(options, location, _.copy(api) );
    merchant = options ? options.merchant : null;
    on.success = merchant.onSuccess;
    on.pending = merchant.onPending;
    on.cancel = merchant.onError;
    on.ko = merchant.onKO;
    on.dismiss = merchant.onDismiss;
  } catch (e) {
    errorMessage = e.message;
  }

  if (merchant) {
    // All functions must be removed as them can't be serialized by postMessage
    delete merchant.onSuccess;
    delete merchant.onPending;
    delete merchant.onError;
    delete merchant.onKO;
    delete merchant.onDismiss;
  }

  if( isApp ) {
    options.meta = options.meta || {};
    options.meta.is_app = true;
  }

  tmpOverlay.className = 'aplazame-overlay aplazame-overlay-show';

  cssOverlay.hack(true);
  cssLogo.hack(true);
  cssBlur.hack(true);

  setTimeout(function () {
    if( !errorLoading ) {
      _.addClass(document.body, 'aplazame-blur');
    }
  }, 0);

  tmpOverlay.innerHTML = '

' +
  require('./loading-svg') + '

Cargando pasarela de pago...

';

  document.body.appendChild(tmpOverlay);

  var loadingText = tmpOverlay.querySelector('.aplazame-overlay-loading-text');

  setTimeout(function () {
    if( !errorLoading ) {
      tmpOverlay.querySelector('.logo-aplazame').className += ' animate';
    }
  }, 200);

  return http( iframeSrc ).then(function (response) {
      var iframe = _.getIFrame({
            top: 0,
            left: 0,
            width: '100%',
            height: '0',
            background: 'transparent'
          }),
          postMessage = function (event_name, message, target) {
            message.aplazame = 'checkout';
            message.event_name = event_name;
            (target || iframe.contentWindow).postMessage(message, '*');
          },
          httpCheckout = function () {
            var started = _.now();
            return http.apply(this, arguments).then(function (response) {
              postMessage('http-success', {
                started: started,
                elapsed: _.now() - started,
                response: http.plainResponse(response)
              });
              return response;
            }, function (response) {
              postMessage('http-error', {
                started: started,
                elapsed: _.now() - started,
                response: http.plainResponse(response)
              });
              throw response;
            });
          };

      iframe.id = 'aplazame-checkout-iframe';
      iframe.className = 'aplazame-modal';

      document.body.appendChild(iframe);
      iframe.src = iframeSrc;

      if (errorMessage) {
        throw new Error(errorMessage);
      }

      var onMessage = function (e, message) {
        console.log('onMessage', message);

        switch( message.event ) {
          case 'merchant':
            iframe.style.display = _.remove_style;
            postMessage('merchant-data', {
              checkout: options
            }, e.source);
            break;
          case 'show-iframe':
            _.removeClass(iframe, 'hide');
            cssModal.hack(true);
            cssOverlay.hack(false);
            document.body.removeChild(tmpOverlay);
            break;
          case 'loading-text':
            loadingText.textContent = message.text;
            break;
          case 'open-link':
            if( isApp )
              navigator.app.loadUrl(message.href, { openExternal: true });
            else
              window.open(message.href, '_system');
            break;
          case 'drop-blur':
            _.removeClass(document.body, 'aplazame-blur');
            _.addClass(document.body, 'aplazame-unblur');
            setTimeout(function () {
              cssBlur.hack(false);
              _.removeClass(document.body, 'aplazame-blur');
              _.removeClass(document.body, 'aplazame-unblur');
            }, 600);
            break;
          case 'confirm':
            _.log('aplazame.checkout:confirm', message);

            httpCheckout( options.merchant.confirmation_url, {
              method: 'post',
              contentType: 'application/json',
              data: message.data,
              params: _.extend(message.params || {}, {
                order_id: message.data.checkout_token,
                checkout_token: message.data.checkout_token
              })
            }).then(function (response) {
              postMessage('confirmation', {
                result: 'success',
                response: http.plainResponse(response)
              }, e.source);
            }, function () {
              postMessage('confirmation', {
                result: 'error',
                response: http.plainResponse(response)
              }, e.source);
            });
            // confirmation_url
            break;
          case 'close':
            if( iframe ) {
              document.body.removeChild(iframe);
              cssModal.hack(false);
              iframe = null;
              document.head.removeChild(viewPortHack);

              _.onMessage.off('checkout', onMessage);

              if( on[message.result] ) on[message.result]();
            }
            break;
        }
      };

      _.onMessage('checkout', onMessage);

    }).catch(function (reason) {
      // throw new Error('can not connect to ' + baseCheckout);
      errorLoading = true;

      log('Aplazame ' + reason);

      _.removeClass(tmpOverlay.querySelector('.logo-aplazame'), 'animate');
      loadingText.innerHTML = '
Error cargando pasarela de pago

soporte@aplazame.com
';
      loadingText.style.lineHeight = '1.5';

      onError(reason);
    });

}

module.exports = checkout;
<|endoftext|>


<commit_before>$(document).ready(function () {

  // Set the main path on the server
  let URL="http://localhost/EntracesExitEmployees/index.php/"

  $("#js_LoginBtn").click(function () {

    // Get Employee Number
    var EmpNumber=$("#js_EmployeeNmbr").val();

    // Get Employe Password
    var Password=$("#js_Password").val();

    // Start ajax
    $.ajax({
      type:"POST",
      url:URL+"LoginController/Login",
      data:{ EMPNUM:EmpNumber, PASS:Password },
      success:function (data) {debugger

        // Display backend result on the DOM
        // $(".Js_LgnResult").html(data);
        window.location.href = "http://stackoverflow.com";

      },
      error:function (xhr) {
        alert("Error: " +xhr.responseText);
      }

    });
    // End ajax

  });
  // End Click

});
// End Scope
<commit_msg>Se creo la valiadcion para redirigi al usuario al dasboard del admin<commit_after>$(document).ready(function () {

  // Set the main path on the server
  let URL="http://localhost/EntracesExitEmployees/index.php/"

  $("#js_LoginBtn").click(function () {

    // Get Employee Number
    var EmpNumber=$("#js_EmployeeNmbr").val();

    // Get Employe Password
    var Password=$("#js_Password").val();

    // Start ajax
    $.ajax({
      type:"POST",
      url:URL+"LoginController/Login",
      data:{ EMPNUM:EmpNumber, PASS:Password },
      success:function (data) {

        // Display backend result on the DOM
        $(".Js_LgnResult").html(data).hide();

        // Get Login val. Bolean. Parse string to a number
        var LoginVal= parseInt($(".Js_LgnResult").text());

        // Validate login Val
        if (LoginVal == 1) {

          // Redirect User to the Login view
          window.location.href = URL+"LoginController/AdminPanel";

        }else {

          alert("Please Verify Employee Information");

        }

      },
      error:function (xhr) {

        alert("Error: " +xhr.responseText);

      }

    });
    // End ajax

  });
  // End Click

});
// End Scope
<|endoftext|>


<commit_before>const DrawCard = require('../../drawcard.js');

class AgashaSumiko extends DrawCard {
    setupCardAbilities(ability) {
        this.persistentEffect({
            match: this,
            condition: this.controller.imperialFavor !== '',
            effect: ability.effects.doesNotBowAsAttacker
        });
    }
}

AgashaSumiko.id = 'agasha-sumiko';

module.exports = AgashaSumiko;
<commit_msg>Fixed condition for not bowing<commit_after>const DrawCard = require('../../drawcard.js');

class AgashaSumiko extends DrawCard {
    setupCardAbilities(ability) {
        this.persistentEffect({
            match: this,
            condition: () => (
                this.controller.imperialFavor !== '' &&
                this.isAttacking()
            ),
            effect: ability.effects.doesNotBowAsAttacker()
        });
    }
}

AgashaSumiko.id = 'agasha-sumiko';

module.exports = AgashaSumiko;
<|endoftext|>


<commit_before>// Generic forms user page (Part A)

'use strict';

var _                = require('mano').i18n.bind('View: Business Process')
  , generateSections = require('./components/generate-form-sections')
  , progressRules    = require('./components/progress-rules');

exports._parent = require('./business-process-data-forms-tabbed');

exports['forms-sections-content'] = function () {
	var nextPageLink = this.section._nextSection.map(function (nextSection) {
		if (!nextSection) return exports._nextSectionUrl.call(this);
		return '/forms/' + nextSection.pageUrl + '/';
	}.bind(this));

	insert(
		_if(this.section._legend, div({ class: 'info-main' },
			md(this.section._legend))),
		disabler(
			exports._disableCondition.call(this),
			this.section.applicableSections ?
					[progressRules(this.section),
						generateSections(this.section.applicableSections, { viewContext: this })] :
					this.section.toDOMForm(document)
		),
		_if(and(nextPageLink, eq(this.section._status, 1)), p({ class: 'user-next-step-button' },
			a({ href: nextPageLink }, _('Continue to next step'))))
	);
};

exports._disableCondition = function () {
	return not(eq(this.businessProcess._guideProgress, 1));
};

exports._nextSectionUrl = function () { return '/documents/'; };

exports._match = 'section';
<commit_msg>#1465 fixed<commit_after>// Generic forms user page (Part A)

'use strict';

var _                = require('mano').i18n.bind('View: Business Process')
  , generateSections = require('./components/generate-form-sections')
  , progressRules    = require('./components/progress-rules');

exports._parent = require('./business-process-data-forms-tabbed');

exports['forms-sections-content'] = function () {
	var nextPageLink = this.section._nextSection.map(function (nextSection) {
		if (!nextSection) return exports._nextSectionUrl.call(this);
		return exports._parent._rootUrl.call(this) + nextSection.pageUrl + '/';
	}.bind(this));

	insert(
		_if(this.section._legend, div({ class: 'info-main' },
			md(this.section._legend))),
		disabler(
			exports._disableCondition.call(this),
			this.section.applicableSections ?
					[progressRules(this.section),
						generateSections(this.section.applicableSections, { viewContext: this })] :
					this.section.toDOMForm(document)
		),
		_if(and(nextPageLink, eq(this.section._status, 1)), p({ class: 'user-next-step-button' },
			a({ href: nextPageLink }, _('Continue to next step'))))
	);
};

exports._disableCondition = function () {
	return not(eq(this.businessProcess._guideProgress, 1));
};

exports._nextSectionUrl = function () { return '/documents/'; };

exports._match = 'section';
<|endoftext|>


<commit_before>var webpack = require('webpack');
var path = require('path');

var isProduction = process.argv.indexOf('-p') >= 0;
var sourcePath = path.join(__dirname, './src');
var outPath = path.join(__dirname, './dist');

var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  context: sourcePath,
  entry: {
    main: './Main.tsx',
    vendor: [
      'react',
      'react-dom',
      'mobx',
      'mobx-react',
    ]
  },
  output: {
    path: outPath,
    filename: 'bundle.js',
    publicPath: '/'
  },
  target: 'web',
  resolve: {
    extensions: ['.js', '.ts', '.tsx'],
    modules: [path.resolve(__dirname, 'src'), 'node_modules'],
    alias: {
      images: path.resolve(__dirname, 'resources', 'images'),
      styles: path.resolve(__dirname, 'resources', 'styles'),
    },
    // Fix webpack's default behavior to not load packages with jsnext:main module
    // (jsnext:main directs not usually distributable es6 format, but es6 sources)
    mainFields: ['module', 'browser', 'main']
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: isProduction
          ? 'awesome-typescript-loader?module=es6'
          : [
            'react-hot-loader',
            'awesome-typescript-loader'
          ]
      },
      {
        test: /\.scss$/,
        loaders: 'style-loader!css-loader!sass-loader'
      },
      { test: /\.html$/, use: 'html-loader' },
      { test: /\.png$/, use: 'url-loader?limit=10000' },
      { test: /\.jpg$/, use: 'file-loader' },
      {
        test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
        loader: 'file-loader?name=fonts/[name].[ext]'
      },
    ]
  },
  plugins: [
    new webpack.LoaderOptionsPlugin({
      options: {
        context: sourcePath
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      filename: 'vendor.bundle.js',
      minChunks: Infinity
    }),
    new webpack.optimize.AggressiveMergingPlugin(),
    new ExtractTextPlugin({
      filename: 'styles.css',
      disable: !isProduction
    }),
    new HtmlWebpackPlugin({
      template: 'index.html'
    })
  ],
  devServer: {
    hot: true,
    historyApiFallback: true,
    stats: {
      warnings: false
    },
  }
};
<commit_msg>Update webpack config to reflect changed resources directory name<commit_after>var webpack = require('webpack');
var path = require('path');

var isProduction = process.argv.indexOf('-p') >= 0;
var sourcePath = path.join(__dirname, './src');
var outPath = path.join(__dirname, './dist');

var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  context: sourcePath,
  entry: {
    main: './Main.tsx',
    vendor: [
      'react',
      'react-dom',
      'mobx',
      'mobx-react',
    ]
  },
  output: {
    path: outPath,
    filename: 'bundle.js',
    publicPath: '/'
  },
  target: 'web',
  resolve: {
    extensions: ['.js', '.ts', '.tsx'],
    modules: [path.resolve(__dirname, 'src'), 'node_modules'],
    alias: {
      images: path.resolve(__dirname, 'res', 'images'),
      styles: path.resolve(__dirname, 'res', 'styles'),
    },
    // Fix webpack's default behavior to not load packages with jsnext:main module
    // (jsnext:main directs not usually distributable es6 format, but es6 sources)
    mainFields: ['module', 'browser', 'main']
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: isProduction
          ? 'awesome-typescript-loader?module=es6'
          : [
            'react-hot-loader',
            'awesome-typescript-loader'
          ]
      },
      {
        test: /\.scss$/,
        loaders: 'style-loader!css-loader!sass-loader'
      },
      { test: /\.html$/, use: 'html-loader' },
      { test: /\.png$/, use: 'url-loader?limit=10000' },
      { test: /\.jpg$/, use: 'file-loader' },
      {
        test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
        loader: 'file-loader?name=fonts/[name].[ext]'
      },
    ]
  },
  plugins: [
    new webpack.LoaderOptionsPlugin({
      options: {
        context: sourcePath
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      filename: 'vendor.bundle.js',
      minChunks: Infinity
    }),
    new webpack.optimize.AggressiveMergingPlugin(),
    new ExtractTextPlugin({
      filename: 'styles.css',
      disable: !isProduction
    }),
    new HtmlWebpackPlugin({
      template: 'index.html'
    })
  ],
  devServer: {
    hot: true,
    historyApiFallback: true,
    stats: {
      warnings: false
    },
  }
};
<|endoftext|>


<commit_before>var id = $.getUrlVar("id");
getUserImageIds = $.ajax( {
	url: "db.php",
	async: true,
	type: "POST",
	dataType: "json",
	data: {"method":"getuserimageids", "id": id }
});
getUserData = $.ajax( {
	url: "db.php",
	async: true,
	type: "POST",
	dataType: "json",
	data: {"method":"getuserdata", "id": id }
});
function resizeLightbox(){
	var lightbox_length = $('#lightbox').width() - 150*3;
	if (lightbox_length < 0){
		lightbox_length = 0;
	}
	$('.lightbox').css({"margin": "0 "+ lightbox_length / 6 +"px"});
}
function whatnext(e){
	$('#body').prepend( '

' );
	$('#overlay').fadeIn();
	$('#lightbox').fadeIn(100);
	resizeLightbox();
};
$( window ).on("resize",resizeLightbox);
jQuery( document ).ready(function() {
	$("#imglist").on( "click", "img", whatnext);

	getUserData.done(function(data){
		$( "#name" ).html( "Photos for "+data[0].nickname+" ("+data[0].firstname+" "+data[0].lastname+")" );
	});
	getUserImageIds.done(function(data){
		for(var i = 0, l = data.length; i < l; ++i){
			$( "#imglist" ).append( '' );
		}
	});

});
<commit_msg>floor<commit_after>var id = $.getUrlVar("id");
getUserImageIds = $.ajax( {
	url: "db.php",
	async: true,
	type: "POST",
	dataType: "json",
	data: {"method":"getuserimageids", "id": id }
});
getUserData = $.ajax( {
	url: "db.php",
	async: true,
	type: "POST",
	dataType: "json",
	data: {"method":"getuserdata", "id": id }
});
function resizeLightbox(){
	var lightbox_length = floor($('#lightbox').width() - 150*3);
	if (lightbox_length < 0){
		lightbox_length = 0;
	}
	$('.lightbox').css({"margin": "0 "+ lightbox_length / 6 +"px"});
}
function whatnext(e){
	$('#body').prepend( '

' );
	$('#overlay').fadeIn();
	$('#lightbox').fadeIn(100);
	resizeLightbox();
};
$( window ).on("resize",resizeLightbox);
jQuery( document ).ready(function() {
	$("#imglist").on( "click", "img", whatnext);

	getUserData.done(function(data){
		$( "#name" ).html( "Photos for "+data[0].nickname+" ("+data[0].firstname+" "+data[0].lastname+")" );
	});
	getUserImageIds.done(function(data){
		for(var i = 0, l = data.length; i < l; ++i){
			$( "#imglist" ).append( '' );
		}
	});

});
<|endoftext|>


<commit_before>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/view') ;

/** @class

  Used to display an iframe.

  @extends SC.View
  @since SproutCore 1.0
*/
SC.IFrameView = SC.View.extend({

  emptyElement: '

',
  styleClass: 'sc-iframe-view',

  content: null,
  contentBindingDefault: SC.Binding.single().notEmpty(),

  contentUrlKey: null,
  contentUrlKeyBindingDefault: SC.Binding.single(),

  displayProperties: ['content'],

  updateDisplay: function() {
    var content = this.get('content') ;
    var urlKey = this.get('contentUrlKey') ;
    var src = '';

    console.log(content);

    switch (content) {
      case SC.MULTIPLE_PLACEHOLDER:
      case SC.EMPTY_PLACEHOLDER:
      case SC.NULL_PLACEHOLDER:
        src = static_url('blank');
        break;
      default:
        src = (urlKey) ? content.get(urlKey) : content ;
        break;
    }

    console.log(src) ;

    this.$('iframe').attr('src', src) ;
  },

  /** @private */
  _contentDidChange: function(target,key,value,propertyRevision) {
    // handle changes to the content...
    if ((value = this.get('content')) != this._field_content) {

      // get the handler method
      var f = this._contentPropertyDidChange ;

      // stop listening to old content.
      if (this._field_content) {
        if (SC.isArray(this._field_content)) {
          this._field_content.invoke('removeObserver', '*', this, f) ;
        } else if (this._field_content.removeObserver) {
          this._field_content.removeObserver('*', this, f) ;
        }
      }

      // start listening for changes on the new content object.
      this._field_content = value ;
      if (value) {
        if (SC.isArray(value)) {
          value.invoke('addObserver', '*', this, f) ;
        } else if (value.addObserver) {
          value.addObserver('*', this, f) ;
        }
      }

      // notify everyone that everything is different now.
      this.allPropertiesDidChange() ;
      this.endPropertyChanges() ;
    }
  }.observes('content'),

  /** @private Invoked when properties on the content object change. */
  _contentPropertyDidChange: function(target,key,value, propertyRevision) {
    if (key === '*' || key === this.get('contentUrlKey')) {
      this.displayDidChange() ;
    }
  }

}) ;<commit_msg>add delegate API to SC.IFrameView<commit_after>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/view') ;

/** @class

  Used to display an iframe.

  @extends SC.View
  @since SproutCore 1.0
*/
SC.IFrameView = SC.View.extend({

  emptyElement: '

',
  styleClass: 'sc-iframe-view',

  content: null,
  contentBindingDefault: SC.Binding.single().notEmpty(),

  contentUrlKey: null,
  contentUrlKeyBindingDefault: SC.Binding.single(),

  displayProperties: ['content'],

  $iframe: function() { return this.$('iframe') ; },

  delegate: null,

  iFrameViewWillLoadUrl: function(view, url) { return url; },
  iFrameViewDidLoadUrl: function(view, url) {},

  updateDisplay: function() {
    var content = this.get('content') ;
    var urlKey = this.get('contentUrlKey') ;
    var src = '';

    switch (content) {
      case SC.MULTIPLE_PLACEHOLDER:
      case SC.EMPTY_PLACEHOLDER:
      case SC.NULL_PLACEHOLDER:
        src = static_url('blank');
        break;
      default:
        src = (urlKey) ? content.get(urlKey) : content ;
        break;
    }

    src = this.invokeDelegateMethod(this.delegate, 'iFrameViewWillLoadUrl', this, src) || '' ;
    var iframe = this.$iframe() ;
    iframe.attr('src', 'javascript:;') ; // clear out the previous src, to force a reload
    iframe.attr('src', src) ;
    this.invokeDelegateMethod(this.delegate, 'iFrameViewDidLoadUrl', this, src) ;
  },

  /** @private */
  _contentDidChange: function(target,key,value,propertyRevision) {
    // handle changes to the content...
    if ((value = this.get('content')) != this._field_content) {

      // get the handler method
      var f = this._contentPropertyDidChange ;

      // stop listening to old content.
      if (this._field_content) {
        if (SC.isArray(this._field_content)) {
          this._field_content.invoke('removeObserver', '*', this, f) ;
        } else if (this._field_content.removeObserver) {
          this._field_content.removeObserver('*', this, f) ;
        }
      }

      // start listening for changes on the new content object.
      this._field_content = value ;
      if (value) {
        if (SC.isArray(value)) {
          value.invoke('addObserver', '*', this, f) ;
        } else if (value.addObserver) {
          value.addObserver('*', this, f) ;
        }
      }

      // notify everyone that everything is different now.
      this.allPropertiesDidChange() ;
      this.endPropertyChanges() ;
    }
  }.observes('content'),

  /** @private Invoked when properties on the content object change. */
  _contentPropertyDidChange: function(target,key,value, propertyRevision) {
    if (key === '*' || key === this.get('contentUrlKey')) {
      this.displayDidChange() ;
    }
  }

}) ;<|endoftext|>