<commit_before>// # Themes API
// RESTful API for Themes
var Promise = require('bluebird'),
    _ = require('lodash'),
    gscan = require('gscan'),
    fs = require('fs-extra'),
    config = require('../config'),
    errors = require('../errors'),
    storage = require('../storage'),
    settings = require('./settings'),
    utils = require('./utils'),
    i18n = require('../i18n'),
    themes;

/**
 * ## Themes API Methods
 *
 * **See:** [API Methods](index.js.html#api%20methods)
 */
themes = {
    upload: function upload(options) {
        options = options || {};

        // consistent filename uploads
        options.originalname = options.originalname.toLowerCase();

        var zip = {
            path: options.path,
            name: options.originalname,
            shortName: options.originalname.split('.zip')[0]
        }, theme, storageAdapter = storage.getStorage('themes');

        // check if zip name is casper.zip
        if (zip.name === 'casper.zip') {
            throw new errors.ValidationError(i18n.t('errors.api.themes.overrideCasper'));
        }

        return utils.handlePermissions('themes', 'add')(options)
            .then(function () {
                return gscan.checkZip(zip, {keepExtractedDir: true});
            })
            .then(function (_theme) {
                theme = _theme;
                theme = gscan.format(theme);

                if (!theme.results.error.length) {
                    return;
                }

                throw new errors.ThemeValidationError(
                    i18n.t('errors.api.themes.invalidTheme'),
                    theme.results.error
                );
            })
            .then(function () {
                return storageAdapter.exists(config.paths.themePath + '/' + zip.shortName);
            })
            .then(function (themeExists) {
                // delete existing theme
                if (themeExists) {
                    return storageAdapter.delete(zip.shortName, config.paths.themePath);
                }
            })
            .then(function () {
                // store extracted theme
                return storageAdapter.save({
                    name: zip.shortName,
                    path: theme.path
                }, config.paths.themePath);
            })
            .then(function () {
                // force reload of availableThemes
                // right now the logic is in the ConfigManager
                // if we create a theme collection, we don't have to read them from disk
                return config.loadThemes();
            })
            .then(function () {
                // the settings endpoint is used to fetch the availableThemes
                // so we have to force updating the in process cache
                return settings.updateSettingsCache();
            })
            .then(function (settings) {
                // gscan theme structure !== ghost theme structure
                return {themes: [_.find(settings.availableThemes.value, {name: zip.shortName})]};
            })
            .finally(function () {
                // remove zip upload from multer
                // happens in background
                Promise.promisify(fs.removeSync)(zip.path)
                    .catch(function (err) {
                        errors.logError(err);
                    });

                // remove extracted dir from gscan
                // happens in background
                if (theme) {
                    Promise.promisify(fs.removeSync)(theme.path)
                        .catch(function (err) {
                            errors.logError(err);
                        });
                }
            });
    },

    download: function download(options) {
        var themeName = options.name,
            theme = config.paths.availableThemes[themeName],
            storageAdapter = storage.getStorage('themes');

        if (!theme) {
            return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.themes.invalidRequest')));
        }

        return utils.handlePermissions('themes', 'read')(options)
            .then(function () {
                return storageAdapter.serve({isTheme: true, name: themeName});
            });
    },

    /**
     * remove theme zip
     * remove theme folder
     */
    destroy: function destroy(options) {
        var name = options.name,
            theme,
            storageAdapter = storage.getStorage('themes');

        return utils.handlePermissions('themes', 'destroy')(options)
            .then(function () {
                if (name === 'casper') {
                    throw new errors.ValidationError(i18n.t('errors.api.themes.destroyCasper'));
                }

                theme = config.paths.availableThemes[name];

                if (!theme) {
                    throw new errors.NotFoundError(i18n.t('errors.api.themes.themeDoesNotExist'));
                }

                return storageAdapter.delete(name, config.paths.themePath);
            })
            .then(function () {
                return config.loadThemes();
            })
            .then(function () {
                return settings.updateSettingsCache();
            });
    }
};

