From 7539c2ed7fe7ae991c823bc958be12fa052eb830 Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Fri, 20 Nov 2020 10:44:34 -0500 Subject: [PATCH] UX: Revamp category security tab (#11273) --- .../app/components/category-permission-row.js | 123 ++++++++++++++++ .../app/components/edit-category-security.js | 87 +++-------- .../app/components/edit-category-tab.js | 3 +- .../app/controllers/edit-category-tabs.js | 6 + .../discourse/app/models/category.js | 32 +++- .../components/category-permission-row.hbs | 29 ++++ .../components/edit-category-general.hbs | 6 +- .../components/edit-category-security.hbs | 90 +++++------- .../components/edit-category-settings.hbs | 2 - .../edit-category-topic-template.hbs | 1 - .../app/templates/edit-category-tabs.hbs | 10 +- .../acceptance/category-edit-security-test.js | 139 +++++++++++++----- .../common/base/edit-category.scss | 90 ++++++++++-- .../stylesheets/mobile/edit-category.scss | 24 +-- config/locales/client.en.yml | 11 ++ 15 files changed, 454 insertions(+), 199 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/category-permission-row.js create mode 100644 app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs diff --git a/app/assets/javascripts/discourse/app/components/category-permission-row.js b/app/assets/javascripts/discourse/app/components/category-permission-row.js new file mode 100644 index 00000000000..881f33a9bab --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/category-permission-row.js @@ -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); + } + }, + }, +}); diff --git a/app/assets/javascripts/discourse/app/components/edit-category-security.js b/app/assets/javascripts/discourse/app/components/edit-category-security.js index a85558ff8a6..cd69ba50434 100644 --- a/app/assets/javascripts/discourse/app/components/edit-category-security.js +++ b/app/assets/javascripts/discourse/app/components/edit-category-security.js @@ -1,78 +1,39 @@ import { buildCategoryPanel } from "discourse/components/edit-category-panel"; 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", { - editingPermissions: false, selectedGroup: null, - selectedPermission: null, - showPendingGroupChangesAlert: false, - interactedWithDropdowns: false, + noGroupSelected: not("selectedGroup"), - @on("init") - _setup() { - this.setProperties({ - selectedGroup: this.get("category.availableGroups.firstObject"), - selectedPermission: this.get( - "category.availablePermissions.firstObject.id" - ), - }); + @discourseComputed("category.permissions.@each.permission_type") + everyonePermission(permissions) { + return permissions.findBy("group_name", "everyone"); }, - @on("init") - _registerValidator() { - this.registerValidator(() => { - if ( - !this.showPendingGroupChangesAlert && - this.interactedWithDropdowns && - this.activeTab - ) { - this.set("showPendingGroupChangesAlert", true); - return true; - } - }); + @discourseComputed("category.permissions.@each.permission_type") + everyoneGrantedFull() { + return ( + this.everyonePermission && + this.everyonePermission.permission_type === PermissionType.FULL + ); + }, + + @discourseComputed("everyonePermission") + minimumPermission(everyonePermission) { + return everyonePermission + ? everyonePermission.permission_type + : PermissionType.READONLY; }, actions: { - onSelectGroup(selectedGroup) { - this.setProperties({ - interactedWithDropdowns: true, - selectedGroup, + onSelectGroup(group_name) { + this.category.addPermission({ + group_name, + permission_type: this.minimumPermission, }); }, - - 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({ - group_name: group + "", - permission: PermissionType.create({ id: parseInt(id, 10) }), - }); - } - - this.setProperties({ - selectedGroup: this.get("category.availableGroups.firstObject"), - showPendingGroupChangesAlert: false, - interactedWithDropdowns: false, - }); - }, - - removePermission(permission) { - if (!this.get("category.is_special")) { - this.category.removePermission(permission); - } - }, }, }); diff --git a/app/assets/javascripts/discourse/app/components/edit-category-tab.js b/app/assets/javascripts/discourse/app/components/edit-category-tab.js index eb0f52c36af..0ead526caa1 100644 --- a/app/assets/javascripts/discourse/app/components/edit-category-tab.js +++ b/app/assets/javascripts/discourse/app/components/edit-category-tab.js @@ -6,6 +6,7 @@ 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"; +import { underscore } from "@ember/string"; export default Component.extend({ tagName: "li", @@ -21,7 +22,7 @@ export default Component.extend({ @discourseComputed("tab") title(tab) { - return I18n.t("category." + tab.replace("-", "_")); + return I18n.t(`category.${underscore(tab)}`); }, didInsertElement() { diff --git a/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js b/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js index 2f38469f8e2..cff351a23a0 100644 --- a/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js +++ b/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js @@ -7,6 +7,7 @@ import DiscourseURL from "discourse/lib/url"; import { readOnly } from "@ember/object/computed"; import PermissionType from "discourse/models/permission-type"; import { NotificationLevels } from "discourse/lib/notification-levels"; +import { underscore } from "@ember/string"; export default Controller.extend({ selectedTab: "general", @@ -69,6 +70,11 @@ export default Controller.extend({ : I18n.t("category.create"); }, + @discourseComputed("selectedTab") + selectedTabTitle(tab) { + return I18n.t(`category.${underscore(tab)}`); + }, + actions: { registerValidator(validator) { this.validators.push(validator); diff --git a/app/assets/javascripts/discourse/app/models/category.js b/app/assets/javascripts/discourse/app/models/category.js index f504c9f3688..b1dbd782d2a 100644 --- a/app/assets/javascripts/discourse/app/models/category.js +++ b/app/assets/javascripts/discourse/app/models/category.js @@ -10,6 +10,8 @@ import Site from "discourse/models/site"; import User from "discourse/models/user"; import { getOwner } from "discourse-common/lib/get-owner"; +const STAFF_GROUP_NAME = "staff"; + const Category = RestModel.extend({ permissions: null, @@ -22,15 +24,13 @@ const Category = RestModel.extend({ this.set("availableGroups", availableGroups); const groupPermissions = this.group_permissions; + if (groupPermissions) { this.set( "permissions", groupPermissions.map((elem) => { availableGroups.removeObject(elem.group_name); - return { - group_name: elem.group_name, - permission: PermissionType.create({ id: elem.permission_type }), - }; + return elem; }) ); } @@ -231,7 +231,12 @@ const Category = RestModel.extend({ _permissionsForUpdate() { const permissions = this.permissions; 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; }, @@ -246,9 +251,20 @@ const Category = RestModel.extend({ this.availableGroups.removeObject(permission.group_name); }, - removePermission(permission) { - this.permissions.removeObject(permission); - this.availableGroups.addObject(permission.group_name); + removePermission(group_name) { + const permission = this.permissions.findBy("group_name", group_name); + if (permission) { + this.permissions.removeObject(permission); + 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") diff --git a/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs b/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs new file mode 100644 index 00000000000..b3ac950558c --- /dev/null +++ b/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs @@ -0,0 +1,29 @@ + + {{group_name}} + + {{d-icon "far-trash-alt"}} + + + + {{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 + }} + diff --git a/app/assets/javascripts/discourse/app/templates/components/edit-category-general.hbs b/app/assets/javascripts/discourse/app/templates/components/edit-category-general.hbs index 23e2981f000..e5ec4c44d13 100644 --- a/app/assets/javascripts/discourse/app/templates/components/edit-category-general.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/edit-category-general.hbs @@ -12,14 +12,16 @@
{{category-chooser - rootNone=true value=category.parent_category_id - excludeCategoryId=category.id categories=parentCategories allowSubCategories=true allowUncategorized=false allowRestrictedCategories=true onChange=(action (mut category.parent_category_id)) + options=(hash + excludeCategoryId=category.id + none=true + ) }}
{{/if}} diff --git a/app/assets/javascripts/discourse/app/templates/components/edit-category-security.hbs b/app/assets/javascripts/discourse/app/templates/components/edit-category-security.hbs index 91978bec1cb..41714758a17 100644 --- a/app/assets/javascripts/discourse/app/templates/components/edit-category-security.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/edit-category-security.hbs @@ -6,62 +6,50 @@

{{i18n "category.special_warning"}}

{{/if}} {{/if}} - {{#unless category.isUncategorizedCategory}} - - {{/unless}} - {{#if editingPermissions}} - {{#if category.availableGroups}} - {{combo-box - class="available-groups" - content=category.availableGroups - onChange=(action "onSelectGroup") - value=selectedGroup - valueProperty=null - nameProperty=null - options=(hash - placementStrategy="absolute" - ) - }} - {{combo-box - 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}} -
-
- {{i18n "category.pending_permission_change_alert" group=selectedGroup}} + + {{#unless category.permissions}} +
+ {{i18n "category.permissions.no_groups_selected"}} +
+ {{/unless}} + + {{#if category.availableGroups}} +
+ + {{combo-box + class="available-groups" + content=category.availableGroups + onChange=(action "onSelectGroup") + value=null + valueProperty=null + nameProperty=null + options=(hash + none="category.security_add_group" + ) + }} +
{{/if}} +
+ + {{#if everyoneGrantedFull}} +

{{i18n "category.permissions.everyone_has_access"}}

{{/if}} - {{else}} - {{#unless category.is_special}} - {{d-button - action=(action "editPermissions") - class="btn-default edit-permission" - label="category.edit_permissions"}} - {{/unless}} - {{/if}} + {{/unless}} {{plugin-outlet name="category-custom-security" args=(hash category=category) connectorTagName="" tagName="section"}} diff --git a/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs b/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs index f847963bfcf..33e59c3a3b6 100644 --- a/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/edit-category-settings.hbs @@ -1,6 +1,4 @@
-

{{i18n "category.settings_sections.general"}}

- {{#if showPositionInput}}