UX: Revamp category security tab (#11273)
This commit is contained in:
parent
dbcf722ab9
commit
7539c2ed7f
|
@ -0,0 +1,123 @@
|
||||||
|
import I18n from "I18n";
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||||
|
import PermissionType from "discourse/models/permission-type";
|
||||||
|
import { equal, alias } from "@ember/object/computed";
|
||||||
|
|
||||||
|
const EVERYONE = "everyone";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
classNames: ["permission-row", "row-body"],
|
||||||
|
canCreate: equal("type", PermissionType.FULL),
|
||||||
|
everyonePermissionType: alias("everyonePermission.permission_type"),
|
||||||
|
|
||||||
|
@discourseComputed("type")
|
||||||
|
canReply(value) {
|
||||||
|
return (
|
||||||
|
value === PermissionType.CREATE_POST || value === PermissionType.FULL
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("type")
|
||||||
|
canReplyIcon() {
|
||||||
|
return this.canReply ? "check-square" : "far-square";
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("type")
|
||||||
|
canCreateIcon() {
|
||||||
|
return this.canCreate ? "check-square" : "far-square";
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("type")
|
||||||
|
replyGranted() {
|
||||||
|
return this.type <= PermissionType.CREATE_POST ? "reply-granted" : "";
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("type")
|
||||||
|
createGranted() {
|
||||||
|
return this.type === PermissionType.FULL ? "create-granted" : "";
|
||||||
|
},
|
||||||
|
|
||||||
|
@observes("everyonePermissionType")
|
||||||
|
inheritFromEveryone() {
|
||||||
|
if (this.group_name === EVERYONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// groups cannot have a lesser permission than "everyone"
|
||||||
|
if (this.everyonePermissionType < this.type) {
|
||||||
|
this.updatePermission(this.everyonePermissionType);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("everyonePermissionType", "type")
|
||||||
|
replyDisabled(everyonePermissionType) {
|
||||||
|
if (
|
||||||
|
this.group_name !== EVERYONE &&
|
||||||
|
everyonePermissionType &&
|
||||||
|
everyonePermissionType <= PermissionType.CREATE_POST
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("replyDisabled")
|
||||||
|
replyTooltip() {
|
||||||
|
return this.replyDisabled
|
||||||
|
? I18n.t("category.permissions.inherited")
|
||||||
|
: I18n.t("category.permissions.toggle_reply");
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("everyonePermissionType", "type")
|
||||||
|
createDisabled(everyonePermissionType) {
|
||||||
|
if (
|
||||||
|
this.group_name !== EVERYONE &&
|
||||||
|
everyonePermissionType &&
|
||||||
|
everyonePermissionType === PermissionType.FULL
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("createDisabled")
|
||||||
|
createTooltip() {
|
||||||
|
return this.createDisabled
|
||||||
|
? I18n.t("category.permissions.inherited")
|
||||||
|
: I18n.t("category.permissions.toggle_full");
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePermission(type) {
|
||||||
|
this.category.updatePermission(this.group_name, type);
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
removeRow() {
|
||||||
|
this.category.removePermission(this.group_name);
|
||||||
|
},
|
||||||
|
|
||||||
|
setPermissionReply() {
|
||||||
|
if (this.type <= PermissionType.CREATE_POST) {
|
||||||
|
this.updatePermission(PermissionType.READONLY);
|
||||||
|
} else {
|
||||||
|
this.updatePermission(PermissionType.CREATE_POST);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setPermissionFull() {
|
||||||
|
if (
|
||||||
|
this.group_name !== EVERYONE &&
|
||||||
|
this.everyonePermissionType === PermissionType.FULL
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === PermissionType.FULL) {
|
||||||
|
this.updatePermission(PermissionType.CREATE_POST);
|
||||||
|
} else {
|
||||||
|
this.updatePermission(PermissionType.FULL);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,78 +1,39 @@
|
||||||
import { buildCategoryPanel } from "discourse/components/edit-category-panel";
|
import { buildCategoryPanel } from "discourse/components/edit-category-panel";
|
||||||
import PermissionType from "discourse/models/permission-type";
|
import PermissionType from "discourse/models/permission-type";
|
||||||
import { on } from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
|
import { not } from "@ember/object/computed";
|
||||||
|
|
||||||
export default buildCategoryPanel("security", {
|
export default buildCategoryPanel("security", {
|
||||||
editingPermissions: false,
|
|
||||||
selectedGroup: null,
|
selectedGroup: null,
|
||||||
selectedPermission: null,
|
noGroupSelected: not("selectedGroup"),
|
||||||
showPendingGroupChangesAlert: false,
|
|
||||||
interactedWithDropdowns: false,
|
|
||||||
|
|
||||||
@on("init")
|
@discourseComputed("category.permissions.@each.permission_type")
|
||||||
_setup() {
|
everyonePermission(permissions) {
|
||||||
this.setProperties({
|
return permissions.findBy("group_name", "everyone");
|
||||||
selectedGroup: this.get("category.availableGroups.firstObject"),
|
|
||||||
selectedPermission: this.get(
|
|
||||||
"category.availablePermissions.firstObject.id"
|
|
||||||
),
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@on("init")
|
@discourseComputed("category.permissions.@each.permission_type")
|
||||||
_registerValidator() {
|
everyoneGrantedFull() {
|
||||||
this.registerValidator(() => {
|
return (
|
||||||
if (
|
this.everyonePermission &&
|
||||||
!this.showPendingGroupChangesAlert &&
|
this.everyonePermission.permission_type === PermissionType.FULL
|
||||||
this.interactedWithDropdowns &&
|
);
|
||||||
this.activeTab
|
},
|
||||||
) {
|
|
||||||
this.set("showPendingGroupChangesAlert", true);
|
@discourseComputed("everyonePermission")
|
||||||
return true;
|
minimumPermission(everyonePermission) {
|
||||||
}
|
return everyonePermission
|
||||||
});
|
? everyonePermission.permission_type
|
||||||
|
: PermissionType.READONLY;
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
onSelectGroup(selectedGroup) {
|
onSelectGroup(group_name) {
|
||||||
this.setProperties({
|
|
||||||
interactedWithDropdowns: true,
|
|
||||||
selectedGroup,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onSelectPermission(selectedPermission) {
|
|
||||||
this.setProperties({
|
|
||||||
interactedWithDropdowns: true,
|
|
||||||
selectedPermission,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
editPermissions() {
|
|
||||||
if (!this.get("category.is_special")) {
|
|
||||||
this.set("editingPermissions", true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
addPermission(group, id) {
|
|
||||||
if (!this.get("category.is_special")) {
|
|
||||||
this.category.addPermission({
|
this.category.addPermission({
|
||||||
group_name: group + "",
|
group_name,
|
||||||
permission: PermissionType.create({ id: parseInt(id, 10) }),
|
permission_type: this.minimumPermission,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
this.setProperties({
|
|
||||||
selectedGroup: this.get("category.availableGroups.firstObject"),
|
|
||||||
showPendingGroupChangesAlert: false,
|
|
||||||
interactedWithDropdowns: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
removePermission(permission) {
|
|
||||||
if (!this.get("category.is_special")) {
|
|
||||||
this.category.removePermission(permission);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { propertyEqual } from "discourse/lib/computed";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { empty } from "@ember/object/computed";
|
import { empty } from "@ember/object/computed";
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
|
import { underscore } from "@ember/string";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "li",
|
tagName: "li",
|
||||||
|
@ -21,7 +22,7 @@ export default Component.extend({
|
||||||
|
|
||||||
@discourseComputed("tab")
|
@discourseComputed("tab")
|
||||||
title(tab) {
|
title(tab) {
|
||||||
return I18n.t("category." + tab.replace("-", "_"));
|
return I18n.t(`category.${underscore(tab)}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import DiscourseURL from "discourse/lib/url";
|
||||||
import { readOnly } from "@ember/object/computed";
|
import { readOnly } from "@ember/object/computed";
|
||||||
import PermissionType from "discourse/models/permission-type";
|
import PermissionType from "discourse/models/permission-type";
|
||||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||||
|
import { underscore } from "@ember/string";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
selectedTab: "general",
|
selectedTab: "general",
|
||||||
|
@ -69,6 +70,11 @@ export default Controller.extend({
|
||||||
: I18n.t("category.create");
|
: I18n.t("category.create");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("selectedTab")
|
||||||
|
selectedTabTitle(tab) {
|
||||||
|
return I18n.t(`category.${underscore(tab)}`);
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
registerValidator(validator) {
|
registerValidator(validator) {
|
||||||
this.validators.push(validator);
|
this.validators.push(validator);
|
||||||
|
|
|
@ -10,6 +10,8 @@ import Site from "discourse/models/site";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
import { getOwner } from "discourse-common/lib/get-owner";
|
||||||
|
|
||||||
|
const STAFF_GROUP_NAME = "staff";
|
||||||
|
|
||||||
const Category = RestModel.extend({
|
const Category = RestModel.extend({
|
||||||
permissions: null,
|
permissions: null,
|
||||||
|
|
||||||
|
@ -22,15 +24,13 @@ const Category = RestModel.extend({
|
||||||
this.set("availableGroups", availableGroups);
|
this.set("availableGroups", availableGroups);
|
||||||
|
|
||||||
const groupPermissions = this.group_permissions;
|
const groupPermissions = this.group_permissions;
|
||||||
|
|
||||||
if (groupPermissions) {
|
if (groupPermissions) {
|
||||||
this.set(
|
this.set(
|
||||||
"permissions",
|
"permissions",
|
||||||
groupPermissions.map((elem) => {
|
groupPermissions.map((elem) => {
|
||||||
availableGroups.removeObject(elem.group_name);
|
availableGroups.removeObject(elem.group_name);
|
||||||
return {
|
return elem;
|
||||||
group_name: elem.group_name,
|
|
||||||
permission: PermissionType.create({ id: elem.permission_type }),
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -231,7 +231,12 @@ const Category = RestModel.extend({
|
||||||
_permissionsForUpdate() {
|
_permissionsForUpdate() {
|
||||||
const permissions = this.permissions;
|
const permissions = this.permissions;
|
||||||
let rval = {};
|
let rval = {};
|
||||||
permissions.forEach((p) => (rval[p.group_name] = p.permission.id));
|
if (permissions.length) {
|
||||||
|
permissions.forEach((p) => (rval[p.group_name] = p.permission_type));
|
||||||
|
} else {
|
||||||
|
// empty permissions => staff-only access
|
||||||
|
rval[STAFF_GROUP_NAME] = PermissionType.FULL;
|
||||||
|
}
|
||||||
return rval;
|
return rval;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -246,9 +251,20 @@ const Category = RestModel.extend({
|
||||||
this.availableGroups.removeObject(permission.group_name);
|
this.availableGroups.removeObject(permission.group_name);
|
||||||
},
|
},
|
||||||
|
|
||||||
removePermission(permission) {
|
removePermission(group_name) {
|
||||||
|
const permission = this.permissions.findBy("group_name", group_name);
|
||||||
|
if (permission) {
|
||||||
this.permissions.removeObject(permission);
|
this.permissions.removeObject(permission);
|
||||||
this.availableGroups.addObject(permission.group_name);
|
this.availableGroups.addObject(group_name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePermission(group_name, type) {
|
||||||
|
this.permissions.forEach((p, i) => {
|
||||||
|
if (p.group_name === group_name) {
|
||||||
|
this.set(`permissions.${i}.permission_type`, type);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("topics")
|
@discourseComputed("topics")
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<span class="group-name">
|
||||||
|
<span class="group-name-label">{{group_name}}</span>
|
||||||
|
<a class="remove-permission" href {{action "removeRow"}}>
|
||||||
|
{{d-icon "far-trash-alt"}}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="options actionable">
|
||||||
|
{{d-button
|
||||||
|
icon="check-square"
|
||||||
|
class="btn btn-flat see"
|
||||||
|
disabled=true
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{d-button
|
||||||
|
icon=canReplyIcon
|
||||||
|
action=(action "setPermissionReply")
|
||||||
|
translatedTitle=replyTooltip
|
||||||
|
class=(concat "btn btn-flat reply-toggle " replyGranted)
|
||||||
|
disabled=replyDisabled
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{d-button
|
||||||
|
icon=canCreateIcon
|
||||||
|
action=(action "setPermissionFull")
|
||||||
|
translatedTitle=createTooltip
|
||||||
|
class=(concat "btn btn-flat create-toggle " createGranted)
|
||||||
|
disabled=createDisabled
|
||||||
|
}}
|
||||||
|
</span>
|
|
@ -12,14 +12,16 @@
|
||||||
<section class="field">
|
<section class="field">
|
||||||
<label>{{i18n "category.parent"}}</label>
|
<label>{{i18n "category.parent"}}</label>
|
||||||
{{category-chooser
|
{{category-chooser
|
||||||
rootNone=true
|
|
||||||
value=category.parent_category_id
|
value=category.parent_category_id
|
||||||
excludeCategoryId=category.id
|
|
||||||
categories=parentCategories
|
categories=parentCategories
|
||||||
allowSubCategories=true
|
allowSubCategories=true
|
||||||
allowUncategorized=false
|
allowUncategorized=false
|
||||||
allowRestrictedCategories=true
|
allowRestrictedCategories=true
|
||||||
onChange=(action (mut category.parent_category_id))
|
onChange=(action (mut category.parent_category_id))
|
||||||
|
options=(hash
|
||||||
|
excludeCategoryId=category.id
|
||||||
|
none=true
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -6,62 +6,50 @@
|
||||||
<p class="warning">{{i18n "category.special_warning"}}</p>
|
<p class="warning">{{i18n "category.special_warning"}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#unless category.isUncategorizedCategory}}
|
|
||||||
<ul class="permission-list">
|
{{#unless category.is_special}}
|
||||||
|
<div class="category-permissions-table">
|
||||||
|
<div class="permission-row row-header">
|
||||||
|
<span class="group-name">{{i18n "category.permissions.group"}}</span>
|
||||||
|
<span class="options">
|
||||||
|
<span class="cell">{{i18n "category.permissions.see"}}</span>
|
||||||
|
<span class="cell">{{i18n "category.permissions.reply"}}</span>
|
||||||
|
<span class="cell">{{i18n "category.permissions.create"}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{{#each category.permissions as |p|}}
|
{{#each category.permissions as |p|}}
|
||||||
<li>
|
{{category-permission-row group_name=p.group_name type=p.permission_type category=category everyonePermission=everyonePermission}}
|
||||||
<span class="name"><span class="badge-group">{{p.group_name}}</span></span>
|
|
||||||
{{html-safe (i18n "category.can")}}
|
|
||||||
<span class="permission">{{p.permission.description}}</span>
|
|
||||||
{{#if editingPermissions}}
|
|
||||||
<a class="remove-permission" href {{action "removePermission" p}}>{{d-icon "times-circle"}}</a>
|
|
||||||
{{/if}}
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
|
||||||
|
{{#unless category.permissions}}
|
||||||
|
<div class="permission-row row-empty">
|
||||||
|
{{i18n "category.permissions.no_groups_selected"}}
|
||||||
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{#if editingPermissions}}
|
|
||||||
{{#if category.availableGroups}}
|
{{#if category.availableGroups}}
|
||||||
|
<div class="add-group">
|
||||||
|
<span class="group-name">
|
||||||
{{combo-box
|
{{combo-box
|
||||||
class="available-groups"
|
class="available-groups"
|
||||||
content=category.availableGroups
|
content=category.availableGroups
|
||||||
onChange=(action "onSelectGroup")
|
onChange=(action "onSelectGroup")
|
||||||
value=selectedGroup
|
value=null
|
||||||
valueProperty=null
|
valueProperty=null
|
||||||
nameProperty=null
|
nameProperty=null
|
||||||
options=(hash
|
options=(hash
|
||||||
placementStrategy="absolute"
|
none="category.security_add_group"
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
{{combo-box
|
</span>
|
||||||
class="permission-selector"
|
|
||||||
nameProperty="description"
|
|
||||||
content=category.availablePermissions
|
|
||||||
onChange=(action "onSelectPermission")
|
|
||||||
value=selectedPermission
|
|
||||||
options=(hash
|
|
||||||
placementStrategy="absolute"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{d-button
|
|
||||||
action=(action "addPermission" selectedGroup selectedPermission)
|
|
||||||
class="btn-primary add-permission"
|
|
||||||
icon="plus"}}
|
|
||||||
{{#if showPendingGroupChangesAlert}}
|
|
||||||
<div class="pending-permission-change-alert">
|
|
||||||
<div class="arrow-div"></div>
|
|
||||||
{{i18n "category.pending_permission_change_alert" group=selectedGroup}}
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if everyoneGrantedFull}}
|
||||||
|
<p class="warning">{{i18n "category.permissions.everyone_has_access"}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
|
||||||
{{#unless category.is_special}}
|
|
||||||
{{d-button
|
|
||||||
action=(action "editPermissions")
|
|
||||||
class="btn-default edit-permission"
|
|
||||||
label="category.edit_permissions"}}
|
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/if}}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{{plugin-outlet name="category-custom-security" args=(hash category=category) connectorTagName="" tagName="section"}}
|
{{plugin-outlet name="category-custom-security" args=(hash category=category) connectorTagName="" tagName="section"}}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<section>
|
<section>
|
||||||
<h3>{{i18n "category.settings_sections.general"}}</h3>
|
|
||||||
|
|
||||||
{{#if showPositionInput}}
|
{{#if showPositionInput}}
|
||||||
<section class="field position-fields">
|
<section class="field position-fields">
|
||||||
<label for="category-position">
|
<label for="category-position">
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
<label>{{i18n "category.topic_template"}}</label>
|
|
||||||
{{d-editor value=category.topic_template showLink=showInsertLinkButton}}
|
{{d-editor value=category.topic_template showLink=showInsertLinkButton}}
|
||||||
|
|
|
@ -26,9 +26,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-category-content">
|
||||||
|
<h3>{{selectedTabTitle}}</h3>
|
||||||
|
|
||||||
{{#each panels as |tab|}}
|
{{#each panels as |tab|}}
|
||||||
{{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}}
|
{{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="edit-category-footer">
|
<div class="edit-category-footer">
|
||||||
{{d-button id="save-category" class="btn-primary" disabled=disabled action=(action "saveCategory") label=saveLabel}}
|
{{d-button id="save-category" class="btn-primary" disabled=disabled action=(action "saveCategory") label=saveLabel}}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { click, visit } from "@ember/test-helpers";
|
||||||
import { test } from "qunit";
|
import { test } from "qunit";
|
||||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import I18n from "I18n";
|
||||||
|
|
||||||
acceptance("Category Edit - security", function (needs) {
|
acceptance("Category Edit - security", function (needs) {
|
||||||
needs.user();
|
needs.user();
|
||||||
|
@ -10,13 +11,12 @@ acceptance("Category Edit - security", function (needs) {
|
||||||
test("default", async function (assert) {
|
test("default", async function (assert) {
|
||||||
await visit("/c/bug/edit/security");
|
await visit("/c/bug/edit/security");
|
||||||
|
|
||||||
const $firstItem = queryAll(".permission-list li:eq(0)");
|
const firstRow = queryAll(".row-body").first();
|
||||||
|
const badgeName = firstRow.find(".group-name-label").text();
|
||||||
const badgeName = $firstItem.find(".badge-group").text();
|
|
||||||
assert.equal(badgeName, "everyone");
|
assert.equal(badgeName, "everyone");
|
||||||
|
|
||||||
const permission = $firstItem.find(".permission").text();
|
const permission = firstRow.find(".d-icon-check-square");
|
||||||
assert.equal(permission, "Create / Reply / See");
|
assert.equal(permission.length, 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("removing a permission", async function (assert) {
|
test("removing a permission", async function (assert) {
|
||||||
|
@ -24,47 +24,42 @@ acceptance("Category Edit - security", function (needs) {
|
||||||
|
|
||||||
await visit("/c/bug/edit/security");
|
await visit("/c/bug/edit/security");
|
||||||
|
|
||||||
await click(".edit-category-tab-security .edit-permission");
|
|
||||||
await availableGroups.expand();
|
await availableGroups.expand();
|
||||||
|
|
||||||
assert.notOk(
|
assert.notOk(
|
||||||
availableGroups.rowByValue("everyone").exists(),
|
availableGroups.rowByValue("everyone").exists(),
|
||||||
"everyone is already used and is not in the available groups"
|
"everyone is already used and is not in the available groups"
|
||||||
);
|
);
|
||||||
|
|
||||||
await click(
|
await click(".row-body .remove-permission");
|
||||||
".edit-category-tab-security .permission-list li:first-of-type .remove-permission"
|
|
||||||
);
|
|
||||||
await availableGroups.expand();
|
await availableGroups.expand();
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
availableGroups.rowByValue("everyone").exists(),
|
availableGroups.rowByValue("everyone").exists(),
|
||||||
"everyone has been removed and appears in the available groups"
|
"everyone has been removed and appears in the available groups"
|
||||||
);
|
);
|
||||||
|
assert.ok(
|
||||||
|
queryAll(".row-empty").text(),
|
||||||
|
I18n.t("category.permissions.no_groups_selected"),
|
||||||
|
"shows message when no groups are selected"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("adding a permission", async function (assert) {
|
test("adding a permission", async function (assert) {
|
||||||
const availableGroups = selectKit(".available-groups");
|
const availableGroups = selectKit(".available-groups");
|
||||||
const permissionSelector = selectKit(".permission-selector");
|
|
||||||
|
|
||||||
await visit("/c/bug/edit/security");
|
await visit("/c/bug/edit/security");
|
||||||
|
|
||||||
await click(".edit-category-tab-security .edit-permission");
|
|
||||||
await availableGroups.expand();
|
await availableGroups.expand();
|
||||||
await availableGroups.selectRowByValue("staff");
|
await availableGroups.selectRowByValue("staff");
|
||||||
await permissionSelector.expand();
|
|
||||||
await permissionSelector.selectRowByValue("2");
|
|
||||||
await click(".edit-category-tab-security .add-permission");
|
|
||||||
|
|
||||||
const $addedPermissionItem = queryAll(
|
const addedRow = queryAll(".row-body").last();
|
||||||
".edit-category-tab-security .permission-list li:nth-child(2)"
|
|
||||||
|
assert.equal(addedRow.find(".group-name-label").text(), "staff");
|
||||||
|
assert.equal(
|
||||||
|
addedRow.find(".d-icon-check-square").length,
|
||||||
|
3,
|
||||||
|
"new row permissions match default 'everyone' permissions"
|
||||||
);
|
);
|
||||||
|
|
||||||
const badgeName = $addedPermissionItem.find(".badge-group").text();
|
|
||||||
assert.equal(badgeName, "staff");
|
|
||||||
|
|
||||||
const permission = $addedPermissionItem.find(".permission").text();
|
|
||||||
assert.equal(permission, "Reply / See");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("adding a previously removed permission", async function (assert) {
|
test("adding a previously removed permission", async function (assert) {
|
||||||
|
@ -72,33 +67,103 @@ acceptance("Category Edit - security", function (needs) {
|
||||||
|
|
||||||
await visit("/c/bug/edit/security");
|
await visit("/c/bug/edit/security");
|
||||||
|
|
||||||
await click(".edit-category-tab-security .edit-permission");
|
await click(".row-body .remove-permission");
|
||||||
await click(
|
|
||||||
".edit-category-tab-security .permission-list li:first-of-type .remove-permission"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
queryAll(".edit-category-tab-security .permission-list li").length,
|
queryAll(".row-body").length,
|
||||||
0,
|
0,
|
||||||
"it removes the permission from the list"
|
"removes the permission from the list"
|
||||||
);
|
);
|
||||||
|
|
||||||
await availableGroups.expand();
|
await availableGroups.expand();
|
||||||
await availableGroups.selectRowByValue("everyone");
|
await availableGroups.selectRowByValue("everyone");
|
||||||
await click(".edit-category-tab-security .add-permission");
|
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
queryAll(".edit-category-tab-security .permission-list li").length,
|
queryAll(".row-body").length,
|
||||||
1,
|
1,
|
||||||
"it adds the permission to the list"
|
"adds back the permission tp the list"
|
||||||
);
|
);
|
||||||
|
|
||||||
const $firstItem = queryAll(".permission-list li:eq(0)");
|
const firstRow = queryAll(".row-body").first();
|
||||||
|
|
||||||
const badgeName = $firstItem.find(".badge-group").text();
|
assert.equal(firstRow.find(".group-name-label").text(), "everyone");
|
||||||
assert.equal(badgeName, "everyone");
|
assert.equal(
|
||||||
|
firstRow.find(".d-icon-check-square").length,
|
||||||
|
1,
|
||||||
|
"adds only 'See' permission for a new row"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const permission = $firstItem.find(".permission").text();
|
test("editing permissions", async function (assert) {
|
||||||
assert.equal(permission, "Create / Reply / See");
|
const availableGroups = selectKit(".available-groups");
|
||||||
|
|
||||||
|
await visit("/c/bug/edit/security");
|
||||||
|
|
||||||
|
const everyoneRow = queryAll(".row-body").first();
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
everyoneRow.find(".reply-granted, .create-granted").length,
|
||||||
|
2,
|
||||||
|
"everyone has full permissions by default"
|
||||||
|
);
|
||||||
|
|
||||||
|
await availableGroups.expand();
|
||||||
|
await availableGroups.selectRowByValue("staff");
|
||||||
|
|
||||||
|
const staffRow = queryAll(".row-body").last();
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
staffRow.find(".reply-granted, .create-granted").length,
|
||||||
|
2,
|
||||||
|
"staff group also has full permissions"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(everyoneRow.find(".reply-toggle"));
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
everyoneRow.find(".reply-granted, .create-granted").length,
|
||||||
|
0,
|
||||||
|
"everyone does not have reply or create"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
staffRow.find(".reply-granted, .create-granted").length,
|
||||||
|
2,
|
||||||
|
"staff group still has full permissions"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(staffRow.find(".reply-toggle"));
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
everyoneRow.find(".reply-granted, .create-granted").length,
|
||||||
|
0,
|
||||||
|
"everyone permission unchanged"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
staffRow.find(".reply-granted").length,
|
||||||
|
0,
|
||||||
|
"staff does not have reply permission"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
staffRow.find(".create-granted").length,
|
||||||
|
0,
|
||||||
|
"staff does not have create permission"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(everyoneRow.find(".create-toggle"));
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
everyoneRow.find(".reply-granted, .create-granted").length,
|
||||||
|
2,
|
||||||
|
"everyone has full permissions"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
staffRow.find(".reply-granted, .create-granted").length,
|
||||||
|
2,
|
||||||
|
"staff group has full permissions (inherited from everyone)"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,6 +27,10 @@ div.edit-category {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-category-content {
|
||||||
|
grid-area: content;
|
||||||
|
}
|
||||||
|
|
||||||
#list-area & h2 {
|
#list-area & h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -35,16 +39,16 @@ div.edit-category {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-category-tab-general {
|
|
||||||
.category-chooser {
|
|
||||||
width: unquote("min(340px, 90%)");
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
background-color: var(--tertiary-low);
|
background-color: var(--tertiary-low);
|
||||||
padding: 0.5em 2.5em 0.5em 1em;
|
padding: 0.5em 2.5em 0.5em 1em;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-category-tab-general {
|
||||||
|
.category-chooser {
|
||||||
|
width: unquote("min(340px, 90%)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-category-tab-security {
|
.edit-category-tab-security {
|
||||||
|
@ -71,11 +75,6 @@ div.edit-category {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-permission {
|
|
||||||
position: relative;
|
|
||||||
top: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.permission-list {
|
.permission-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0 0 30px;
|
margin: 0 0 30px;
|
||||||
|
@ -147,7 +146,7 @@ div.edit-category {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-self: start;
|
align-self: start;
|
||||||
padding-bottom: 2em;
|
padding: 0 1.5em 2em 0;
|
||||||
|
|
||||||
.disable-info {
|
.disable-info {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -175,3 +174,70 @@ div.edit-category {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.category-permissions-table {
|
||||||
|
max-width: 450px;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
|
||||||
|
.permission-row {
|
||||||
|
border-bottom: 1px solid var(--primary-low);
|
||||||
|
display: flex;
|
||||||
|
&.row-header {
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 2px solid var(--primary-low);
|
||||||
|
}
|
||||||
|
.group-name,
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
width: 50%;
|
||||||
|
margin: 0px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.group-name {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5em;
|
||||||
|
padding-left: 0;
|
||||||
|
.group-name-label {
|
||||||
|
@include ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cell,
|
||||||
|
.btn-flat {
|
||||||
|
width: 33%;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
.btn-flat:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.btn-flat .d-icon-check-square,
|
||||||
|
.btn-flat:hover .d-icon-check-square {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-permission {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
padding: 0.15em;
|
||||||
|
color: var(--danger);
|
||||||
|
&:hover {
|
||||||
|
color: var(--danger-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-empty {
|
||||||
|
padding: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-empty {
|
||||||
|
color: var(--primary-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-group {
|
||||||
|
margin: 1em 0;
|
||||||
|
.group-name {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -39,24 +39,6 @@ div.edit-category {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-category-tab,
|
|
||||||
.edit-category-footer {
|
|
||||||
background-color: var(--secondary);
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.expanded-menu {
|
|
||||||
.edit-category-tab,
|
|
||||||
.edit-category-footer {
|
|
||||||
transform: translateX(45%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-stacked {
|
|
||||||
left: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-category-title {
|
.edit-category-title {
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -74,7 +56,11 @@ div.edit-category {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-category-content {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.edit-category-footer {
|
.edit-category-footer {
|
||||||
padding-bottom: 2em;
|
padding: 0 0.5em 2em 0.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2961,6 +2961,17 @@ en:
|
||||||
change_in_category_topic: "Edit Description"
|
change_in_category_topic: "Edit Description"
|
||||||
already_used: "This color has been used by another category"
|
already_used: "This color has been used by another category"
|
||||||
security: "Security"
|
security: "Security"
|
||||||
|
security_add_group: "Add a group"
|
||||||
|
permissions:
|
||||||
|
group: "Group"
|
||||||
|
see: "See"
|
||||||
|
reply: "Reply"
|
||||||
|
create: "Create"
|
||||||
|
no_groups_selected: "No groups have been granted access; this category will only be visible to staff."
|
||||||
|
everyone_has_access: "This category is public, everyone can see, reply and create posts. To restrict permissions, remove one or more of the permissions granted to the \"everyone\" group."
|
||||||
|
toggle_reply: "Toggle Reply permission"
|
||||||
|
toggle_full: "Toggle Create permission"
|
||||||
|
inherited: "This permission is inherited from \"everyone\""
|
||||||
special_warning: "Warning: This category is a pre-seeded category and the security settings cannot be edited. If you do not wish to use this category, delete it instead of repurposing it."
|
special_warning: "Warning: This category is a pre-seeded category and the security settings cannot be edited. If you do not wish to use this category, delete it instead of repurposing it."
|
||||||
uncategorized_security_warning: "This category is special. It is intended as holding area for topics that have no category; it cannot have security settings."
|
uncategorized_security_warning: "This category is special. It is intended as holding area for topics that have no category; it cannot have security settings."
|
||||||
uncategorized_general_warning: 'This category is special. It is used as the default category for new topics that do not have a category selected. If you want to prevent this behavior and force category selection, <a href="%{settingLink}">please disable the setting here</a>. If you want to change the name or description, go to <a href="%{customizeLink}">Customize / Text Content</a>.'
|
uncategorized_general_warning: 'This category is special. It is used as the default category for new topics that do not have a category selected. If you want to prevent this behavior and force category selection, <a href="%{settingLink}">please disable the setting here</a>. If you want to change the name or description, go to <a href="%{customizeLink}">Customize / Text Content</a>.'
|
||||||
|
|
Loading…
Reference in New Issue