mirror of
https://github.com/discourse/discourse.git
synced 2025-03-06 03:09:43 +00:00
DEV: Update /admin/badges
to modern Ember patterns (#17672)
* Nest admin badges controller and route files * Use standard file names for admin-badges * Update resolver to allow standardized file structure for admin * Add adminBadges.index controller for property tracking * Modernize admin badges controller * Modernize admin-badges route * Add admin-badges index route * Modernize admin-badges.show controller and route * Modernize admin-badges.award controller and route * Convert BadgeButton to a Glimmer component
This commit is contained in:
parent
36446649ff
commit
40cb46631f
@ -1,99 +0,0 @@
|
||||
import Controller from "@ember/controller";
|
||||
import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import { extractError } from "discourse/lib/ajax-error";
|
||||
import { action } from "@ember/object";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
saving: false,
|
||||
replaceBadgeOwners: false,
|
||||
grantExistingHolders: false,
|
||||
fileSelected: false,
|
||||
unmatchedEntries: null,
|
||||
resultsMessage: null,
|
||||
success: false,
|
||||
unmatchedEntriesCount: 0,
|
||||
|
||||
resetState() {
|
||||
this.setProperties({
|
||||
saving: false,
|
||||
unmatchedEntries: null,
|
||||
resultsMessage: null,
|
||||
success: false,
|
||||
unmatchedEntriesCount: 0,
|
||||
});
|
||||
this.send("updateFileSelected");
|
||||
},
|
||||
|
||||
@discourseComputed("fileSelected", "saving")
|
||||
massAwardButtonDisabled(fileSelected, saving) {
|
||||
return !fileSelected || saving;
|
||||
},
|
||||
|
||||
@discourseComputed("unmatchedEntriesCount", "unmatchedEntries.length")
|
||||
unmatchedEntriesTruncated(unmatchedEntriesCount, length) {
|
||||
return unmatchedEntriesCount && length && unmatchedEntriesCount > length;
|
||||
},
|
||||
|
||||
@action
|
||||
updateFileSelected() {
|
||||
this.set(
|
||||
"fileSelected",
|
||||
!!document.querySelector("#massAwardCSVUpload")?.files?.length
|
||||
);
|
||||
},
|
||||
|
||||
@action
|
||||
massAward() {
|
||||
const file = document.querySelector("#massAwardCSVUpload").files[0];
|
||||
|
||||
if (this.model && file) {
|
||||
const options = {
|
||||
type: "POST",
|
||||
processData: false,
|
||||
contentType: false,
|
||||
data: new FormData(),
|
||||
};
|
||||
|
||||
options.data.append("file", file);
|
||||
options.data.append("replace_badge_owners", this.replaceBadgeOwners);
|
||||
options.data.append("grant_existing_holders", this.grantExistingHolders);
|
||||
|
||||
this.resetState();
|
||||
this.set("saving", true);
|
||||
|
||||
ajax(`/admin/badges/award/${this.model.id}`, options)
|
||||
.then(
|
||||
({
|
||||
matched_users_count: matchedCount,
|
||||
unmatched_entries: unmatchedEntries,
|
||||
unmatched_entries_count: unmatchedEntriesCount,
|
||||
}) => {
|
||||
this.setProperties({
|
||||
resultsMessage: I18n.t("admin.badges.mass_award.success", {
|
||||
count: matchedCount,
|
||||
}),
|
||||
success: true,
|
||||
});
|
||||
if (unmatchedEntries.length) {
|
||||
this.setProperties({
|
||||
unmatchedEntries,
|
||||
unmatchedEntriesCount,
|
||||
});
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((error) => {
|
||||
this.setProperties({
|
||||
resultsMessage: extractError(error),
|
||||
success: false,
|
||||
});
|
||||
})
|
||||
.finally(() => this.set("saving", false));
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.badges.mass_award.aborted"));
|
||||
}
|
||||
},
|
||||
});
|
@ -1,216 +0,0 @@
|
||||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
import bootbox from "bootbox";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { propertyNotEqual } from "discourse/lib/computed";
|
||||
import { equal, reads } from "@ember/object/computed";
|
||||
import { next } from "@ember/runloop";
|
||||
import { action } from "@ember/object";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
|
||||
const IMAGE = "image";
|
||||
const ICON = "icon";
|
||||
|
||||
export default Controller.extend(bufferedProperty("model"), {
|
||||
adminBadges: controller(),
|
||||
saving: false,
|
||||
savingStatus: "",
|
||||
selectedGraphicType: null,
|
||||
badgeTypes: reads("adminBadges.badgeTypes"),
|
||||
badgeGroupings: reads("adminBadges.badgeGroupings"),
|
||||
badgeTriggers: reads("adminBadges.badgeTriggers"),
|
||||
protectedSystemFields: reads("adminBadges.protectedSystemFields"),
|
||||
readOnly: reads("buffered.system"),
|
||||
showDisplayName: propertyNotEqual("name", "displayName"),
|
||||
iconSelectorSelected: equal("selectedGraphicType", ICON),
|
||||
imageUploaderSelected: equal("selectedGraphicType", IMAGE),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
// this is needed because the model doesnt have default values
|
||||
// and as we are using a bufferedProperty it's not accessible
|
||||
// in any other way
|
||||
next(() => {
|
||||
if (this.model) {
|
||||
if (!this.model.badge_type_id) {
|
||||
this.model.set(
|
||||
"badge_type_id",
|
||||
this.get("badgeTypes.firstObject.id")
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.model.badge_grouping_id) {
|
||||
this.model.set(
|
||||
"badge_grouping_id",
|
||||
this.get("badgeGroupings.firstObject.id")
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.model.trigger) {
|
||||
this.model.set("trigger", this.get("badgeTriggers.firstObject.id"));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed("model.query", "buffered.query")
|
||||
hasQuery(modelQuery, bufferedQuery) {
|
||||
if (bufferedQuery) {
|
||||
return bufferedQuery.trim().length > 0;
|
||||
}
|
||||
return modelQuery && modelQuery.trim().length > 0;
|
||||
},
|
||||
|
||||
@discourseComputed("model.i18n_name")
|
||||
textCustomizationPrefix(i18n_name) {
|
||||
return `badges.${i18n_name}.`;
|
||||
},
|
||||
|
||||
@observes("model.id")
|
||||
_resetSaving() {
|
||||
this.set("saving", false);
|
||||
this.set("savingStatus", "");
|
||||
},
|
||||
|
||||
showIconSelector() {
|
||||
this.set("selectedGraphicType", ICON);
|
||||
},
|
||||
|
||||
showImageUploader() {
|
||||
this.set("selectedGraphicType", IMAGE);
|
||||
},
|
||||
|
||||
@action
|
||||
changeGraphicType(newType) {
|
||||
if (newType === IMAGE) {
|
||||
this.showImageUploader();
|
||||
} else if (newType === ICON) {
|
||||
this.showIconSelector();
|
||||
} else {
|
||||
throw new Error(`Unknown badge graphic type "${newType}"`);
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
setImage(upload) {
|
||||
this.buffered.setProperties({
|
||||
image_upload_id: upload.id,
|
||||
image_url: getURL(upload.url),
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
removeImage() {
|
||||
this.buffered.setProperties({
|
||||
image_upload_id: null,
|
||||
image_url: null,
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
if (!this.saving) {
|
||||
let fields = [
|
||||
"allow_title",
|
||||
"multiple_grant",
|
||||
"listable",
|
||||
"auto_revoke",
|
||||
"enabled",
|
||||
"show_posts",
|
||||
"target_posts",
|
||||
"name",
|
||||
"description",
|
||||
"long_description",
|
||||
"icon",
|
||||
"image_upload_id",
|
||||
"query",
|
||||
"badge_grouping_id",
|
||||
"trigger",
|
||||
"badge_type_id",
|
||||
];
|
||||
|
||||
if (this.get("buffered.system")) {
|
||||
let protectedFields = this.protectedSystemFields || [];
|
||||
fields = fields.filter((f) => !protectedFields.includes(f));
|
||||
}
|
||||
|
||||
this.set("saving", true);
|
||||
this.set("savingStatus", I18n.t("saving"));
|
||||
|
||||
const boolFields = [
|
||||
"allow_title",
|
||||
"multiple_grant",
|
||||
"listable",
|
||||
"auto_revoke",
|
||||
"enabled",
|
||||
"show_posts",
|
||||
"target_posts",
|
||||
];
|
||||
|
||||
const data = {};
|
||||
const buffered = this.buffered;
|
||||
fields.forEach(function (field) {
|
||||
let d = buffered.get(field);
|
||||
if (boolFields.includes(field)) {
|
||||
d = !!d;
|
||||
}
|
||||
data[field] = d;
|
||||
});
|
||||
|
||||
const newBadge = !this.id;
|
||||
const model = this.model;
|
||||
this.model
|
||||
.save(data)
|
||||
.then(() => {
|
||||
if (newBadge) {
|
||||
const adminBadges = this.get("adminBadges.model");
|
||||
if (!adminBadges.includes(model)) {
|
||||
adminBadges.pushObject(model);
|
||||
}
|
||||
this.transitionToRoute("adminBadges.show", model.get("id"));
|
||||
} else {
|
||||
this.commitBuffer();
|
||||
this.set("savingStatus", I18n.t("saved"));
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => {
|
||||
this.set("saving", false);
|
||||
this.set("savingStatus", "");
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
destroy() {
|
||||
const adminBadges = this.get("adminBadges.model");
|
||||
const model = this.model;
|
||||
|
||||
if (!model.get("id")) {
|
||||
this.transitionToRoute("adminBadges.index");
|
||||
return;
|
||||
}
|
||||
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.badges.delete_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
(result) => {
|
||||
if (result) {
|
||||
model
|
||||
.destroy()
|
||||
.then(() => {
|
||||
adminBadges.removeObject(model);
|
||||
this.transitionToRoute("adminBadges.index");
|
||||
})
|
||||
.catch(() => {
|
||||
bootbox.alert(I18n.t("generic_error"));
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
@ -1,18 +1,23 @@
|
||||
import Controller from "@ember/controller";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default Controller.extend({
|
||||
routing: service("-routing"),
|
||||
export default class AdminBadgesController extends Controller {
|
||||
@service router;
|
||||
|
||||
@discourseComputed("routing.currentRouteName")
|
||||
selectedRoute() {
|
||||
const currentRoute = this.routing.currentRouteName;
|
||||
// Set by the route
|
||||
@tracked badgeGroupings;
|
||||
@tracked badgeTypes;
|
||||
@tracked protectedSystemFields;
|
||||
@tracked badgeTriggers;
|
||||
|
||||
get selectedRoute() {
|
||||
const currentRoute = this.router.currentRouteName;
|
||||
const indexRoute = "adminBadges.index";
|
||||
if (currentRoute === indexRoute) {
|
||||
return "adminBadges.show";
|
||||
} else {
|
||||
return this.routing.currentRouteName;
|
||||
return currentRoute;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
import Controller from "@ember/controller";
|
||||
import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import { extractError } from "discourse/lib/ajax-error";
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default class AdminBadgesAwardController extends Controller {
|
||||
@tracked saving = false;
|
||||
@tracked replaceBadgeOwners = false;
|
||||
@tracked grantExistingHolders = false;
|
||||
@tracked fileSelected = false;
|
||||
@tracked unmatchedEntries = null;
|
||||
@tracked resultsMessage = null;
|
||||
@tracked success = false;
|
||||
@tracked unmatchedEntriesCount = 0;
|
||||
|
||||
resetState() {
|
||||
this.saving = false;
|
||||
this.unmatchedEntries = null;
|
||||
this.resultsMessage = null;
|
||||
this.success = false;
|
||||
this.unmatchedEntriesCount = 0;
|
||||
|
||||
this.updateFileSelected();
|
||||
}
|
||||
|
||||
get massAwardButtonDisabled() {
|
||||
return !this.fileSelected || this.saving;
|
||||
}
|
||||
|
||||
get unmatchedEntriesTruncated() {
|
||||
let count = this.unmatchedEntriesCount;
|
||||
let length = this.unmatchedEntries.length;
|
||||
return count && length && count > length;
|
||||
}
|
||||
|
||||
@action
|
||||
updateFileSelected() {
|
||||
this.fileSelected = !!document.querySelector("#massAwardCSVUpload")?.files
|
||||
?.length;
|
||||
}
|
||||
|
||||
@action
|
||||
massAward() {
|
||||
const file = document.querySelector("#massAwardCSVUpload").files[0];
|
||||
|
||||
if (this.model && file) {
|
||||
const options = {
|
||||
type: "POST",
|
||||
processData: false,
|
||||
contentType: false,
|
||||
data: new FormData(),
|
||||
};
|
||||
|
||||
options.data.append("file", file);
|
||||
options.data.append("replace_badge_owners", this.replaceBadgeOwners);
|
||||
options.data.append("grant_existing_holders", this.grantExistingHolders);
|
||||
|
||||
this.resetState();
|
||||
this.saving = true;
|
||||
|
||||
ajax(`/admin/badges/award/${this.model.id}`, options)
|
||||
.then(
|
||||
({
|
||||
matched_users_count: matchedCount,
|
||||
unmatched_entries: unmatchedEntries,
|
||||
unmatched_entries_count: unmatchedEntriesCount,
|
||||
}) => {
|
||||
this.resultsMessage = I18n.t("admin.badges.mass_award.success", {
|
||||
count: matchedCount,
|
||||
});
|
||||
this.success = true;
|
||||
if (unmatchedEntries.length) {
|
||||
this.unmatchedEntries = unmatchedEntries;
|
||||
this.unmatchedEntriesCount = unmatchedEntriesCount;
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((error) => {
|
||||
this.resultsMessage = extractError(error);
|
||||
this.success = false;
|
||||
})
|
||||
.finally(() => (this.saving = false));
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.badges.mass_award.aborted"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default class AdminBadgesIndexController extends Controller {
|
||||
// Set by the route
|
||||
@tracked badgeIntroLinks;
|
||||
@tracked badgeIntroEmoji;
|
||||
}
|
@ -0,0 +1,238 @@
|
||||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
import bootbox from "bootbox";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { next } from "@ember/runloop";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
const IMAGE = "image";
|
||||
const ICON = "icon";
|
||||
|
||||
// TODO: Stop using Mixin here
|
||||
export default class AdminBadgesShowController extends Controller.extend(
|
||||
bufferedProperty("model")
|
||||
) {
|
||||
@controller adminBadges;
|
||||
@service router;
|
||||
|
||||
@tracked saving = false;
|
||||
@tracked savingStatus = "";
|
||||
@tracked selectedGraphicType = null;
|
||||
|
||||
get badgeTypes() {
|
||||
return this.adminBadges.badgeTypes;
|
||||
}
|
||||
|
||||
get badgeGroupings() {
|
||||
return this.adminBadges.badgeGroupings;
|
||||
}
|
||||
|
||||
get badgeTriggers() {
|
||||
return this.adminBadges.badgeTriggers;
|
||||
}
|
||||
|
||||
get protectedSystemFields() {
|
||||
return this.adminBadges.protectedSystemFields;
|
||||
}
|
||||
|
||||
get readOnly() {
|
||||
return this.buffered.get("system");
|
||||
}
|
||||
|
||||
get showDisplayName() {
|
||||
return this.name !== this.displayName;
|
||||
}
|
||||
|
||||
get iconSelectorSelected() {
|
||||
return this.selectedGraphicType === ICON;
|
||||
}
|
||||
|
||||
get imageUploaderSelected() {
|
||||
return this.selectedGraphicType === IMAGE;
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(...arguments);
|
||||
|
||||
// this is needed because the model doesnt have default values
|
||||
// and as we are using a bufferedProperty it's not accessible
|
||||
// in any other way
|
||||
next(() => {
|
||||
// Using `set` here isn't ideal, but we don't know that tracking is set up on the model yet.
|
||||
if (this.model) {
|
||||
if (!this.model.badge_type_id) {
|
||||
this.model.set("badge_type_id", this.badgeTypes?.[0]?.id);
|
||||
}
|
||||
|
||||
if (!this.model.badge_grouping_id) {
|
||||
this.model.set("badge_grouping_id", this.badgeGroupings?.[0]?.id);
|
||||
}
|
||||
|
||||
if (!this.model.trigger) {
|
||||
this.model.set("trigger", this.badgeTriggers?.[0]?.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get hasQuery() {
|
||||
let modelQuery = this.model.query;
|
||||
let bufferedQuery = this.bufferedQuery;
|
||||
|
||||
if (bufferedQuery) {
|
||||
return bufferedQuery.trim().length > 0;
|
||||
}
|
||||
return modelQuery && modelQuery.trim().length > 0;
|
||||
}
|
||||
|
||||
get textCustomizationPrefix() {
|
||||
return `badges.${this.model.i18n_name}.`;
|
||||
}
|
||||
|
||||
// FIXME: Remove observer
|
||||
@observes("model.id")
|
||||
_resetSaving() {
|
||||
this.saving = false;
|
||||
this.savingStatus = "";
|
||||
}
|
||||
|
||||
showIconSelector() {
|
||||
this.selectedGraphicType = ICON;
|
||||
}
|
||||
|
||||
showImageUploader() {
|
||||
this.selectedGraphicType = IMAGE;
|
||||
}
|
||||
|
||||
@action
|
||||
changeGraphicType(newType) {
|
||||
if (newType === IMAGE) {
|
||||
this.showImageUploader();
|
||||
} else if (newType === ICON) {
|
||||
this.showIconSelector();
|
||||
} else {
|
||||
throw new Error(`Unknown badge graphic type "${newType}"`);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
setImage(upload) {
|
||||
this.buffered.set("image_upload_id", upload.id);
|
||||
this.buffered.set("image_url", getURL(upload.url));
|
||||
}
|
||||
|
||||
@action
|
||||
removeImage() {
|
||||
this.buffered.set("image_upload_id", null);
|
||||
this.buffered.set("image_url", null);
|
||||
}
|
||||
|
||||
@action
|
||||
save() {
|
||||
if (!this.saving) {
|
||||
let fields = [
|
||||
"allow_title",
|
||||
"multiple_grant",
|
||||
"listable",
|
||||
"auto_revoke",
|
||||
"enabled",
|
||||
"show_posts",
|
||||
"target_posts",
|
||||
"name",
|
||||
"description",
|
||||
"long_description",
|
||||
"icon",
|
||||
"image_upload_id",
|
||||
"query",
|
||||
"badge_grouping_id",
|
||||
"trigger",
|
||||
"badge_type_id",
|
||||
];
|
||||
|
||||
if (this.buffered.get("system")) {
|
||||
let protectedFields = this.protectedSystemFields || [];
|
||||
fields = fields.filter((f) => !protectedFields.includes(f));
|
||||
}
|
||||
|
||||
this.saving = true;
|
||||
this.savingStatus = I18n.t("saving");
|
||||
|
||||
const boolFields = [
|
||||
"allow_title",
|
||||
"multiple_grant",
|
||||
"listable",
|
||||
"auto_revoke",
|
||||
"enabled",
|
||||
"show_posts",
|
||||
"target_posts",
|
||||
];
|
||||
|
||||
const data = {};
|
||||
const buffered = this.buffered;
|
||||
fields.forEach(function (field) {
|
||||
let d = buffered.get(field);
|
||||
if (boolFields.includes(field)) {
|
||||
d = !!d;
|
||||
}
|
||||
data[field] = d;
|
||||
});
|
||||
|
||||
const newBadge = !this.id;
|
||||
const model = this.model;
|
||||
this.model
|
||||
.save(data)
|
||||
.then(() => {
|
||||
if (newBadge) {
|
||||
const adminBadges = this.get("adminBadges.model");
|
||||
if (!adminBadges.includes(model)) {
|
||||
adminBadges.pushObject(model);
|
||||
}
|
||||
this.transitionToRoute("adminBadges.show", model.get("id"));
|
||||
} else {
|
||||
this.commitBuffer();
|
||||
this.savingStatus = I18n.t("saved");
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => {
|
||||
this.saving = false;
|
||||
this.savingStatus = "";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
destroyBadge() {
|
||||
const adminBadges = this.adminBadges.model;
|
||||
const model = this.model;
|
||||
|
||||
if (!model?.get("id")) {
|
||||
this.router.transitionTo("adminBadges.index");
|
||||
return;
|
||||
}
|
||||
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.badges.delete_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
(result) => {
|
||||
if (result) {
|
||||
model
|
||||
.destroy()
|
||||
.then(() => {
|
||||
adminBadges.removeObject(model);
|
||||
this.transitionToRoute("adminBadges.index");
|
||||
})
|
||||
.catch(() => {
|
||||
bootbox.alert(I18n.t("generic_error"));
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
import Badge from "discourse/models/badge";
|
||||
import I18n from "I18n";
|
||||
import Route from "@ember/routing/route";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import { get } from "@ember/object";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Route.extend({
|
||||
serialize(m) {
|
||||
return { badge_id: get(m, "id") || "new" };
|
||||
},
|
||||
|
||||
model(params) {
|
||||
if (params.badge_id === "new") {
|
||||
return Badge.create({
|
||||
name: I18n.t("admin.badges.new_badge"),
|
||||
});
|
||||
}
|
||||
return this.modelFor("adminBadges").findBy(
|
||||
"id",
|
||||
parseInt(params.badge_id, 10)
|
||||
);
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
this._super(...arguments);
|
||||
if (model.image_url) {
|
||||
controller.showImageUploader();
|
||||
} else if (model.icon) {
|
||||
controller.showIconSelector();
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
editGroupings() {
|
||||
const model = this.controllerFor("admin-badges").get("badgeGroupings");
|
||||
showModal("admin-edit-badge-groupings", { model, admin: true });
|
||||
},
|
||||
|
||||
preview(badge, explain) {
|
||||
badge.set("preview_loading", true);
|
||||
ajax("/admin/badges/preview.json", {
|
||||
type: "POST",
|
||||
data: {
|
||||
sql: badge.get("query"),
|
||||
target_posts: !!badge.get("target_posts"),
|
||||
trigger: badge.get("trigger"),
|
||||
explain,
|
||||
},
|
||||
})
|
||||
.then(function (model) {
|
||||
badge.set("preview_loading", false);
|
||||
showModal("admin-badge-preview", { model, admin: true });
|
||||
})
|
||||
.catch(function (error) {
|
||||
badge.set("preview_loading", false);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
bootbox.alert("Network error");
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
@ -4,15 +4,14 @@ import DiscourseRoute from "discourse/routes/discourse";
|
||||
import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
_json: null,
|
||||
export default class AdminBadgesRoute extends DiscourseRoute {
|
||||
_json = null;
|
||||
|
||||
model() {
|
||||
return ajax("/admin/badges.json").then((json) => {
|
||||
this._json = json;
|
||||
return Badge.createFromJson(json);
|
||||
});
|
||||
},
|
||||
async model() {
|
||||
let json = await ajax("/admin/badges.json");
|
||||
this._json = json;
|
||||
return Badge.createFromJson(json);
|
||||
}
|
||||
|
||||
setupController(controller, model) {
|
||||
const json = this._json;
|
||||
@ -31,12 +30,11 @@ export default DiscourseRoute.extend({
|
||||
badgeGroupings.push(BadgeGrouping.create(badgeGroupingJson));
|
||||
});
|
||||
|
||||
controller.setProperties({
|
||||
badgeGroupings,
|
||||
badgeTypes: json.badge_types,
|
||||
protectedSystemFields: json.admin_badges.protected_system_fields,
|
||||
badgeTriggers,
|
||||
model,
|
||||
});
|
||||
},
|
||||
});
|
||||
controller.badgeGroupings = badgeGroupings;
|
||||
controller.badgeTypes = json.badge_types;
|
||||
controller.protectedSystemFields =
|
||||
json.admin_badges.protected_system_fields;
|
||||
controller.badgeTriggers = badgeTriggers;
|
||||
controller.model = model;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Route from "discourse/routes/discourse";
|
||||
|
||||
export default Route.extend({
|
||||
export default class AdminBadgesAwardRoute extends Route {
|
||||
model(params) {
|
||||
if (params.badge_id !== "new") {
|
||||
return this.modelFor("adminBadges").findBy(
|
||||
@ -8,10 +8,10 @@ export default Route.extend({
|
||||
parseInt(params.badge_id, 10)
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
setupController(controller) {
|
||||
this._super(...arguments);
|
||||
super.setupController(...arguments);
|
||||
controller.resetState();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
@ -14,11 +14,9 @@ const badgeIntroLinks = [
|
||||
},
|
||||
];
|
||||
|
||||
export default Route.extend({
|
||||
export default class AdminBadgesIndexRoute extends Route {
|
||||
setupController(controller) {
|
||||
controller.setProperties({
|
||||
badgeIntroLinks,
|
||||
badgeIntroEmoji: emojiUrlFor("woman_student:t4"),
|
||||
});
|
||||
},
|
||||
});
|
||||
controller.badgeIntroLinks = badgeIntroLinks;
|
||||
controller.badgeIntroEmoji = emojiUrlFor("woman_student:t4");
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
import Badge from "discourse/models/badge";
|
||||
import I18n from "I18n";
|
||||
import Route from "@ember/routing/route";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import { action, get } from "@ember/object";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default class AdminBadgesShowRoute extends Route {
|
||||
serialize(m) {
|
||||
return { badge_id: get(m, "id") || "new" };
|
||||
}
|
||||
|
||||
model(params) {
|
||||
if (params.badge_id === "new") {
|
||||
return Badge.create({
|
||||
name: I18n.t("admin.badges.new_badge"),
|
||||
});
|
||||
}
|
||||
return this.modelFor("adminBadges").findBy(
|
||||
"id",
|
||||
parseInt(params.badge_id, 10)
|
||||
);
|
||||
}
|
||||
|
||||
setupController(controller, model) {
|
||||
super.setupController(...arguments);
|
||||
if (model.image_url) {
|
||||
controller.showImageUploader();
|
||||
} else if (model.icon) {
|
||||
controller.showIconSelector();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
editGroupings() {
|
||||
const model = this.controllerFor("admin-badges").get("badgeGroupings");
|
||||
showModal("admin-edit-badge-groupings", { model, admin: true });
|
||||
}
|
||||
|
||||
@action
|
||||
preview(badge, explain) {
|
||||
badge.set("preview_loading", true);
|
||||
ajax("/admin/badges/preview.json", {
|
||||
type: "POST",
|
||||
data: {
|
||||
sql: badge.get("query"),
|
||||
target_posts: !!badge.get("target_posts"),
|
||||
trigger: badge.get("trigger"),
|
||||
explain,
|
||||
},
|
||||
})
|
||||
.then(function (model) {
|
||||
badge.set("preview_loading", false);
|
||||
showModal("admin-badge-preview", { model, admin: true });
|
||||
})
|
||||
.catch(function (error) {
|
||||
badge.set("preview_loading", false);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
bootbox.alert("Network error");
|
||||
});
|
||||
}
|
||||
}
|
@ -159,7 +159,7 @@
|
||||
<DButton @class="btn-primary" @action={{action "save"}} @type="submit" @disabled={{this.saving}} @label="admin.badges.save" />
|
||||
<span class="saving">{{this.savingStatus}}</span>
|
||||
{{#unless this.readOnly}}
|
||||
<DButton @action={{action "destroy"}} @class="btn-danger" @label="admin.badges.delete" />
|
||||
<DButton @action={{action "destroyBadge"}} @class="btn-danger" @label="admin.badges.delete" />
|
||||
{{/unless}}
|
||||
</div>
|
||||
</form>
|
@ -353,6 +353,7 @@ export function buildResolver(baseName) {
|
||||
resolved =
|
||||
// Built-in
|
||||
this.findTemplate(adminParsedName, "admin/templates/") ||
|
||||
this.findTemplate(parsedName, "admin/templates/") ||
|
||||
// Plugin
|
||||
this.findTemplate(adminParsedName, "javascripts/admin/");
|
||||
}
|
||||
|
@ -1,16 +1,12 @@
|
||||
import Component from "@ember/component";
|
||||
import { computed } from "@ember/object";
|
||||
import Component from "@glimmer/component";
|
||||
import domFromString from "discourse-common/lib/dom-from-string";
|
||||
|
||||
// Takes @badge as argument.
|
||||
export default class BadgeButtonComponent extends Component {
|
||||
tagName = "";
|
||||
badge = null;
|
||||
|
||||
@computed("badge.description")
|
||||
get title() {
|
||||
if (this.badge?.description) {
|
||||
return domFromString(`<div>${this.badge?.description}</div>`)[0]
|
||||
.innerText;
|
||||
const description = this.args.badge?.description;
|
||||
if (description) {
|
||||
return domFromString(`<div>${description}</div>`)[0].innerText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<span class="user-badge {{this.badge.badgeTypeClassName}} {{unless this.badge.enabled "disabled"}}" title={{this.title}} data-badge-name={{this.badge.name}}>
|
||||
{{icon-or-image this.badge}}
|
||||
<span class="badge-display-name">{{this.badge.name}}</span>
|
||||
<span class="user-badge {{@badge.badgeTypeClassName}} {{unless @badge.enabled "disabled"}}" title={{this.title}} data-badge-name={{@badge.name}}>
|
||||
{{icon-or-image @badge}}
|
||||
<span class="badge-display-name">{{@badge.name}}</span>
|
||||
{{yield}}
|
||||
</span>
|
||||
|
Loading…
x
Reference in New Issue
Block a user