mirror of
https://github.com/discourse/discourse.git
synced 2025-03-23 19:35:38 +08:00
Add routing for category edit screens (#11027)
Also fixes category editing for instances with slug generation set to "none".
This commit is contained in:
parent
6d4cfbf120
commit
ab6894ea36
@ -3,10 +3,14 @@ import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { propertyEqual } from "discourse/lib/computed";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { empty } from "@ember/object/computed";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "li",
|
||||
classNameBindings: ["active", "tabClassName"],
|
||||
newCategory: empty("params.slug"),
|
||||
|
||||
@discourseComputed("tab")
|
||||
tabClassName(tab) {
|
||||
@ -25,23 +29,32 @@ export default Component.extend({
|
||||
scheduleOnce("afterRender", this, this._addToCollection);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.setProperties({
|
||||
selectedTab: "general",
|
||||
params: {},
|
||||
});
|
||||
},
|
||||
|
||||
_addToCollection: function () {
|
||||
this.panels.addObject(this.tabClassName);
|
||||
},
|
||||
|
||||
_resetModalScrollState() {
|
||||
const $modalBody = $(this.element)
|
||||
.parents("#discourse-modal")
|
||||
.find(".modal-body");
|
||||
if ($modalBody.length === 1) {
|
||||
$modalBody.scrollTop(0);
|
||||
}
|
||||
@discourseComputed("params.slug", "params.parentSlug")
|
||||
fullSlug(slug, parentSlug) {
|
||||
const slugPart = parentSlug && slug ? `${parentSlug}/${slug}` : slug;
|
||||
return getURL(`/c/${slugPart}/edit/${this.tab}`);
|
||||
},
|
||||
|
||||
actions: {
|
||||
select: function () {
|
||||
this.set("selectedTab", this.tab);
|
||||
this._resetModalScrollState();
|
||||
|
||||
if (!this.newCategory) {
|
||||
DiscourseURL.routeTo(this.fullSlug);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -15,6 +15,7 @@ export default Controller.extend({
|
||||
createdCategory: false,
|
||||
expandedMenu: false,
|
||||
mobileView: readOnly("site.mobileView"),
|
||||
parentParams: null,
|
||||
|
||||
@on("init")
|
||||
_initPanels() {
|
@ -485,6 +485,25 @@ Category.reopenClass({
|
||||
return ajax(`/c/${slugPath}/find_by_slug.json`);
|
||||
},
|
||||
|
||||
reloadCategoryWithPermissions(params, store, site) {
|
||||
if (params.slug && params.slug.match(/^\d+-category/)) {
|
||||
const id = parseInt(params.slug, 10);
|
||||
return this.reloadById(id).then((result) =>
|
||||
this._includePermissions(result.category, store, site)
|
||||
);
|
||||
}
|
||||
return this.reloadBySlug(params.slug, params.parentSlug).then((result) =>
|
||||
this._includePermissions(result.category, store, site)
|
||||
);
|
||||
},
|
||||
|
||||
_includePermissions(category, store, site) {
|
||||
const record = store.createRecord("category", category);
|
||||
record.setupGroupsAndPermissions();
|
||||
site.updateCategory(record);
|
||||
return record;
|
||||
},
|
||||
|
||||
search(term, opts) {
|
||||
var limit = 5;
|
||||
|
||||
|
@ -22,6 +22,16 @@ export default function () {
|
||||
this.route("topicBySlugOrId", { path: "/t/:slugOrId", resetNamespace: true });
|
||||
|
||||
this.route("newCategory", { path: "/new-category" });
|
||||
this.route("editCategory", { path: "/c/:slug/edit" }, function () {
|
||||
this.route("tabs", { path: "/:tab" });
|
||||
});
|
||||
this.route(
|
||||
"editChildCategory",
|
||||
{ path: "/c/:parentSlug/:slug/edit" },
|
||||
function () {
|
||||
this.route("tabs", { path: "/:tab" });
|
||||
}
|
||||
);
|
||||
|
||||
this.route("discovery", { path: "/", resetNamespace: true }, function () {
|
||||
// legacy route
|
||||
@ -65,8 +75,6 @@ export default function () {
|
||||
});
|
||||
|
||||
this.route("categories");
|
||||
this.route("editCategory", { path: "/c/:slug/edit" });
|
||||
this.route("editChildCategory", { path: "/c/:parentSlug/:slug/edit" });
|
||||
|
||||
// legacy routes
|
||||
this.route("parentCategory", { path: "/c/:slug" });
|
||||
|
@ -1,28 +0,0 @@
|
||||
import I18n from "I18n";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model(params) {
|
||||
return Category.reloadBySlugPath(params.slug).then((result) => {
|
||||
const record = this.store.createRecord("category", result.category);
|
||||
record.setupGroupsAndPermissions();
|
||||
this.site.updateCategory(record);
|
||||
return record;
|
||||
});
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
return I18n.t("category.edit_dialog_title", {
|
||||
categoryName: this.currentModel.name,
|
||||
});
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
this.render("edit-category", {
|
||||
controller: "edit-category",
|
||||
outlet: "list-container",
|
||||
model: this.currentModel,
|
||||
});
|
||||
},
|
||||
});
|
@ -1,30 +0,0 @@
|
||||
import I18n from "I18n";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model(params) {
|
||||
return Category.reloadBySlug(params.slug, params.parentSlug).then(
|
||||
(result) => {
|
||||
const record = this.store.createRecord("category", result.category);
|
||||
record.setupGroupsAndPermissions();
|
||||
this.site.updateCategory(record);
|
||||
return record;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
return I18n.t("category.edit_dialog_title", {
|
||||
categoryName: this.currentModel.name,
|
||||
});
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
this.render("edit-category", {
|
||||
controller: "edit-category",
|
||||
outlet: "list-container",
|
||||
model: this.currentModel,
|
||||
});
|
||||
},
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
afterModel() {
|
||||
const params = this.paramsFor("editCategory");
|
||||
this.replaceWith(`/c/${params.slug}/edit/general`);
|
||||
},
|
||||
});
|
@ -0,0 +1,18 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model() {
|
||||
return this.modelFor("editCategory");
|
||||
},
|
||||
|
||||
setupController(controller, model, transition) {
|
||||
this._super(...arguments);
|
||||
|
||||
const parentParams = this.paramsFor("editCategory");
|
||||
|
||||
controller.setProperties({
|
||||
parentParams,
|
||||
selectedTab: transition.to.params.tab,
|
||||
});
|
||||
},
|
||||
});
|
19
app/assets/javascripts/discourse/app/routes/edit-category.js
Normal file
19
app/assets/javascripts/discourse/app/routes/edit-category.js
Normal file
@ -0,0 +1,19 @@
|
||||
import I18n from "I18n";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model(params) {
|
||||
return Category.reloadCategoryWithPermissions(
|
||||
params,
|
||||
this.store,
|
||||
this.site
|
||||
);
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
return I18n.t("category.edit_dialog_title", {
|
||||
categoryName: this.currentModel.name,
|
||||
});
|
||||
},
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
afterModel() {
|
||||
const params = this.paramsFor("editChildCategory");
|
||||
this.replaceWith(`/c/${params.parentSlug}/${params.slug}/edit/general`);
|
||||
},
|
||||
});
|
@ -0,0 +1,19 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model() {
|
||||
return this.modelFor("editChildCategory");
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
this.render("edit-category-tabs", {
|
||||
controller: "edit-category-tabs",
|
||||
model: this.currentModel,
|
||||
});
|
||||
|
||||
this.controllerFor("editCategory.tabs").set(
|
||||
"parentParams",
|
||||
this.paramsFor("editChildCategory")
|
||||
);
|
||||
},
|
||||
});
|
@ -0,0 +1,19 @@
|
||||
import I18n from "I18n";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model(params) {
|
||||
return Category.reloadCategoryWithPermissions(
|
||||
params,
|
||||
this.store,
|
||||
this.site
|
||||
);
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
return I18n.t("category.edit_dialog_title", {
|
||||
categoryName: this.currentModel.name,
|
||||
});
|
||||
},
|
||||
});
|
@ -24,8 +24,8 @@ export default DiscourseRoute.extend({
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
this.render("edit-category", {
|
||||
controller: "edit-category",
|
||||
this.render("edit-category-tabs", {
|
||||
controller: "edit-category-tabs",
|
||||
model: this.currentModel,
|
||||
});
|
||||
},
|
||||
|
@ -1,12 +1,12 @@
|
||||
<div class="edit-category {{if expandedMenu "expanded-menu"}}">
|
||||
<ul class="nav nav-stacked">
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab tab="general"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab tab="security"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab tab="settings"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab tab="images"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab tab="topic-template"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab params=parentParams tab="general"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab params=parentParams tab="security"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab params=parentParams tab="settings"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab params=parentParams tab="images"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab params=parentParams tab="topic-template"}}
|
||||
{{#if siteSettings.tagging_enabled}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab tab="tags"}}
|
||||
{{edit-category-tab panels=panels selectedTab=selectedTab params=parentParams tab="tags"}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
|
@ -7,10 +7,7 @@ acceptance("Category Edit - security", function (needs) {
|
||||
needs.user();
|
||||
|
||||
test("default", async (assert) => {
|
||||
await visit("/c/bug");
|
||||
|
||||
await click(".edit-category");
|
||||
await click("li.edit-category-security a");
|
||||
await visit("/c/bug/edit/security");
|
||||
|
||||
const $permissionListItems = find(".permission-list li");
|
||||
|
||||
@ -24,10 +21,8 @@ acceptance("Category Edit - security", function (needs) {
|
||||
test("removing a permission", async (assert) => {
|
||||
const availableGroups = selectKit(".available-groups");
|
||||
|
||||
await visit("/c/bug");
|
||||
await visit("/c/bug/edit/security");
|
||||
|
||||
await click(".edit-category");
|
||||
await click("li.edit-category-security a");
|
||||
await click(".edit-category-tab-security .edit-permission");
|
||||
await availableGroups.expand();
|
||||
|
||||
@ -51,10 +46,8 @@ acceptance("Category Edit - security", function (needs) {
|
||||
const availableGroups = selectKit(".available-groups");
|
||||
const permissionSelector = selectKit(".permission-selector");
|
||||
|
||||
await visit("/c/bug");
|
||||
await visit("/c/bug/edit/security");
|
||||
|
||||
await click(".edit-category");
|
||||
await click("li.edit-category-security a");
|
||||
await click(".edit-category-tab-security .edit-permission");
|
||||
await availableGroups.expand();
|
||||
await availableGroups.selectRowByValue("staff");
|
||||
@ -76,10 +69,8 @@ acceptance("Category Edit - security", function (needs) {
|
||||
test("adding a previously removed permission", async (assert) => {
|
||||
const availableGroups = selectKit(".available-groups");
|
||||
|
||||
await visit("/c/bug");
|
||||
await visit("/c/bug/edit/security");
|
||||
|
||||
await click(".edit-category");
|
||||
await click("li.edit-category-security a");
|
||||
await click(".edit-category-tab-security .edit-permission");
|
||||
await click(
|
||||
".edit-category-tab-security .permission-list li:first-of-type .remove-permission"
|
||||
|
@ -2,6 +2,8 @@ import { click, fillIn, visit, currentURL } from "@ember/test-helpers";
|
||||
import { test } from "qunit";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import sinon from "sinon";
|
||||
|
||||
acceptance("Category Edit", function (needs) {
|
||||
needs.user();
|
||||
@ -11,7 +13,11 @@ acceptance("Category Edit", function (needs) {
|
||||
await visit("/c/bug");
|
||||
|
||||
await click("button.edit-category");
|
||||
assert.equal(currentURL(), "/c/bug/edit", "it jumps to the correct screen");
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
"/c/bug/edit/general",
|
||||
"it jumps to the correct screen"
|
||||
);
|
||||
|
||||
assert.equal(find(".badge-category").text(), "bug");
|
||||
await fillIn("input.category-name", "testing");
|
||||
@ -22,19 +28,55 @@ acceptance("Category Edit", function (needs) {
|
||||
await click(".edit-category-topic-template");
|
||||
await fillIn(".d-editor-input", "this is the new topic template");
|
||||
|
||||
await click(".edit-category-settings");
|
||||
await click("#save-category");
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
"/c/bug/edit/general",
|
||||
"it stays on the edit screen"
|
||||
);
|
||||
|
||||
await visit("/c/bug/edit/settings");
|
||||
const searchPriorityChooser = selectKit("#category-search-priority");
|
||||
await searchPriorityChooser.expand();
|
||||
await searchPriorityChooser.selectRowByValue(1);
|
||||
|
||||
await click("#save-category");
|
||||
assert.equal(currentURL(), "/c/bug/edit", "it stays on the edit screen");
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
"/c/bug/edit/settings",
|
||||
"it stays on the edit screen"
|
||||
);
|
||||
|
||||
sinon.stub(DiscourseURL, "routeTo");
|
||||
|
||||
await click(".edit-category-security a");
|
||||
assert.ok(
|
||||
DiscourseURL.routeTo.calledWith("/c/bug/edit/security"),
|
||||
"tab routing works"
|
||||
);
|
||||
});
|
||||
|
||||
test("Index Route", async (assert) => {
|
||||
await visit("/c/bug/edit");
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
"/c/bug/edit/general",
|
||||
"it redirects to the general tab"
|
||||
);
|
||||
});
|
||||
|
||||
test("Slugless Route", async (assert) => {
|
||||
await visit("/c/1-category/edit");
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
"/c/1-category/edit/general",
|
||||
"it goes to the general tab"
|
||||
);
|
||||
assert.equal(find("input.category-name").val(), "bug");
|
||||
});
|
||||
|
||||
test("Error Saving", async (assert) => {
|
||||
await visit("/c/bug");
|
||||
await click("button.edit-category");
|
||||
await click(".edit-category-settings");
|
||||
await visit("/c/bug/edit/settings");
|
||||
await fillIn(".email-in", "duplicate@example.com");
|
||||
await click("#save-category");
|
||||
|
||||
@ -46,13 +88,7 @@ acceptance("Category Edit", function (needs) {
|
||||
});
|
||||
|
||||
test("Subcategory list settings", async (assert) => {
|
||||
const categoryChooser = selectKit(
|
||||
".edit-category-tab-general .category-chooser"
|
||||
);
|
||||
|
||||
await visit("/c/bug");
|
||||
await click("button.edit-category");
|
||||
await click(".edit-category-settings a");
|
||||
await visit("/c/bug/edit/settings");
|
||||
|
||||
assert.ok(
|
||||
!visible(".subcategory-list-style-field"),
|
||||
@ -66,11 +102,15 @@ acceptance("Category Edit", function (needs) {
|
||||
"subcategory list style is shown if show subcategory list is checked"
|
||||
);
|
||||
|
||||
await click(".edit-category-general");
|
||||
await visit("/c/bug/edit/general");
|
||||
|
||||
const categoryChooser = selectKit(
|
||||
".edit-category-tab-general .category-chooser"
|
||||
);
|
||||
await categoryChooser.expand();
|
||||
await categoryChooser.selectRowByValue(3);
|
||||
|
||||
await click(".edit-category-settings a");
|
||||
await visit("/c/bug/edit/settings");
|
||||
|
||||
assert.ok(
|
||||
!visible(".show-subcategory-list-field"),
|
||||
|
@ -3,6 +3,7 @@ import { test } from "qunit";
|
||||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||
import I18n from "I18n";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import sinon from "sinon";
|
||||
|
||||
acceptance("Category New", function (needs) {
|
||||
needs.user();
|
||||
@ -23,11 +24,23 @@ acceptance("Category New", function (needs) {
|
||||
})
|
||||
);
|
||||
|
||||
await click(".category-back");
|
||||
await click(".edit-category-security a");
|
||||
assert.ok(
|
||||
find("button.edit-permission"),
|
||||
"it can switch to the security tab"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
DiscourseURL.redirectedTo,
|
||||
"/c/testing/11",
|
||||
await click(".edit-category-settings a");
|
||||
assert.ok(
|
||||
find("#category-search-priority"),
|
||||
"it can switch to the settings tab"
|
||||
);
|
||||
|
||||
sinon.stub(DiscourseURL, "redirectTo");
|
||||
|
||||
await click(".category-back");
|
||||
assert.ok(
|
||||
DiscourseURL.redirectTo.calledWith("/c/testing/11"),
|
||||
"it full page redirects after a newly created category"
|
||||
);
|
||||
});
|
||||
|
@ -4,6 +4,8 @@ import I18n from "I18n";
|
||||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||
import PreloadStore from "discourse/lib/preload-store";
|
||||
import { parsePostData } from "discourse/tests/helpers/create-pretender";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import sinon from "sinon";
|
||||
|
||||
acceptance("Password Reset", function (needs) {
|
||||
needs.pretender((server, helper) => {
|
||||
@ -84,8 +86,9 @@ acceptance("Password Reset", function (needs) {
|
||||
);
|
||||
|
||||
await fillIn(".password-reset input", "perf3ctly5ecur3");
|
||||
sinon.stub(DiscourseURL, "redirectTo");
|
||||
await click(".password-reset form button");
|
||||
assert.ok(!exists(".password-reset form"), "form is gone");
|
||||
assert.ok(DiscourseURL.redirectTo.calledWith("/"), "form is gone");
|
||||
});
|
||||
|
||||
test("Password Reset Page With Second Factor", async (assert) => {
|
||||
@ -116,8 +119,12 @@ acceptance("Password Reset", function (needs) {
|
||||
assert.ok(exists("#new-account-password"), "shows the input");
|
||||
|
||||
await fillIn(".password-reset input", "perf3ctly5ecur3");
|
||||
await click(".password-reset form button");
|
||||
|
||||
assert.ok(!exists(".password-reset form"), "form is gone");
|
||||
sinon.stub(DiscourseURL, "redirectTo");
|
||||
await click(".password-reset form button");
|
||||
assert.ok(
|
||||
DiscourseURL.redirectTo.calledWith("/"),
|
||||
"it redirects after submitting form"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -12,7 +12,6 @@ import createPretender, {
|
||||
} from "discourse/tests/helpers/create-pretender";
|
||||
import { flushMap } from "discourse/models/store";
|
||||
import { ScrollingDOMMethods } from "discourse/mixins/scrolling";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import {
|
||||
resetSite,
|
||||
applyPretender,
|
||||
@ -130,11 +129,6 @@ export default function setupTests(app, container) {
|
||||
site,
|
||||
});
|
||||
|
||||
DiscourseURL.redirectedTo = null;
|
||||
DiscourseURL.redirectTo = function (url) {
|
||||
DiscourseURL.redirectedTo = url;
|
||||
};
|
||||
|
||||
PreloadStore.reset();
|
||||
|
||||
window.sinon.stub(ScrollingDOMMethods, "screenNotFull");
|
||||
|
@ -682,8 +682,8 @@ Discourse::Application.routes.draw do
|
||||
|
||||
get "c/:category_slug/find_by_slug" => "categories#find_by_slug"
|
||||
get "c/:parent_category_slug/:category_slug/find_by_slug" => "categories#find_by_slug"
|
||||
get "c/:category_slug/edit" => "categories#find_by_slug", constraints: { format: 'html' }
|
||||
get "c/:parent_category_slug/:category_slug/edit" => "categories#find_by_slug", constraints: { format: 'html' }
|
||||
get "c/:category_slug/edit(/:tab)" => "categories#find_by_slug", constraints: { format: 'html' }
|
||||
get "c/:parent_category_slug/:category_slug/edit(/:tab)" => "categories#find_by_slug", constraints: { format: 'html' }
|
||||
get "/new-category" => "categories#show", constraints: { format: 'html' }
|
||||
|
||||
get "c/*category_slug_path_with_id.rss" => "list#category_feed", format: :rss
|
||||
|
Loading…
x
Reference in New Issue
Block a user