FEATURE - Moderators can create and manage groups (#10432)

FEATURE - Moderators can create and manage groups (#10432)

Enabling the moderators_manage_categories_and_groups site setting will allow moderator users to create/manage groups.

  • show New Group form to moderators

  • Allow moderators to update groups and read logs, where appropriate

  • Rename site setting from create -> manage

  • improved tests

  • Migration should rename old log entries

  • Log group changes, even if those changes mean you can no longer see the group

  • Slight reshuffle

  • RouteTo /g if they no longer have permissions to view group

diff --git a/app/assets/javascripts/discourse/app/components/group-manage-save-button.js b/app/assets/javascripts/discourse/app/components/group-manage-save-button.js
index 91a6adb..9e385d6 100644
--- a/app/assets/javascripts/discourse/app/components/group-manage-save-button.js
+++ b/app/assets/javascripts/discourse/app/components/group-manage-save-button.js
@@ -3,6 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators";
 import Component from "@ember/component";
 import { popupAjaxError } from "discourse/lib/ajax-error";
 import { popupAutomaticMembershipAlert } from "discourse/controllers/groups-new";
+import DiscourseURL from "discourse/lib/url";
 
 export default Component.extend({
   saving: null,
@@ -24,7 +25,11 @@ export default Component.extend({
 
       return group
         .save()
-        .then(() => {
+        .then(data => {
+          if (data.route_to) {
+            DiscourseURL.routeTo(data.route_to);
+          }
+
           this.set("saved", true);
         })
         .catch(popupAjaxError)
diff --git a/app/assets/javascripts/discourse/app/components/groups-form-interaction-fields.js b/app/assets/javascripts/discourse/app/components/groups-form-interaction-fields.js
index d241176..bd5689a 100644
--- a/app/assets/javascripts/discourse/app/components/groups-form-interaction-fields.js
+++ b/app/assets/javascripts/discourse/app/components/groups-form-interaction-fields.js
@@ -70,5 +70,14 @@ export default Component.extend({
   )
   showEmailSettings(emailIn, automatic, isAdmin) {
     return emailIn && isAdmin && !automatic;
+  },
+
+  @discourseComputed(
+    "model.isCreated",
+    "model.can_admin_group",
+    "currentUser.can_create_group"
+  )
+  canAdminGroup(isCreated, canAdmin, canCreate) {
+    return (!isCreated && canCreate) || (isCreated && canAdmin);
   }
 });
diff --git a/app/assets/javascripts/discourse/app/controllers/group.js b/app/assets/javascripts/discourse/app/controllers/group.js
index e4b62a0..7073fcf 100644
--- a/app/assets/javascripts/discourse/app/controllers/group.js
+++ b/app/assets/javascripts/discourse/app/controllers/group.js
@@ -128,7 +128,7 @@ export default Controller.extend({
     return (
       this.currentUser &&
       (this.currentUser.canManageGroup(model) ||
-        (this.currentUser.admin && automatic))
+        (model.can_admin_group && automatic))
     );
   },
 
diff --git a/app/assets/javascripts/discourse/app/models/user.js b/app/assets/javascripts/discourse/app/models/user.js
index a30d248..2267727 100644
--- a/app/assets/javascripts/discourse/app/models/user.js
+++ b/app/assets/javascripts/discourse/app/models/user.js
@@ -833,7 +833,7 @@ const User = RestModel.extend({
   canManageGroup(group) {
     return group.get("automatic")
       ? false
-      : this.admin || group.get("is_group_owner");
+      : group.get("can_admin_group") || group.get("is_group_owner");
   },
 
   @discourseComputed("groups.@each.title", "badges.[]")
diff --git a/app/assets/javascripts/discourse/app/routes/group-manage.js b/app/assets/javascripts/discourse/app/routes/group-manage.js
index ec052e0..2748ebe 100644
--- a/app/assets/javascripts/discourse/app/routes/group-manage.js
+++ b/app/assets/javascripts/discourse/app/routes/group-manage.js
@@ -15,7 +15,7 @@ export default DiscourseRoute.extend({
   afterModel(group) {
     if (
       !this.currentUser ||
-      (!(this.currentUser.admin && group.get("automatic")) &&
+      (!(this.modelFor("group").can_admin_group && group.get("automatic")) &&
         !this.currentUser.canManageGroup(group))
     ) {
       this.transitionTo("group.members", group);
diff --git a/app/assets/javascripts/discourse/app/routes/groups-new.js b/app/assets/javascripts/discourse/app/routes/groups-new.js
index 9da8dca..95fb707 100644
--- a/app/assets/javascripts/discourse/app/routes/groups-new.js
+++ b/app/assets/javascripts/discourse/app/routes/groups-new.js
@@ -18,7 +18,7 @@ export default DiscourseRoute.extend({
   },
 
   afterModel() {
-    if (!(this.currentUser && this.currentUser.admin)) {
+    if (!this.get("currentUser.can_create_group")) {
       this.transitionTo("groups");
     }
   }
diff --git a/app/assets/javascripts/discourse/app/templates/components/groups-form-interaction-fields.hbs b/app/assets/javascripts/discourse/app/templates/components/groups-form-interaction-fields.hbs
index cad46c6..80d0b14 100644
--- a/app/assets/javascripts/discourse/app/templates/components/groups-form-interaction-fields.hbs
+++ b/app/assets/javascripts/discourse/app/templates/components/groups-form-interaction-fields.hbs
@@ -1,4 +1,4 @@
-{{#if currentUser.admin}}
+{{#if canAdminGroup}}
   <div class="control-group">
     <label class="control-label">{{i18n "admin.groups.manage.interaction.visibility"}}</label>
     <label for="visiblity">{{i18n "admin.groups.manage.interaction.visibility_levels.title"}}</label>
@@ -62,7 +62,7 @@
   }}
 </div>
 
-{{#if currentUser.admin}}
+{{#if canAdminGroup}}
   <div class="control-group">
     <label>
       {{input type="checkbox"
diff --git a/app/assets/javascripts/discourse/app/templates/components/groups-form-membership-fields.hbs b/app/assets/javascripts/discourse/app/templates/components/groups-form-membership-fields.hbs
index 2b4fdfa..03a6813 100644
--- a/app/assets/javascripts/discourse/app/templates/components/groups-form-membership-fields.hbs
+++ b/app/assets/javascripts/discourse/app/templates/components/groups-form-membership-fields.hbs
@@ -1,4 +1,4 @@
-{{#if currentUser.admin}}
+{{#if model.can_admin_group}}
   <div class="control-group">
     <label class="control-label">{{i18n "admin.groups.manage.membership.automatic"}}</label>
 
diff --git a/app/assets/javascripts/discourse/app/templates/components/groups-form-profile-fields.hbs b/app/assets/javascripts/discourse/app/templates/components/groups-form-profile-fields.hbs
index 1523077..d9590bb 100644
--- a/app/assets/javascripts/discourse/app/templates/components/groups-form-profile-fields.hbs
+++ b/app/assets/javascripts/discourse/app/templates/components/groups-form-profile-fields.hbs
@@ -1,5 +1,5 @@
 {{#if canEdit}}
-  {{#if this.currentUser.admin}}
+  {{#if this.currentUser.can_create_group}}
     <div class="control-group">
       <label class="control-label" for="name">{{i18n "groups.name"}}</label>
 
@@ -20,7 +20,7 @@
         value=model.full_name}}
   </div>
 
-  {{#if this.currentUser.admin}}
+  {{#if this.currentUser.can_create_group}}
     <div class="control-group">
       <label class="control-label" for="title">
         {{i18n "admin.groups.default_title"}}
diff --git a/app/assets/javascripts/discourse/app/templates/groups/index.hbs b/app/assets/javascripts/discourse/app/templates/groups/index.hbs
index c0befd5..ede51ea 100644
--- a/app/assets/javascripts/discourse/app/templates/groups/index.hbs
+++ b/app/assets/javascripts/discourse/app/templates/groups/index.hbs
@@ -1,6 +1,6 @@
 {{#d-section pageClass="groups"}}
   <div class="groups-header">
-    {{#if currentUser.admin}}
+    {{#if currentUser.can_create_group}}
       {{d-button
         action=(action "new")
         class="btn-default groups-header-new pull-right"
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 1eabd40..fa44fc7 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -37,6 +37,8 @@ class Admin::GroupsController < Admin::AdminController
   end
 
   def create
+    guardian.ensure_can_create_group!
+
     attributes = group_params.to_h.except(:owner_usernames, :usernames)
     group = Group.new(attributes)
 
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index c473ce6..dea5cbb 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -140,11 +140,17 @@ class GroupsController < ApplicationController
 
   def update
     group = Group.find(params[:id])

[... diff too long, it was truncated ...]

GitHub sha: aa1fc013

This commit appears in #10432 which was approved by eviltrout. It was merged by jbrw.

This commit has been mentioned on Discourse Meta. There might be relevant details there:

https://meta.discourse.org/t/allow-moderators-to-create-groups/41408/49