2019-10-31 04:28:29 +08:00
|
|
|
import { COMPONENTS, THEMES } from "admin/models/theme";
|
2020-03-07 00:41:41 +08:00
|
|
|
import Controller, { inject as controller } from "@ember/controller";
|
2019-10-31 04:28:29 +08:00
|
|
|
import { alias, equal, match } from "@ember/object/computed";
|
2020-01-17 01:56:53 +08:00
|
|
|
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
2020-05-14 04:23:41 +08:00
|
|
|
import I18n from "I18n";
|
2019-02-21 03:58:31 +08:00
|
|
|
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
2019-06-03 22:47:17 +08:00
|
|
|
import { POPULAR_THEMES } from "discourse-common/helpers/popular-themes";
|
2019-02-21 03:58:31 +08:00
|
|
|
import { ajax } from "discourse/lib/ajax";
|
|
|
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
2019-11-06 02:43:49 +08:00
|
|
|
import { set } from "@ember/object";
|
2019-02-21 03:58:31 +08:00
|
|
|
|
|
|
|
const MIN_NAME_LENGTH = 4;
|
|
|
|
|
2019-10-24 01:06:54 +08:00
|
|
|
export default Controller.extend(ModalFunctionality, {
|
2020-03-07 00:41:41 +08:00
|
|
|
adminCustomizeThemes: controller(),
|
|
|
|
themesController: controller("adminCustomizeThemes"),
|
2019-10-31 04:28:29 +08:00
|
|
|
popular: equal("selection", "popular"),
|
|
|
|
local: equal("selection", "local"),
|
|
|
|
remote: equal("selection", "remote"),
|
|
|
|
create: equal("selection", "create"),
|
2021-02-19 01:51:44 +08:00
|
|
|
directRepoInstall: equal("selection", "directRepoInstall"),
|
2019-02-21 03:58:31 +08:00
|
|
|
selection: "popular",
|
|
|
|
loading: false,
|
|
|
|
keyGenUrl: "/admin/themes/generate_key_pair",
|
|
|
|
importUrl: "/admin/themes/import",
|
2019-02-28 18:48:30 +08:00
|
|
|
recordType: "theme",
|
2022-08-31 03:05:43 +08:00
|
|
|
checkPrivate: match("uploadUrl", /^ssh:\/\/.+@.+$|.+@.+:.+$/),
|
2019-02-21 03:58:31 +08:00
|
|
|
localFile: null,
|
|
|
|
uploadUrl: null,
|
2021-02-19 01:51:44 +08:00
|
|
|
uploadName: null,
|
2019-02-21 03:58:31 +08:00
|
|
|
advancedVisible: false,
|
2019-10-31 04:28:29 +08:00
|
|
|
selectedType: alias("themesController.currentTab"),
|
|
|
|
component: equal("selectedType", COMPONENTS),
|
2019-02-21 03:58:31 +08:00
|
|
|
|
2019-05-28 18:15:12 +08:00
|
|
|
init() {
|
|
|
|
this._super(...arguments);
|
|
|
|
|
|
|
|
this.createTypes = [
|
|
|
|
{ name: I18n.t("admin.customize.theme.theme"), value: THEMES },
|
|
|
|
{ name: I18n.t("admin.customize.theme.component"), value: COMPONENTS },
|
|
|
|
];
|
|
|
|
},
|
|
|
|
|
2019-11-08 05:38:28 +08:00
|
|
|
@discourseComputed("themesController.installedThemes")
|
2019-02-21 03:58:31 +08:00
|
|
|
themes(installedThemes) {
|
|
|
|
return POPULAR_THEMES.map((t) => {
|
2021-02-25 05:10:17 +08:00
|
|
|
if (
|
|
|
|
installedThemes.some((theme) => this.themeHasSameUrl(theme, t.value))
|
|
|
|
) {
|
2019-11-06 02:43:49 +08:00
|
|
|
set(t, "installed", true);
|
2019-02-21 03:58:31 +08:00
|
|
|
}
|
|
|
|
return t;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2019-11-08 05:38:28 +08:00
|
|
|
@discourseComputed(
|
2019-02-21 03:58:31 +08:00
|
|
|
"loading",
|
|
|
|
"remote",
|
|
|
|
"uploadUrl",
|
|
|
|
"local",
|
|
|
|
"localFile",
|
|
|
|
"create",
|
|
|
|
"nameTooShort"
|
|
|
|
)
|
|
|
|
installDisabled(
|
|
|
|
isLoading,
|
|
|
|
isRemote,
|
|
|
|
uploadUrl,
|
|
|
|
isLocal,
|
|
|
|
localFile,
|
|
|
|
isCreate,
|
|
|
|
nameTooShort
|
|
|
|
) {
|
|
|
|
return (
|
|
|
|
isLoading ||
|
|
|
|
(isRemote && !uploadUrl) ||
|
|
|
|
(isLocal && !localFile) ||
|
|
|
|
(isCreate && nameTooShort)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2021-01-11 23:27:31 +08:00
|
|
|
@discourseComputed("privateChecked")
|
|
|
|
urlPlaceholder(privateChecked) {
|
|
|
|
return privateChecked
|
|
|
|
? "git@github.com:discourse/sample_theme.git"
|
|
|
|
: "https://github.com/discourse/sample_theme";
|
|
|
|
},
|
|
|
|
|
2019-02-21 03:58:31 +08:00
|
|
|
@observes("privateChecked")
|
|
|
|
privateWasChecked() {
|
2019-05-27 16:15:39 +08:00
|
|
|
const checked = this.privateChecked;
|
2019-02-21 03:58:31 +08:00
|
|
|
if (checked && !this._keyLoading) {
|
|
|
|
this._keyLoading = true;
|
2020-03-27 04:00:10 +08:00
|
|
|
ajax(this.keyGenUrl, { type: "POST" })
|
2019-02-21 03:58:31 +08:00
|
|
|
.then((pair) => {
|
2022-09-01 18:15:23 +08:00
|
|
|
this.set("publicKey", pair.public_key);
|
2019-02-21 03:58:31 +08:00
|
|
|
})
|
|
|
|
.catch(popupAjaxError)
|
|
|
|
.finally(() => {
|
|
|
|
this._keyLoading = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-11-08 05:38:28 +08:00
|
|
|
@discourseComputed("name")
|
2019-02-21 03:58:31 +08:00
|
|
|
nameTooShort(name) {
|
|
|
|
return !name || name.length < MIN_NAME_LENGTH;
|
|
|
|
},
|
|
|
|
|
2019-11-08 05:38:28 +08:00
|
|
|
@discourseComputed("component")
|
2019-02-21 03:58:31 +08:00
|
|
|
placeholder(component) {
|
|
|
|
if (component) {
|
|
|
|
return I18n.t("admin.customize.theme.component_name");
|
|
|
|
} else {
|
|
|
|
return I18n.t("admin.customize.theme.theme_name");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2022-08-10 18:30:18 +08:00
|
|
|
@discourseComputed("selection", "themeCannotBeInstalled")
|
|
|
|
submitLabel(selection, themeCannotBeInstalled) {
|
|
|
|
if (themeCannotBeInstalled) {
|
|
|
|
return "admin.customize.theme.create_placeholder";
|
|
|
|
}
|
|
|
|
|
2019-02-21 03:58:31 +08:00
|
|
|
return `admin.customize.theme.${
|
|
|
|
selection === "create" ? "create" : "install"
|
|
|
|
}`;
|
|
|
|
},
|
|
|
|
|
2019-11-08 05:38:28 +08:00
|
|
|
@discourseComputed("privateChecked", "checkPrivate", "publicKey")
|
2019-02-21 03:58:31 +08:00
|
|
|
showPublicKey(privateChecked, checkPrivate, publicKey) {
|
|
|
|
return privateChecked && checkPrivate && publicKey;
|
|
|
|
},
|
|
|
|
|
2021-01-08 02:03:35 +08:00
|
|
|
onClose() {
|
2021-01-11 23:27:31 +08:00
|
|
|
this.setProperties({
|
|
|
|
duplicateRemoteThemeWarning: null,
|
|
|
|
privateChecked: false,
|
|
|
|
localFile: null,
|
|
|
|
uploadUrl: null,
|
|
|
|
publicKey: null,
|
|
|
|
branch: null,
|
2021-02-19 01:51:44 +08:00
|
|
|
selection: "popular",
|
2021-01-11 23:27:31 +08:00
|
|
|
});
|
2021-01-08 02:03:35 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
themeHasSameUrl(theme, url) {
|
|
|
|
const themeUrl = theme.remote_theme && theme.remote_theme.remote_url;
|
|
|
|
return (
|
|
|
|
themeUrl &&
|
|
|
|
url &&
|
|
|
|
url.replace(/\.git$/, "") === themeUrl.replace(/\.git$/, "")
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2019-02-21 03:58:31 +08:00
|
|
|
actions: {
|
|
|
|
uploadLocaleFile() {
|
|
|
|
this.set("localFile", $("#file-input")[0].files[0]);
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleAdvanced() {
|
|
|
|
this.toggleProperty("advancedVisible");
|
|
|
|
},
|
|
|
|
|
|
|
|
installThemeFromList(url) {
|
|
|
|
this.set("uploadUrl", url);
|
|
|
|
this.send("installTheme");
|
|
|
|
},
|
|
|
|
|
|
|
|
installTheme() {
|
2019-05-27 16:15:39 +08:00
|
|
|
if (this.create) {
|
2019-02-21 03:58:31 +08:00
|
|
|
this.set("loading", true);
|
2019-05-27 16:15:39 +08:00
|
|
|
const theme = this.store.createRecord(this.recordType);
|
2019-02-21 03:58:31 +08:00
|
|
|
theme
|
2019-05-27 16:15:39 +08:00
|
|
|
.save({ name: this.name, component: this.component })
|
2019-02-21 03:58:31 +08:00
|
|
|
.then(() => {
|
2019-05-27 16:15:39 +08:00
|
|
|
this.themesController.send("addTheme", theme);
|
2019-02-21 03:58:31 +08:00
|
|
|
this.send("closeModal");
|
|
|
|
})
|
|
|
|
.catch(popupAjaxError)
|
|
|
|
.finally(() => this.set("loading", false));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let options = {
|
|
|
|
type: "POST",
|
|
|
|
};
|
|
|
|
|
2019-05-27 16:15:39 +08:00
|
|
|
if (this.local) {
|
2019-02-21 03:58:31 +08:00
|
|
|
options.processData = false;
|
|
|
|
options.contentType = false;
|
|
|
|
options.data = new FormData();
|
2019-05-27 16:15:39 +08:00
|
|
|
options.data.append("theme", this.localFile);
|
2019-02-21 03:58:31 +08:00
|
|
|
}
|
|
|
|
|
2021-02-19 01:51:44 +08:00
|
|
|
if (this.remote || this.popular || this.directRepoInstall) {
|
2021-01-08 02:03:35 +08:00
|
|
|
const duplicate = this.themesController.model.content.find((theme) =>
|
|
|
|
this.themeHasSameUrl(theme, this.uploadUrl)
|
|
|
|
);
|
|
|
|
if (duplicate && !this.duplicateRemoteThemeWarning) {
|
|
|
|
const warning = I18n.t(
|
|
|
|
"admin.customize.theme.duplicate_remote_theme",
|
|
|
|
{ name: duplicate.name }
|
|
|
|
);
|
|
|
|
this.set("duplicateRemoteThemeWarning", warning);
|
|
|
|
return;
|
|
|
|
}
|
2019-02-21 03:58:31 +08:00
|
|
|
options.data = {
|
2019-05-27 16:15:39 +08:00
|
|
|
remote: this.uploadUrl,
|
|
|
|
branch: this.branch,
|
2019-02-21 03:58:31 +08:00
|
|
|
};
|
|
|
|
|
2019-05-27 16:15:39 +08:00
|
|
|
if (this.privateChecked) {
|
2022-09-01 18:15:23 +08:00
|
|
|
options.data.public_key = this.publicKey;
|
2019-02-21 03:58:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-10 18:30:18 +08:00
|
|
|
// User knows that theme cannot be installed, but they want to continue
|
|
|
|
// to force install it.
|
|
|
|
if (this.themeCannotBeInstalled) {
|
|
|
|
options.data["force"] = true;
|
|
|
|
}
|
|
|
|
|
2019-02-21 03:58:31 +08:00
|
|
|
if (this.get("model.user_id")) {
|
|
|
|
// Used by theme-creator
|
|
|
|
options.data["user_id"] = this.get("model.user_id");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.set("loading", true);
|
2019-05-27 16:15:39 +08:00
|
|
|
ajax(this.importUrl, options)
|
2019-02-21 03:58:31 +08:00
|
|
|
.then((result) => {
|
2019-05-27 16:42:53 +08:00
|
|
|
const theme = this.store.createRecord(this.recordType, result.theme);
|
2019-05-27 16:15:39 +08:00
|
|
|
this.adminCustomizeThemes.send("addTheme", theme);
|
2019-02-21 03:58:31 +08:00
|
|
|
this.send("closeModal");
|
|
|
|
})
|
|
|
|
.then(() => {
|
2022-09-01 18:15:23 +08:00
|
|
|
this.set("publicKey", null);
|
2019-02-21 03:58:31 +08:00
|
|
|
})
|
2022-08-10 18:30:18 +08:00
|
|
|
.catch((error) => {
|
2022-09-01 18:15:23 +08:00
|
|
|
if (!this.publicKey || this.themeCannotBeInstalled) {
|
2022-08-10 18:30:18 +08:00
|
|
|
return popupAjaxError(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.set(
|
|
|
|
"themeCannotBeInstalled",
|
|
|
|
I18n.t("admin.customize.theme.force_install")
|
|
|
|
);
|
|
|
|
})
|
2019-02-21 03:58:31 +08:00
|
|
|
.finally(() => this.set("loading", false));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|