diff --git a/app/assets/javascripts/admin/controllers/admin-group.js.es6 b/app/assets/javascripts/admin/controllers/admin-group.js.es6
index 660fbafd3b6..71cb2c70328 100644
--- a/app/assets/javascripts/admin/controllers/admin-group.js.es6
+++ b/app/assets/javascripts/admin/controllers/admin-group.js.es6
@@ -1,5 +1,6 @@
import { popupAjaxError } from 'discourse/lib/ajax-error';
import { propertyEqual } from 'discourse/lib/computed';
+import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Controller.extend({
adminGroupsType: Ember.inject.controller(),
@@ -35,6 +36,16 @@ export default Ember.Controller.extend({
];
}.property(),
+ @computed('model.visible', 'model.public', 'model.alias_level')
+ disableMembershipRequestSetting(visible, publicGroup) {
+ return !visible || publicGroup || !this.get('model.canEveryoneMention');
+ },
+
+ @computed('model.visible', 'model.allow_membership_requests')
+ disablePublicSetting(visible, allowMembershipRequests) {
+ return !visible || allowMembershipRequests;
+ },
+
actions: {
next() {
if (this.get("showingLast")) { return; }
diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs
index 3b06790e439..f66e0ca8557 100644
--- a/app/assets/javascripts/admin/templates/group.hbs
+++ b/app/assets/javascripts/admin/templates/group.hbs
@@ -64,16 +64,29 @@
{{#unless model.automatic}}
-
- {{input type="checkbox" checked=model.public}}
- {{i18n 'groups.public'}}
+ {{input type="checkbox"
+ checked=model.allow_membership_requests
+ disabled=disableMembershipRequestSetting}}
+
+ {{i18n 'groups.allow_membership_requests'}}
+
+
+
+
+
+ {{input type="checkbox" checked=model.primary_group}}
+ {{i18n 'admin.groups.primary_group'}}
{{/unless}}
diff --git a/app/assets/javascripts/discourse/controllers/group-index.js.es6 b/app/assets/javascripts/discourse/controllers/group-index.js.es6
index 385ad7414e6..ff1b32741e5 100644
--- a/app/assets/javascripts/discourse/controllers/group-index.js.es6
+++ b/app/assets/javascripts/discourse/controllers/group-index.js.es6
@@ -22,6 +22,11 @@ export default Ember.Controller.extend({
return !!(this.currentUser) && publicGroup;
},
+ @computed('model.allow_membership_requests', 'model.alias_level')
+ canRequestMembership(allowMembershipRequests, aliasLevel) {
+ return !!(this.currentUser) && allowMembershipRequests && aliasLevel === 99;
+ },
+
actions: {
removeMember(user) {
this.get('model').removeMember(user);
@@ -34,6 +39,13 @@ export default Ember.Controller.extend({
}
},
+ requestMembership() {
+ const groupName = this.get('model.name');
+ const title = I18n.t('groups.request_membership_pm.title');
+ const body = I18n.t('groups.request_membership_pm.body', { groupName });
+ this.transitionToRoute(`/new-message?groupname=${groupName}&title=${title}&body=${body}`);
+ },
+
joinGroup() {
this.set('updatingMembership', true);
const model = this.get('model');
diff --git a/app/assets/javascripts/discourse/models/group.js.es6 b/app/assets/javascripts/discourse/models/group.js.es6
index 7d8a58cec66..c4cd97ddf00 100644
--- a/app/assets/javascripts/discourse/models/group.js.es6
+++ b/app/assets/javascripts/discourse/models/group.js.es6
@@ -1,5 +1,5 @@
import { ajax } from 'discourse/lib/ajax';
-import computed from "ember-addons/ember-computed-decorators";
+import { default as computed, observes } from "ember-addons/ember-computed-decorators";
import GroupHistory from 'discourse/models/group-history';
const Group = Discourse.Model.extend({
@@ -101,6 +101,23 @@ const Group = Discourse.Model.extend({
return this.get('flair_color') ? this.get('flair_color').replace(new RegExp("[^0-9a-fA-F]", "g"), "") : null;
},
+ @computed('alias_level')
+ canEveryoneMention(aliasLevel) {
+ return aliasLevel === '99';
+ },
+
+ @observes("visible", "canEveryoneMention")
+ _updateAllowMembershipRequests() {
+ if (!this.get('visible') || !this.get('canEveryoneMention')) {
+ this.set('allow_membership_requests', false);
+ }
+ },
+
+ @observes("visible")
+ _updatePublic() {
+ if (!this.get('visible')) this.set('public', false);
+ },
+
asJSON() {
return {
name: this.get('name'),
@@ -116,7 +133,8 @@ const Group = Discourse.Model.extend({
flair_bg_color: this.get('flairBackgroundHexColor'),
flair_color: this.get('flairHexColor'),
bio_raw: this.get('bio_raw'),
- public: this.get('public')
+ public: this.get('public'),
+ allow_membership_requests: this.get('allow_membership_requests')
};
},
diff --git a/app/assets/javascripts/discourse/templates/group-index.hbs b/app/assets/javascripts/discourse/templates/group-index.hbs
index 694750e6821..cd01f61fa4d 100644
--- a/app/assets/javascripts/discourse/templates/group-index.hbs
+++ b/app/assets/javascripts/discourse/templates/group-index.hbs
@@ -18,6 +18,11 @@
label="groups.join"
disabled=updatingMembership}}
{{/if}}
+ {{else if canRequestMembership}}
+ {{d-button action="requestMembership"
+ class="group-index-request"
+ icon="envelope"
+ label="groups.request"}}
{{/if}}
{{#load-more selector=".group-members tr" action="loadMore"}}
diff --git a/app/assets/javascripts/discourse/templates/modal/edit-group.hbs b/app/assets/javascripts/discourse/templates/modal/edit-group.hbs
index 3c65ade87ed..863f83dd3c3 100644
--- a/app/assets/javascripts/discourse/templates/modal/edit-group.hbs
+++ b/app/assets/javascripts/discourse/templates/modal/edit-group.hbs
@@ -9,9 +9,22 @@
{{group-flair-inputs model=model}}
- {{input type='checkbox' checked=model.public class="edit-group-public"}}
+ {{input type='checkbox'
+ checked=model.public
+ class="edit-group-public"
+ disabled=model.allow_membership_requests}}
+
{{i18n 'groups.public'}}
+
+
+ {{input type='checkbox'
+ checked=model.allow_membership_requests
+ class="edit-group-allow-membership-requests"
+ disabled=model.public}}
+
+ {{i18n 'groups.allow_membership_requests'}}
+
{{/d-modal-body}}
diff --git a/app/assets/stylesheets/common/base/group.scss b/app/assets/stylesheets/common/base/group.scss
index ef9ee506f60..146c5039ad2 100644
--- a/app/assets/stylesheets/common/base/group.scss
+++ b/app/assets/stylesheets/common/base/group.scss
@@ -23,6 +23,10 @@
}
}
+.group-index-request, .group-index-join, .group-index-leave {
+ float: right;
+}
+
table.group-logs {
width: 100%;
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 7f4109277e6..ed96b325100 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -145,7 +145,7 @@ class Admin::GroupsController < Admin::AdminController
:name, :alias_level, :visible, :automatic_membership_email_domains,
:automatic_membership_retroactive, :title, :primary_group,
:grant_trust_level, :incoming_email, :flair_url, :flair_bg_color,
- :flair_color, :bio_raw, :public
+ :flair_color, :bio_raw, :public, :allow_membership_requests
)
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index cfb8b686743..87137ed5937 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -237,7 +237,8 @@ class GroupsController < ApplicationController
:flair_color,
:bio_raw,
:title,
- :public
+ :public,
+ :allow_membership_requests
)
end
diff --git a/app/serializers/basic_group_serializer.rb b/app/serializers/basic_group_serializer.rb
index a4f1e4a2d0c..264013cbdaf 100644
--- a/app/serializers/basic_group_serializer.rb
+++ b/app/serializers/basic_group_serializer.rb
@@ -17,7 +17,8 @@ class BasicGroupSerializer < ApplicationSerializer
:flair_color,
:bio_raw,
:bio_cooked,
- :public
+ :public,
+ :allow_membership_requests
def include_incoming_email?
staff?
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index cb0c4bc99f4..c5c9c3443b6 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -397,8 +397,11 @@ en:
edit:
title: 'Edit Group'
group_title: 'Title'
+ request_membership_pm:
+ title: "Membership Request"
+ body: "I would like to request membership in @%{groupName}."
name_placeholder: "Group name, no spaces, same as username rule"
- public: "Allow users to join/leave the group freely"
+ public: "Allow users to join/leave the group freely (Requires group to be visible)"
empty:
posts: "There is no post by members of this group."
members: "There is no member in this group."
@@ -409,6 +412,8 @@ en:
add: "Add"
join: "Join Group"
leave: "Leave Group"
+ request: "Request to Join Group"
+ allow_membership_requests: "Allow users to send membership requests to group owners (Requires everyone to be able to mention the group)"
name: "Name"
bio: "About Group"
selector_placeholder: "Add members"
diff --git a/db/migrate/20161212123649_add_allow_membership_requests_to_groups.rb b/db/migrate/20161212123649_add_allow_membership_requests_to_groups.rb
new file mode 100644
index 00000000000..b219ed4ef8c
--- /dev/null
+++ b/db/migrate/20161212123649_add_allow_membership_requests_to_groups.rb
@@ -0,0 +1,5 @@
+class AddAllowMembershipRequestsToGroups < ActiveRecord::Migration
+ def change
+ add_column :groups, :allow_membership_requests, :boolean, default: false, null: false
+ end
+end
diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb
index a5b696932fe..f9ee82f9ee6 100644
--- a/spec/controllers/admin/groups_controller_spec.rb
+++ b/spec/controllers/admin/groups_controller_spec.rb
@@ -40,7 +40,8 @@ describe Admin::GroupsController do
"flair_color"=>nil,
"bio_raw"=>nil,
"bio_cooked"=>nil,
- "public"=>false
+ "public"=>false,
+ "allow_membership_requests"=>false
}])
end
diff --git a/spec/integration/groups_spec.rb b/spec/integration/groups_spec.rb
index e13677f9793..0a53cee2f89 100644
--- a/spec/integration/groups_spec.rb
+++ b/spec/integration/groups_spec.rb
@@ -55,9 +55,10 @@ describe "Groups" do
flair_url: 'fa-adjust',
bio_raw: 'testing',
title: 'awesome team',
- public: true
+ public: true,
+ allow_membership_requests: true
} }
- end.to change { GroupHistory.count }.by(6)
+ end.to change { GroupHistory.count }.by(7)
expect(response).to be_success
@@ -69,7 +70,8 @@ describe "Groups" do
expect(group.bio_raw).to eq('testing')
expect(group.title).to eq('awesome team')
expect(group.public).to eq(true)
- expect(GroupHistory.last.subject).to eq('public')
+ expect(group.allow_membership_requests).to eq(true)
+ expect(GroupHistory.last.subject).to eq('allow_membership_requests')
end
end
diff --git a/test/javascripts/acceptance/groups-test.js.es6 b/test/javascripts/acceptance/groups-test.js.es6
index 8ba58086aad..c3578e43d74 100644
--- a/test/javascripts/acceptance/groups-test.js.es6
+++ b/test/javascripts/acceptance/groups-test.js.es6
@@ -53,5 +53,19 @@ test("Admin Browsing Groups", () => {
ok(find('.edit-group-bio').length === 1, 'it should display group bio input');
ok(find('.edit-group-title').length === 1, 'it should display group title input');
ok(find('.edit-group-public').length === 1, 'it should display group public input');
+ ok(find('.edit-group-allow-membership-requests').length === 1, 'it should display group allow_membership_requets input');
+ });
+
+ click('.edit-group-public');
+
+ andThen(() => {
+ ok(find('.edit-group-allow-membership-requests[disabled]').length === 1, 'it should disable group allow_membership_requets input');
+ });
+
+ click('.edit-group-public');
+ click('.edit-group-allow-membership-requests');
+
+ andThen(() => {
+ ok(find('.edit-group-public[disabled]').length === 1, 'it should disable group public input');
});
});
diff --git a/test/javascripts/controllers/admin-group-test.js.es6 b/test/javascripts/controllers/admin-group-test.js.es6
new file mode 100644
index 00000000000..e80e50d6f3c
--- /dev/null
+++ b/test/javascripts/controllers/admin-group-test.js.es6
@@ -0,0 +1,33 @@
+moduleFor("controller:admin-group");
+
+test("disablePublicSetting", function() {
+ this.subject().setProperties({
+ model: { visible: false, allow_membership_requests: false }
+ });
+
+ equal(this.subject().get("disablePublicSetting"), true, "it should disable setting");
+
+ this.subject().set("model.visible", true);
+
+ equal(this.subject().get("disablePublicSetting"), false, "it should enable setting");
+
+ this.subject().set("model.allow_membership_requests", true);
+
+ equal(this.subject().get("disablePublicSetting"), true, "it should disable setting");
+});
+
+test("disableMembershipRequestSetting", function() {
+ this.subject().setProperties({
+ model: { visible: false, public: false, canEveryoneMention: true }
+ });
+
+ equal(this.subject().get("disableMembershipRequestSetting"), true, "it should disable setting");
+
+ this.subject().set("model.visible", true);
+
+ equal(this.subject().get("disableMembershipRequestSetting"), false, "it should enable setting");
+
+ this.subject().set("model.public", true);
+
+ equal(this.subject().get("disableMembershipRequestSetting"), true, "it should disalbe setting");
+});
diff --git a/test/javascripts/controllers/group-index-test.js.es6 b/test/javascripts/controllers/group-index-test.js.es6
index d3ac38a18a0..f308def5747 100644
--- a/test/javascripts/controllers/group-index-test.js.es6
+++ b/test/javascripts/controllers/group-index-test.js.es6
@@ -19,3 +19,21 @@ test("canJoinGroup", function() {
equal(this.subject().get("canJoinGroup"), false, "can't join group when not logged in");
});
+
+test('canRequestMembership', function() {
+ this.subject().setProperties({
+ model: { allow_membership_requests: false, alias_level: 0 }
+ })
+
+ equal(this.subject().get('canRequestMembership'), false);
+
+ this.subject().setProperties({
+ currentUser: currentUser(), model: { allow_membership_requests: true, alias_level: 99 }
+ });
+
+ equal(this.subject().get('canRequestMembership'), true);
+
+ this.subject().set("model.alias_level", 0);
+
+ equal(this.subject().get('canRequestMembership'), false);
+});