module.exports = themes;<commit_msg>🎨 Theme events (#7269)<commit_after>// # Themes API
// RESTful API for Themes
var Promise = require('bluebird'),
    _ = require('lodash'),
    gscan = require('gscan'),
    fs = require('fs-extra'),
    config = require('../config'),
    errors = require('../errors'),
    events = require('../events'),
    storage = require('../storage'),
    settings = require('./settings'),
    utils = require('./utils'),
    i18n = require('../i18n'),
    themes;

/**
 * ## Themes API Methods
 *
 * **See:** [API Methods](index.js.html#api%20methods)
 */
themes = {
    upload: function upload(options) {
        options = options || {};

        // consistent filename uploads
        options.originalname = options.originalname.toLowerCase();

        var zip = {
            path: options.path,
            name: options.originalname,
            shortName: options.originalname.split('.zip')[0]
        }, theme, storageAdapter = storage.getStorage('themes');

        // check if zip name is casper.zip
        if (zip.name === 'casper.zip') {
            throw new errors.ValidationError(i18n.t('errors.api.themes.overrideCasper'));
        }

        return utils.handlePermissions('themes', 'add')(options)
            .then(function () {
                return gscan.checkZip(zip, {keepExtractedDir: true});
            })
            .then(function (_theme) {
                theme = _theme;
                theme = gscan.format(theme);

                if (!theme.results.error.length) {
                    return;
                }

                throw new errors.ThemeValidationError(
                    i18n.t('errors.api.themes.invalidTheme'),
                    theme.results.error
                );
            })
            .then(function () {
                return storageAdapter.exists(config.paths.themePath + '/' + zip.shortName);
            })
            .then(function (themeExists) {
                // delete existing theme
                if (themeExists) {
                    return storageAdapter.delete(zip.shortName, config.paths.themePath);
                }
            })
            .then(function () {
                events.emit('theme.uploaded', zip.shortName);
                // store extracted theme
                return storageAdapter.save({
                    name: zip.shortName,
                    path: theme.path
                }, config.paths.themePath);
            })
            .then(function () {
                // force reload of availableThemes
                // right now the logic is in the ConfigManager
                // if we create a theme collection, we don't have to read them from disk
                return config.loadThemes();
            })
            .then(function () {
                // the settings endpoint is used to fetch the availableThemes
                // so we have to force updating the in process cache
                return settings.updateSettingsCache();
            })
            .then(function (settings) {
                // gscan theme structure !== ghost theme structure
                return {themes: [_.find(settings.availableThemes.value, {name: zip.shortName})]};
            })
            .finally(function () {
                // remove zip upload from multer
                // happens in background
                Promise.promisify(fs.removeSync)(zip.path)
                    .catch(function (err) {
                        errors.logError(err);
                    });

                // remove extracted dir from gscan
                // happens in background
                if (theme) {
                    Promise.promisify(fs.removeSync)(theme.path)
                        .catch(function (err) {
                            errors.logError(err);
                        });
                }
            });
    },

    download: function download(options) {
        var themeName = options.name,
            theme = config.paths.availableThemes[themeName],
            storageAdapter = storage.getStorage('themes');

        if (!theme) {
            return Promise.reject(new errors.BadRequestError(i18n.t('errors.api.themes.invalidRequest')));
        }

        return utils.handlePermissions('themes', 'read')(options)
            .then(function () {
                events.emit('theme.downloaded', themeName);
                return storageAdapter.serve({isTheme: true, name: themeName});
            });
    },

    /**
     * remove theme zip
     * remove theme folder
     */
    destroy: function destroy(options) {
        var name = options.name,
            theme,
            storageAdapter = storage.getStorage('themes');

        return utils.handlePermissions('themes', 'destroy')(options)
            .then(function () {
                if (name === 'casper') {
                    throw new errors.ValidationError(i18n.t('errors.api.themes.destroyCasper'));
                }

                theme = config.paths.availableThemes[name];

                if (!theme) {
                    throw new errors.NotFoundError(i18n.t('errors.api.themes.themeDoesNotExist'));
                }

                events.emit('theme.deleted', name);
                return storageAdapter.delete(name, config.paths.themePath);
            })
            .then(function () {
                return config.loadThemes();
            })
            .then(function () {
                return settings.updateSettingsCache();
            });
    }
};

module.exports = themes;
{'fraction_non_alphanumeric': 0.11846496106785318, 'fraction_numerical': 0.0035224323322209862, 'mean_word_length': 1.4705747652850927, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4893792', 'n_tokens_mistral': 2745, 'n_tokens_neox': 2560, 'n_words': 556}