mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 22:43:33 +08:00
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
This commit is contained in:
@ -3,6 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators";
|
|||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { popupAutomaticMembershipAlert } from "discourse/controllers/groups-new";
|
import { popupAutomaticMembershipAlert } from "discourse/controllers/groups-new";
|
||||||
|
import DiscourseURL from "discourse/lib/url";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
saving: null,
|
saving: null,
|
||||||
@ -24,7 +25,11 @@ export default Component.extend({
|
|||||||
|
|
||||||
return group
|
return group
|
||||||
.save()
|
.save()
|
||||||
.then(() => {
|
.then(data => {
|
||||||
|
if (data.route_to) {
|
||||||
|
DiscourseURL.routeTo(data.route_to);
|
||||||
|
}
|
||||||
|
|
||||||
this.set("saved", true);
|
this.set("saved", true);
|
||||||
})
|
})
|
||||||
.catch(popupAjaxError)
|
.catch(popupAjaxError)
|
||||||
|
@ -70,5 +70,14 @@ export default Component.extend({
|
|||||||
)
|
)
|
||||||
showEmailSettings(emailIn, automatic, isAdmin) {
|
showEmailSettings(emailIn, automatic, isAdmin) {
|
||||||
return emailIn && isAdmin && !automatic;
|
return emailIn && isAdmin && !automatic;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed(
|
||||||
|
"model.isCreated",
|
||||||
|
"model.can_admin_group",
|
||||||
|
"currentUser.can_create_group"
|
||||||
|
)
|
||||||
|
canAdminGroup(isCreated, canAdmin, canCreate) {
|
||||||
|
return (!isCreated && canCreate) || (isCreated && canAdmin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -128,7 +128,7 @@ export default Controller.extend({
|
|||||||
return (
|
return (
|
||||||
this.currentUser &&
|
this.currentUser &&
|
||||||
(this.currentUser.canManageGroup(model) ||
|
(this.currentUser.canManageGroup(model) ||
|
||||||
(this.currentUser.admin && automatic))
|
(model.can_admin_group && automatic))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -833,7 +833,7 @@ const User = RestModel.extend({
|
|||||||
canManageGroup(group) {
|
canManageGroup(group) {
|
||||||
return group.get("automatic")
|
return group.get("automatic")
|
||||||
? false
|
? false
|
||||||
: this.admin || group.get("is_group_owner");
|
: group.get("can_admin_group") || group.get("is_group_owner");
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("groups.@each.title", "badges.[]")
|
@discourseComputed("groups.@each.title", "badges.[]")
|
||||||
|
@ -15,7 +15,7 @@ export default DiscourseRoute.extend({
|
|||||||
afterModel(group) {
|
afterModel(group) {
|
||||||
if (
|
if (
|
||||||
!this.currentUser ||
|
!this.currentUser ||
|
||||||
(!(this.currentUser.admin && group.get("automatic")) &&
|
(!(this.modelFor("group").can_admin_group && group.get("automatic")) &&
|
||||||
!this.currentUser.canManageGroup(group))
|
!this.currentUser.canManageGroup(group))
|
||||||
) {
|
) {
|
||||||
this.transitionTo("group.members", group);
|
this.transitionTo("group.members", group);
|
||||||
|
@ -18,7 +18,7 @@ export default DiscourseRoute.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
afterModel() {
|
afterModel() {
|
||||||
if (!(this.currentUser && this.currentUser.admin)) {
|
if (!this.get("currentUser.can_create_group")) {
|
||||||
this.transitionTo("groups");
|
this.transitionTo("groups");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{{#if currentUser.admin}}
|
{{#if canAdminGroup}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">{{i18n "admin.groups.manage.interaction.visibility"}}</label>
|
<label class="control-label">{{i18n "admin.groups.manage.interaction.visibility"}}</label>
|
||||||
<label for="visiblity">{{i18n "admin.groups.manage.interaction.visibility_levels.title"}}</label>
|
<label for="visiblity">{{i18n "admin.groups.manage.interaction.visibility_levels.title"}}</label>
|
||||||
@ -62,7 +62,7 @@
|
|||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if currentUser.admin}}
|
{{#if canAdminGroup}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>
|
<label>
|
||||||
{{input type="checkbox"
|
{{input type="checkbox"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{{#if currentUser.admin}}
|
{{#if model.can_admin_group}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">{{i18n "admin.groups.manage.membership.automatic"}}</label>
|
<label class="control-label">{{i18n "admin.groups.manage.membership.automatic"}}</label>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{#if canEdit}}
|
{{#if canEdit}}
|
||||||
{{#if this.currentUser.admin}}
|
{{#if this.currentUser.can_create_group}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label" for="name">{{i18n "groups.name"}}</label>
|
<label class="control-label" for="name">{{i18n "groups.name"}}</label>
|
||||||
|
|
||||||
@ -20,7 +20,7 @@
|
|||||||
value=model.full_name}}
|
value=model.full_name}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.currentUser.admin}}
|
{{#if this.currentUser.can_create_group}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label" for="title">
|
<label class="control-label" for="title">
|
||||||
{{i18n "admin.groups.default_title"}}
|
{{i18n "admin.groups.default_title"}}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{{#d-section pageClass="groups"}}
|
{{#d-section pageClass="groups"}}
|
||||||
<div class="groups-header">
|
<div class="groups-header">
|
||||||
{{#if currentUser.admin}}
|
{{#if currentUser.can_create_group}}
|
||||||
{{d-button
|
{{d-button
|
||||||
action=(action "new")
|
action=(action "new")
|
||||||
class="btn-default groups-header-new pull-right"
|
class="btn-default groups-header-new pull-right"
|
||||||
|
@ -37,6 +37,8 @@ class Admin::GroupsController < Admin::AdminController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
guardian.ensure_can_create_group!
|
||||||
|
|
||||||
attributes = group_params.to_h.except(:owner_usernames, :usernames)
|
attributes = group_params.to_h.except(:owner_usernames, :usernames)
|
||||||
group = Group.new(attributes)
|
group = Group.new(attributes)
|
||||||
|
|
||||||
|
@ -140,11 +140,17 @@ class GroupsController < ApplicationController
|
|||||||
|
|
||||||
def update
|
def update
|
||||||
group = Group.find(params[:id])
|
group = Group.find(params[:id])
|
||||||
guardian.ensure_can_edit!(group) unless current_user.admin
|
guardian.ensure_can_edit!(group) unless guardian.can_admin_group?(group)
|
||||||
|
|
||||||
if group.update(group_params(automatic: group.automatic))
|
if group.update(group_params(automatic: group.automatic))
|
||||||
GroupActionLogger.new(current_user, group).log_change_group_settings
|
GroupActionLogger.new(current_user, group, skip_guardian: true).log_change_group_settings
|
||||||
|
|
||||||
|
if guardian.can_see?(group)
|
||||||
render json: success_json
|
render json: success_json
|
||||||
|
else
|
||||||
|
# They can no longer see the group after changing permissions
|
||||||
|
render json: { route_to: '/g' }
|
||||||
|
end
|
||||||
else
|
else
|
||||||
render_json_error(group)
|
render_json_error(group)
|
||||||
end
|
end
|
||||||
@ -511,7 +517,7 @@ class GroupsController < ApplicationController
|
|||||||
|
|
||||||
def histories
|
def histories
|
||||||
group = find_group(:group_id)
|
group = find_group(:group_id)
|
||||||
guardian.ensure_can_edit!(group) unless current_user.admin
|
guardian.ensure_can_edit!(group) unless guardian.can_admin_group?(group)
|
||||||
|
|
||||||
page_size = 25
|
page_size = 25
|
||||||
offset = (params[:offset] && params[:offset].to_i) || 0
|
offset = (params[:offset] && params[:offset].to_i) || 0
|
||||||
@ -582,7 +588,7 @@ class GroupsController < ApplicationController
|
|||||||
membership_request_template
|
membership_request_template
|
||||||
}
|
}
|
||||||
|
|
||||||
if current_user.admin
|
if current_user.staff?
|
||||||
default_params.push(*[
|
default_params.push(*[
|
||||||
:incoming_email,
|
:incoming_email,
|
||||||
:smtp_server,
|
:smtp_server,
|
||||||
|
@ -31,6 +31,7 @@ class BasicGroupSerializer < ApplicationSerializer
|
|||||||
:is_group_owner,
|
:is_group_owner,
|
||||||
:members_visibility_level,
|
:members_visibility_level,
|
||||||
:can_see_members,
|
:can_see_members,
|
||||||
|
:can_admin_group,
|
||||||
:publish_read_state
|
:publish_read_state
|
||||||
|
|
||||||
def self.admin_attributes(*attrs)
|
def self.admin_attributes(*attrs)
|
||||||
@ -115,6 +116,14 @@ class BasicGroupSerializer < ApplicationSerializer
|
|||||||
owner_group_ids.present?
|
owner_group_ids.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_admin_group
|
||||||
|
scope.can_admin_group?(object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_can_admin_group?
|
||||||
|
scope.can_admin_group?(object)
|
||||||
|
end
|
||||||
|
|
||||||
def is_group_owner
|
def is_group_owner
|
||||||
owner_group_ids.include?(object.id)
|
owner_group_ids.include?(object.id)
|
||||||
end
|
end
|
||||||
|
@ -38,6 +38,7 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||||||
:seen_notification_id,
|
:seen_notification_id,
|
||||||
:primary_group_id,
|
:primary_group_id,
|
||||||
:can_create_topic,
|
:can_create_topic,
|
||||||
|
:can_create_group,
|
||||||
:link_posting_access,
|
:link_posting_access,
|
||||||
:external_id,
|
:external_id,
|
||||||
:top_category_ids,
|
:top_category_ids,
|
||||||
@ -62,6 +63,14 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||||||
scope.can_create_topic?(nil)
|
scope.can_create_topic?(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_create_group
|
||||||
|
scope.can_create_group?
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_can_create_group?
|
||||||
|
scope.can_create_group?
|
||||||
|
end
|
||||||
|
|
||||||
def read_faq
|
def read_faq
|
||||||
object.user_stat.read_faq?
|
object.user_stat.read_faq?
|
||||||
end
|
end
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
class GroupActionLogger
|
class GroupActionLogger
|
||||||
|
|
||||||
def initialize(acting_user, group)
|
def initialize(acting_user, group, opts = {})
|
||||||
@acting_user = acting_user
|
@acting_user = acting_user
|
||||||
@group = group
|
@group = group
|
||||||
|
@opts = opts
|
||||||
end
|
end
|
||||||
|
|
||||||
def log_make_user_group_owner(target_user)
|
def log_make_user_group_owner(target_user)
|
||||||
@ -44,7 +45,7 @@ class GroupActionLogger
|
|||||||
end
|
end
|
||||||
|
|
||||||
def log_change_group_settings
|
def log_change_group_settings
|
||||||
can_edit?
|
@opts[:skip_guardian] || can_edit?
|
||||||
|
|
||||||
@group.previous_changes.except(*excluded_attributes).each do |attribute_name, value|
|
@group.previous_changes.except(*excluded_attributes).each do |attribute_name, value|
|
||||||
next if value[0].blank? && value[1].blank?
|
next if value[0].blank? && value[1].blank?
|
||||||
|
@ -1547,7 +1547,7 @@ en:
|
|||||||
ga_universal_auto_link_domains: "Enable Google Universal Analytics (analytics.js) cross-domain tracking. Outgoing links to these domains will have the client id added to them. See <a href='https://support.google.com/analytics/answer/1034342?hl=en' target='_blank'>Google's Cross-Domain Tracking guide.</a>"
|
ga_universal_auto_link_domains: "Enable Google Universal Analytics (analytics.js) cross-domain tracking. Outgoing links to these domains will have the client id added to them. See <a href='https://support.google.com/analytics/answer/1034342?hl=en' target='_blank'>Google's Cross-Domain Tracking guide.</a>"
|
||||||
gtm_container_id: "Google Tag Manager container id. eg: GTM-ABCDEF. <br/>Note: Third-party scripts loaded by GTM may need to be allowlisted in 'content security policy script src'."
|
gtm_container_id: "Google Tag Manager container id. eg: GTM-ABCDEF. <br/>Note: Third-party scripts loaded by GTM may need to be allowlisted in 'content security policy script src'."
|
||||||
enable_escaped_fragments: "Fall back to Google's Ajax-Crawling API if no webcrawler is detected. See <a href='https://developers.google.com/webmasters/ajax-crawling/docs/learn-more' target='_blank'>https://developers.google.com/webmasters/ajax-crawling/docs/learn-more</a>"
|
enable_escaped_fragments: "Fall back to Google's Ajax-Crawling API if no webcrawler is detected. See <a href='https://developers.google.com/webmasters/ajax-crawling/docs/learn-more' target='_blank'>https://developers.google.com/webmasters/ajax-crawling/docs/learn-more</a>"
|
||||||
moderators_create_categories: "Allow moderators to create new categories"
|
moderators_manage_categories_and_groups: "Allow moderators to manage categories and groups"
|
||||||
cors_origins: "Allowed origins for cross-origin requests (CORS). Each origin must include http:// or https://. The DISCOURSE_ENABLE_CORS env variable must be set to true to enable CORS."
|
cors_origins: "Allowed origins for cross-origin requests (CORS). Each origin must include http:// or https://. The DISCOURSE_ENABLE_CORS env variable must be set to true to enable CORS."
|
||||||
use_admin_ip_allowlist: "Admins can only log in if they are at an IP address defined in the Screened IPs list (Admin > Logs > Screened Ips)."
|
use_admin_ip_allowlist: "Admins can only log in if they are at an IP address defined in the Screened IPs list (Admin > Logs > Screened Ips)."
|
||||||
blocked_ip_blocks: "A list of private IP blocks that should never be crawled by Discourse"
|
blocked_ip_blocks: "A list of private IP blocks that should never be crawled by Discourse"
|
||||||
|
@ -92,7 +92,8 @@ Discourse::Application.routes.draw do
|
|||||||
get "reports/bulk" => "reports#bulk"
|
get "reports/bulk" => "reports#bulk"
|
||||||
get "reports/:type" => "reports#show"
|
get "reports/:type" => "reports#show"
|
||||||
|
|
||||||
resources :groups, constraints: AdminConstraint.new do
|
resources :groups, only: [:create]
|
||||||
|
resources :groups, except: [:create], constraints: AdminConstraint.new do
|
||||||
collection do
|
collection do
|
||||||
get 'bulk'
|
get 'bulk'
|
||||||
get 'bulk-complete' => 'groups#bulk'
|
get 'bulk-complete' => 'groups#bulk'
|
||||||
@ -560,7 +561,7 @@ Discourse::Application.routes.draw do
|
|||||||
|
|
||||||
collection do
|
collection do
|
||||||
get "check-name" => 'groups#check_name'
|
get "check-name" => 'groups#check_name'
|
||||||
get 'custom/new' => 'groups#new', constraints: AdminConstraint.new
|
get 'custom/new' => 'groups#new', constraints: StaffConstraint.new
|
||||||
get "search" => "groups#search"
|
get "search" => "groups#search"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1452,7 +1452,7 @@ security:
|
|||||||
regex: "^(Lax|Strict|Disabled|None)$"
|
regex: "^(Lax|Strict|Disabled|None)$"
|
||||||
enable_escaped_fragments: true
|
enable_escaped_fragments: true
|
||||||
allow_index_in_robots_txt: true
|
allow_index_in_robots_txt: true
|
||||||
moderators_create_categories: false
|
moderators_manage_categories_and_groups: false
|
||||||
moderators_view_emails:
|
moderators_view_emails:
|
||||||
client: true
|
client: true
|
||||||
default: false
|
default: false
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RenameModeratorsCreateCategoriesSetting < ActiveRecord::Migration[6.0]
|
||||||
|
def up
|
||||||
|
execute "UPDATE site_settings SET name = 'moderators_manage_categories_and_groups' WHERE name = 'moderators_create_categories'"
|
||||||
|
execute "UPDATE user_histories SET subject = 'moderators_manage_categories_and_groups' WHERE subject = 'moderators_create_categories'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
execute "UPDATE site_settings SET name = 'moderators_create_categories' WHERE name = 'moderators_manage_categories_and_groups'"
|
||||||
|
execute "UPDATE user_histories SET subject = 'moderators_create_categories' WHERE subject = 'moderators_manage_categories_and_groups'"
|
||||||
|
end
|
||||||
|
end
|
@ -7,7 +7,7 @@ module CategoryGuardian
|
|||||||
def can_create_category?(parent = nil)
|
def can_create_category?(parent = nil)
|
||||||
is_admin? ||
|
is_admin? ||
|
||||||
(
|
(
|
||||||
SiteSetting.moderators_create_categories &&
|
SiteSetting.moderators_manage_categories_and_groups &&
|
||||||
is_moderator?
|
is_moderator?
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
@ -16,7 +16,7 @@ module CategoryGuardian
|
|||||||
def can_edit_category?(category)
|
def can_edit_category?(category)
|
||||||
is_admin? ||
|
is_admin? ||
|
||||||
(
|
(
|
||||||
SiteSetting.moderators_create_categories &&
|
SiteSetting.moderators_manage_categories_and_groups &&
|
||||||
is_moderator? &&
|
is_moderator? &&
|
||||||
can_see_category?(category)
|
can_see_category?(category)
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,15 @@
|
|||||||
#mixin for all guardian methods dealing with group permissions
|
#mixin for all guardian methods dealing with group permissions
|
||||||
module GroupGuardian
|
module GroupGuardian
|
||||||
|
|
||||||
|
# Creating Method
|
||||||
|
def can_create_group?
|
||||||
|
is_admin? ||
|
||||||
|
(
|
||||||
|
SiteSetting.moderators_manage_categories_and_groups &&
|
||||||
|
is_moderator?
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
# Edit authority for groups means membership changes only.
|
# Edit authority for groups means membership changes only.
|
||||||
# Automatic groups are not represented in the GROUP_USERS
|
# Automatic groups are not represented in the GROUP_USERS
|
||||||
# table and thus do not allow membership changes.
|
# table and thus do not allow membership changes.
|
||||||
@ -11,7 +20,17 @@ module GroupGuardian
|
|||||||
end
|
end
|
||||||
|
|
||||||
def can_log_group_changes?(group)
|
def can_log_group_changes?(group)
|
||||||
(is_admin? || group.users.where('group_users.owner').include?(user))
|
can_admin_group?(group) || group.users.where('group_users.owner').include?(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_admin_group?(group)
|
||||||
|
is_admin? ||
|
||||||
|
(
|
||||||
|
SiteSetting.moderators_manage_categories_and_groups &&
|
||||||
|
is_moderator? &&
|
||||||
|
can_see?(group) &&
|
||||||
|
group.id != Group::AUTO_GROUPS[:admins]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_see_group_messages?(group)
|
def can_see_group_messages?(group)
|
||||||
|
@ -9,7 +9,8 @@ module SiteSettings::DeprecatedSettings
|
|||||||
['disable_edit_notifications', 'disable_system_edit_notifications', true, '2.4'],
|
['disable_edit_notifications', 'disable_system_edit_notifications', true, '2.4'],
|
||||||
['enable_category_group_review', 'enable_category_group_moderation', true, '2.7'],
|
['enable_category_group_review', 'enable_category_group_moderation', true, '2.7'],
|
||||||
['newuser_max_images', 'newuser_max_embedded_media', true, '2.7'],
|
['newuser_max_images', 'newuser_max_embedded_media', true, '2.7'],
|
||||||
['min_trust_to_post_images', 'min_trust_to_post_embedded_media', true, '2.7']
|
['min_trust_to_post_images', 'min_trust_to_post_embedded_media', true, '2.7'],
|
||||||
|
['moderators_create_categories', 'moderators_manage_categories_and_groups', '2.7']
|
||||||
]
|
]
|
||||||
|
|
||||||
def setup_deprecated_methods
|
def setup_deprecated_methods
|
||||||
|
@ -829,6 +829,99 @@ describe GroupsController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when user is a site moderator" do
|
||||||
|
before do
|
||||||
|
SiteSetting.moderators_manage_categories_and_groups = true
|
||||||
|
sign_in(moderator)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not be able to update the group if the SiteSetting is false' do
|
||||||
|
SiteSetting.moderators_manage_categories_and_groups = false
|
||||||
|
|
||||||
|
put "/groups/#{group.id}.json", params: { group: { name: 'testing' } }
|
||||||
|
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not be able to update a group it cannot see' do
|
||||||
|
group.update!(visibility_level: 2)
|
||||||
|
|
||||||
|
put "/groups/#{group.id}.json", params: { group: { name: 'testing' } }
|
||||||
|
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be able to update the group' do
|
||||||
|
put "/groups/#{group.id}.json", params: {
|
||||||
|
group: {
|
||||||
|
flair_color: 'BBB',
|
||||||
|
name: 'testing',
|
||||||
|
incoming_email: 'test@mail.org',
|
||||||
|
primary_group: true,
|
||||||
|
automatic_membership_email_domains: 'test.org',
|
||||||
|
grant_trust_level: 2,
|
||||||
|
visibility_level: 1,
|
||||||
|
members_visibility_level: 3,
|
||||||
|
tracking_category_ids: [category.id],
|
||||||
|
tracking_tags: [tag.name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
group.reload
|
||||||
|
expect(group.flair_color).to eq('BBB')
|
||||||
|
expect(group.name).to eq('testing')
|
||||||
|
expect(group.incoming_email).to eq("test@mail.org")
|
||||||
|
expect(group.primary_group).to eq(true)
|
||||||
|
expect(group.visibility_level).to eq(1)
|
||||||
|
expect(group.members_visibility_level).to eq(3)
|
||||||
|
expect(group.automatic_membership_email_domains).to eq('test.org')
|
||||||
|
expect(group.grant_trust_level).to eq(2)
|
||||||
|
expect(group.group_category_notification_defaults.first&.category).to eq(category)
|
||||||
|
expect(group.group_tag_notification_defaults.first&.tag).to eq(tag)
|
||||||
|
|
||||||
|
expect(Jobs::AutomaticGroupMembership.jobs.first["args"].first["group_id"])
|
||||||
|
.to eq(group.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to update an automatic group" do
|
||||||
|
group = Group.find(Group::AUTO_GROUPS[:trust_level_4])
|
||||||
|
|
||||||
|
group.update!(
|
||||||
|
mentionable_level: 2,
|
||||||
|
messageable_level: 2,
|
||||||
|
default_notification_level: 2
|
||||||
|
)
|
||||||
|
|
||||||
|
put "/groups/#{group.id}.json", params: {
|
||||||
|
group: {
|
||||||
|
mentionable_level: 1,
|
||||||
|
messageable_level: 1,
|
||||||
|
default_notification_level: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
group.reload
|
||||||
|
expect(group.flair_color).to eq(nil)
|
||||||
|
expect(group.name).to eq('trust_level_4')
|
||||||
|
expect(group.mentionable_level).to eq(1)
|
||||||
|
expect(group.messageable_level).to eq(1)
|
||||||
|
expect(group.default_notification_level).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'triggers a extensibility event' do
|
||||||
|
event = DiscourseEvent.track_events {
|
||||||
|
put "/groups/#{group.id}.json", params: { group: { flair_color: 'BBB' } }
|
||||||
|
}.last
|
||||||
|
|
||||||
|
expect(event[:event_name]).to eq(:group_updated)
|
||||||
|
expect(event[:params].first).to eq(group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "when user is not a group owner or admin" do
|
context "when user is not a group owner or admin" do
|
||||||
it 'should not be able to update the group' do
|
it 'should not be able to update the group' do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
@ -8,7 +8,13 @@ acceptance("Managing Group Interaction Settings", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("As an admin", async assert => {
|
QUnit.test("As an admin", async assert => {
|
||||||
await visit("/g/discourse/manage/interaction");
|
updateCurrentUser({
|
||||||
|
moderator: false,
|
||||||
|
admin: true,
|
||||||
|
can_create_group: true
|
||||||
|
});
|
||||||
|
|
||||||
|
await visit("/g/alternative-group/manage/interaction");
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
find(".groups-form-visibility-level").length,
|
find(".groups-form-visibility-level").length,
|
||||||
@ -42,13 +48,18 @@ QUnit.test("As an admin", async assert => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("As a group owner", async assert => {
|
QUnit.test("As a group owner", async assert => {
|
||||||
updateCurrentUser({ moderator: false, admin: false });
|
updateCurrentUser({
|
||||||
|
moderator: false,
|
||||||
|
admin: false,
|
||||||
|
can_create_group: false
|
||||||
|
});
|
||||||
|
|
||||||
await visit("/g/discourse/manage/interaction");
|
await visit("/g/discourse/manage/interaction");
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
find(".groups-form-visibility-level").length,
|
find(".groups-form-visibility-level").length,
|
||||||
0,
|
0,
|
||||||
"it should display visibility level selector"
|
"it should not display visibility level selector"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
@ -6,7 +6,9 @@ acceptance("Managing Group Membership", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("As an admin", async assert => {
|
QUnit.test("As an admin", async assert => {
|
||||||
await visit("/g/discourse/manage/membership");
|
updateCurrentUser({ can_create_group: true });
|
||||||
|
|
||||||
|
await visit("/g/alternative-group/manage/membership");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
find('label[for="automatic_membership"]').length === 1,
|
find('label[for="automatic_membership"]').length === 1,
|
||||||
|
@ -34,7 +34,11 @@ QUnit.test("As an admin", async assert => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("As a group owner", async assert => {
|
QUnit.test("As a group owner", async assert => {
|
||||||
updateCurrentUser({ moderator: false, admin: false });
|
updateCurrentUser({
|
||||||
|
moderator: false,
|
||||||
|
admin: false,
|
||||||
|
can_create_group: false
|
||||||
|
});
|
||||||
|
|
||||||
await visit("/g/discourse/manage/profile");
|
await visit("/g/discourse/manage/profile");
|
||||||
|
|
||||||
|
@ -1273,5 +1273,27 @@ export default {
|
|||||||
"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"
|
"/user_avatar/meta.discourse.org/codinghorror/{size}/5297.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"/groups/alternative-group.json": {
|
||||||
|
group: {
|
||||||
|
id: 57,
|
||||||
|
automatic: false,
|
||||||
|
name: "alternative-group",
|
||||||
|
full_name: "Moderatable Table",
|
||||||
|
user_count: 8,
|
||||||
|
alias_level: 99,
|
||||||
|
visible: true,
|
||||||
|
public_admission: true,
|
||||||
|
public_exit: false,
|
||||||
|
flair_url: "fa-adjust",
|
||||||
|
is_group_owner: true,
|
||||||
|
mentionable: true,
|
||||||
|
messageable: true,
|
||||||
|
can_see_members: true,
|
||||||
|
can_admin_group: true,
|
||||||
|
},
|
||||||
|
extras: {
|
||||||
|
visible_group_names: ["alternative-group"]
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -14,6 +14,7 @@ export default {
|
|||||||
site_flagged_posts_count: 1,
|
site_flagged_posts_count: 1,
|
||||||
moderator: true,
|
moderator: true,
|
||||||
staff: true,
|
staff: true,
|
||||||
|
can_create_group: true,
|
||||||
title: "co-founder",
|
title: "co-founder",
|
||||||
reply_count: 859,
|
reply_count: 859,
|
||||||
topic_count: 36,
|
topic_count: 36,
|
||||||
|
@ -52,6 +52,7 @@ QUnit.test("canMangeGroup", assert => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
group.set("automatic", false);
|
group.set("automatic", false);
|
||||||
|
group.setProperties({ can_admin_group: true });
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
user.canManageGroup(group),
|
user.canManageGroup(group),
|
||||||
|
Reference in New Issue
Block a user