import { COMPONENTS, THEMES } from "admin/models/theme"; import { empty, filterBy, mapBy, match, notEmpty, } from "@ember/object/computed"; import Controller from "@ember/controller"; import EmberObject from "@ember/object"; import I18n from "I18n"; import ThemeSettings from "admin/models/theme-settings"; import bootbox from "bootbox"; import discourseComputed from "discourse-common/utils/decorators"; import { makeArray } from "discourse-common/lib/helpers"; import { popupAjaxError } from "discourse/lib/ajax-error"; import showModal from "discourse/lib/show-modal"; import { url } from "discourse/lib/computed"; const THEME_UPLOAD_VAR = 2; export default Controller.extend({ downloadUrl: url("model.id", "/admin/customize/themes/%@/export"), previewUrl: url("model.id", "/admin/themes/%@/preview"), addButtonDisabled: empty("selectedChildThemeId"), editRouteName: "adminCustomizeThemes.edit", parentThemesNames: mapBy("model.parentThemes", "name"), availableParentThemes: filterBy("allThemes", "component", false), availableActiveParentThemes: filterBy("availableParentThemes", "isActive"), availableThemesNames: mapBy("availableParentThemes", "name"), availableActiveThemesNames: mapBy("availableActiveParentThemes", "name"), availableActiveChildThemes: filterBy("availableChildThemes", "hasParents"), availableComponentsNames: mapBy("availableChildThemes", "name"), availableActiveComponentsNames: mapBy("availableActiveChildThemes", "name"), childThemesNames: mapBy("model.childThemes", "name"), extraFiles: filterBy("model.theme_fields", "target", "extra_js"), @discourseComputed("model.component", "model.remote_theme") showCheckboxes() { return !this.model.component || this.model.remote_theme; }, @discourseComputed("model.editedFields") editedFieldsFormatted() { const descriptions = []; ["common", "desktop", "mobile"].forEach((target) => { const fields = this.editedFieldsForTarget(target); if (fields.length < 1) { return; } let resultString = I18n.t("admin.customize.theme." + target); const formattedFields = fields .map((f) => I18n.t("admin.customize.theme." + f.name + ".text")) .join(" , "); resultString += `: ${formattedFields}`; descriptions.push(resultString); }); return descriptions; }, @discourseComputed("colorSchemeId", "model.color_scheme_id") colorSchemeChanged(colorSchemeId, existingId) { colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId, 10); return colorSchemeId !== existingId; }, @discourseComputed("availableChildThemes", "model.childThemes.[]", "model") selectableChildThemes(available, childThemes) { if (available) { const themes = !childThemes ? available : available.filter((theme) => !childThemes.includes(theme)); return themes.length === 0 ? null : themes; } }, @discourseComputed("model.parentThemes.[]") relativesSelectorSettingsForComponent() { return EmberObject.create({ list_type: "compact", type: "list", preview: null, anyValue: false, setting: "parent_theme_ids", label: I18n.t("admin.customize.theme.component_on_themes"), choices: this.availableThemesNames, default: this.parentThemesNames.join("|"), value: this.parentThemesNames.join("|"), defaultValues: this.availableActiveThemesNames.join("|"), allThemes: this.allThemes, setDefaultValuesLabel: I18n.t("admin.customize.theme.add_all_themes"), }); }, @discourseComputed("model.parentThemes.[]") relativesSelectorSettingsForTheme() { return EmberObject.create({ list_type: "compact", type: "list", preview: null, anyValue: false, setting: "child_theme_ids", label: I18n.t("admin.customize.theme.included_components"), choices: this.availableComponentsNames, default: this.childThemesNames.join("|"), value: this.childThemesNames.join("|"), defaultValues: this.availableActiveComponentsNames.join("|"), allThemes: this.allThemes, setDefaultValuesLabel: I18n.t("admin.customize.theme.add_all"), }); }, @discourseComputed("allThemes", "model.component", "model") availableChildThemes(allThemes) { if (!this.get("model.component")) { const themeId = this.get("model.id"); return allThemes.filter( (theme) => theme.get("id") !== themeId && theme.get("component") ); } }, @discourseComputed("model.component") convertKey(component) { const type = component ? "component" : "theme"; return `admin.customize.theme.convert_${type}`; }, @discourseComputed("model.component") convertIcon(component) { return component ? "cube" : ""; }, @discourseComputed("model.component") convertTooltip(component) { const type = component ? "component" : "theme"; return `admin.customize.theme.convert_${type}_tooltip`; }, @discourseComputed("model.settings") settings(settings) { return settings.map((setting) => ThemeSettings.create(setting)); }, hasSettings: notEmpty("settings"), @discourseComputed("model.translations") translations(translations) { return translations.map((setting) => ThemeSettings.create({ ...setting, textarea: true }) ); }, hasTranslations: notEmpty("translations"), @discourseComputed( "model.remote_theme.local_version", "model.remote_theme.remote_version", "model.remote_theme.commits_behind" ) hasOverwrittenHistory(localVersion, remoteVersion, commitsBehind) { return localVersion !== remoteVersion && commitsBehind === -1; }, @discourseComputed("model.remoteError", "updatingRemote") showRemoteError(errorMessage, updating) { return errorMessage && !updating; }, editedFieldsForTarget(target) { return this.get("model.editedFields").filter( (field) => field.target === target ); }, commitSwitchType() { const model = this.model; const newValue = !model.get("component"); model.set("component", newValue); if (newValue) { this.set("parentController.currentTab", COMPONENTS); } else { this.set("parentController.currentTab", THEMES); } model .saveChanges("component") .then(() => { this.set("colorSchemeId", null); model.setProperties({ default: false, color_scheme_id: null, user_selectable: false, child_themes: [], childThemes: [], }); this.get("parentController.model.content").forEach((theme) => { const children = makeArray(theme.get("childThemes")); const rawChildren = makeArray(theme.get("child_themes")); const index = children ? children.indexOf(model) : -1; if (index > -1) { children.splice(index, 1); rawChildren.splice(index, 1); theme.setProperties({ childThemes: children, child_themes: rawChildren, }); } }); }) .catch(popupAjaxError); }, transitionToEditRoute() { this.transitionToRoute( this.editRouteName, this.get("model.id"), "common", "scss" ); }, sourceIsHttp: match("model.remote_theme.remote_url", /^http(s)?:\/\//), @discourseComputed( "model.remote_theme.remote_url", "model.remote_theme.branch" ) remoteThemeLink(remoteThemeUrl, remoteThemeBranch) { return remoteThemeBranch ? `${remoteThemeUrl.replace(/\.git$/, "")}/tree/${remoteThemeBranch}` : remoteThemeUrl; }, @discourseComputed("model.user.id", "model.default") showConvert(userId, defaultTheme) { return userId > 0 && !defaultTheme; }, actions: { updateToLatest() { this.set("updatingRemote", true); this.model .updateToLatest() .catch(popupAjaxError) .finally(() => { this.set("updatingRemote", false); }); }, checkForThemeUpdates() { this.set("updatingRemote", true); this.model .checkForUpdates() .catch(popupAjaxError) .finally(() => { this.set("updatingRemote", false); }); }, addUploadModal() { showModal("admin-add-upload", { admin: true, name: "" }); }, addUpload(info) { let model = this.model; model.setField("common", info.name, "", info.upload_id, THEME_UPLOAD_VAR); model.saveChanges("theme_fields").catch((e) => popupAjaxError(e)); }, cancelChangeScheme() { this.set("colorSchemeId", this.get("model.color_scheme_id")); }, changeScheme() { let schemeId = this.colorSchemeId; this.set( "model.color_scheme_id", schemeId === null ? null : parseInt(schemeId, 10) ); this.model.saveChanges("color_scheme_id"); }, startEditingName() { this.set("oldName", this.get("model.name")); this.set("editingName", true); }, cancelEditingName() { this.set("model.name", this.oldName); this.set("editingName", false); }, finishedEditingName() { this.model.saveChanges("name"); this.set("editingName", false); }, editTheme() { if (this.get("model.remote_theme.is_git")) { bootbox.confirm( I18n.t("admin.customize.theme.edit_confirm"), (result) => { if (result) { this.transitionToEditRoute(); } } ); } else { this.transitionToEditRoute(); } }, applyDefault() { const model = this.model; model.saveChanges("default").then(() => { if (model.get("default")) { this.allThemes.forEach((theme) => { if (theme !== model && theme.get("default")) { theme.set("default", false); } }); } }); }, applyUserSelectable() { this.model.saveChanges("user_selectable"); }, applyAutoUpdateable() { this.model.saveChanges("auto_update"); }, addChildTheme() { let themeId = parseInt(this.selectedChildThemeId, 10); let theme = this.allThemes.findBy("id", themeId); this.model.addChildTheme(theme).then(() => this.store.findAll("theme")); }, removeUpload(upload) { return bootbox.confirm( I18n.t("admin.customize.theme.delete_upload_confirm"), I18n.t("no_value"), I18n.t("yes_value"), (result) => { if (result) { this.model.removeField(upload); } } ); }, removeChildTheme(theme) { this.model .removeChildTheme(theme) .then(() => this.store.findAll("theme")); }, destroy() { return bootbox.confirm( I18n.t("admin.customize.delete_confirm", { theme_name: this.get("model.name"), }), I18n.t("no_value"), I18n.t("yes_value"), (result) => { if (result) { const model = this.model; model.setProperties({ recentlyInstalled: false }); model.destroyRecord().then(() => { this.allThemes.removeObject(model); this.transitionToRoute("adminCustomizeThemes"); }); } } ); }, switchType() { const relatives = this.get("model.component") ? this.get("model.parentThemes") : this.get("model.childThemes"); let message = I18n.t(`${this.convertKey}_alert_generic`); if (relatives && relatives.length > 0) { message = I18n.t(`${this.convertKey}_alert`, { relatives: relatives .map((relative) => relative.get("name")) .join(", "), }); } bootbox.confirm( message, I18n.t("no_value"), I18n.t("yes_value"), (result) => { if (result) { this.commitSwitchType(); } } ); }, enableComponent() { this.model.set("enabled", true); this.model .saveChanges("enabled") .catch(() => this.model.set("enabled", false)); }, disableComponent() { this.model.set("enabled", false); this.model .saveChanges("enabled") .catch(() => this.model.set("enabled", true)); }, }, });