Add optional waiting period for users to create streams.

This adds support for only allowing normal users with account age
equal or greater than a "waiting period" threshold to create streams;
this is useful for open organizations that want new members to
understand the community before creating streams.

If create_stream_by_admins_only setting is set to True, only admin users
were able to create streams. Now normal users with account age greater
or equal than waiting period threshold can also create streams.

Account age is defined as number of days passed since the user had
created his account.

Fixes: #2308.

Tweaked by tabbott to clean up the actual can_create_streams logic and
the tests.
This commit is contained in:
vaibhav 2016-11-29 13:27:35 +05:30 committed by Tim Abbott
parent e04fe084b7
commit 75bf501553
10 changed files with 102 additions and 13 deletions

View File

@ -274,7 +274,8 @@ function _setup_page() {
realm_message_content_edit_limit_minutes:
Math.ceil(page_params.realm_message_content_edit_limit_seconds / 60),
language_list: page_params.language_list,
realm_default_language: page_params.realm_default_language
realm_default_language: page_params.realm_default_language,
realm_waiting_period_threshold: page_params.realm_waiting_period_threshold
};
var admin_tab = templates.render('admin_tab', options);
$("#administration").html(admin_tab);
@ -287,6 +288,7 @@ function _setup_page() {
$("#admin-realm-create-stream-by-admins-only-status").expectOne().hide();
$("#admin-realm-message-editing-status").expectOne().hide();
$("#admin-realm-default-language-status").expectOne().hide();
$('#admin-realm-waiting_period_threshold_status').expectOne().hide();
$("#admin-emoji-status").expectOne().hide();
$('#admin-filter-status').expectOne().hide();
$('#admin-filter-pattern-status').expectOne().hide();
@ -499,6 +501,7 @@ function _setup_page() {
var create_stream_by_admins_only_status = $("#admin-realm-create-stream-by-admins-only-status").expectOne();
var message_editing_status = $("#admin-realm-message-editing-status").expectOne();
var default_language_status = $("#admin-realm-default-language-status").expectOne();
var waiting_period_threshold_status = $("#admin-realm-waiting_period_threshold_status").expectOne();
name_status.hide();
restricted_to_domain_status.hide();
invite_required_status.hide();
@ -519,6 +522,7 @@ function _setup_page() {
var new_allow_message_editing = $("#id_realm_allow_message_editing").prop("checked");
var new_message_content_edit_limit_minutes = $("#id_realm_message_content_edit_limit_minutes").val();
var new_default_language = $("#id_realm_default_language").val();
var new_waiting_period_threshold = $("#id_realm_waiting_period_threshold").val();
var new_auth_methods = {};
_.each($("#admin_auth_methods_table").find('tr.method_row'), function (method_row) {
new_auth_methods[$(method_row).data('method')] = $(method_row).find('input').prop('checked');
@ -547,7 +551,8 @@ function _setup_page() {
allow_message_editing: JSON.stringify(new_allow_message_editing),
message_content_edit_limit_seconds:
JSON.stringify(parseInt(new_message_content_edit_limit_minutes, 10) * 60),
default_language: JSON.stringify(new_default_language)
default_language: JSON.stringify(new_default_language),
waiting_period_threshold: JSON.stringify(parseInt(new_waiting_period_threshold, 10))
};
channel.patch({
@ -617,6 +622,11 @@ function _setup_page() {
ui.report_success(i18n.t("Default language changed!"), default_language_status);
}
}
if (response_data.waiting_period_threshold !== undefined) {
if (response_data.waiting_period_threshold > 0) {
ui.report_success(i18n.t("waiting period threshold changed!"), waiting_period_threshold_status);
}
}
},
error: function (xhr) {
var reason = $.parseJSON(xhr.responseText).reason;

View File

@ -70,6 +70,8 @@ function dispatch_normal_event(event) {
} else if (event.op === 'update' && event.property === 'default_language') {
page_params.realm_default_language = event.value;
admin.reset_realm_default_language();
} else if (event.op === 'update' && event.property === 'waiting_period_threshold') {
page_params.realm_waiting_period_threshold = event.value;
}
break;

View File

@ -9,6 +9,7 @@
<div class="alert" id="admin-realm-create-stream-by-admins-only-status"></div>
<div class="alert" id="admin-realm-message-editing-status"></div>
<div class="alert" id="admin-realm-default-language-status"></div>
<div class="alert" id="admin-realm-waiting_period_threshold_status"></div>
<div class="input-group admin-realm">
<label for="realm_name">{{t "Your organization's name" }}</label>
@ -83,6 +84,13 @@
{{/each}}
</select>
</div>
<div class="input-group">
<label for="realm_waiting_period_threshold">{{t "Waiting period for stream creation (in days)" }}</label>
<input type="text" id="id_realm_waiting_period_threshold"
name="realm_waiting_period_threshold"
class="admin-realm-message-content-edit-limit-minutes"
value="{{ realm_waiting_period_threshold }}"/>
</div>
<div class="input-group organization-submission">
<input type="submit" class="button" value="{{t 'Save changes' }}" />
</div>

View File

@ -564,6 +564,18 @@ def do_set_realm_default_language(realm, default_language):
)
send_event(event, active_user_ids(realm))
def do_set_realm_waiting_period_threshold(realm, threshold):
# type: (Realm, int) -> None
realm.waiting_period_threshold = threshold
realm.save(update_fields=['waiting_period_threshold'])
event = dict(
type="realm",
op="update",
property='waiting_period_threshold',
value=threshold,
)
send_event(event, active_user_ids(realm))
def do_deactivate_realm(realm):
# type: (Realm) -> None
"""
@ -3046,6 +3058,7 @@ def fetch_initial_state_data(user_profile, event_types, queue_id):
state['realm_allow_message_editing'] = user_profile.realm.allow_message_editing
state['realm_message_content_edit_limit_seconds'] = user_profile.realm.message_content_edit_limit_seconds
state['realm_default_language'] = user_profile.realm.default_language
state['realm_waiting_period_threshold'] = user_profile.realm.waiting_period_threshold
if want('realm_domain'):
state['realm_domain'] = user_profile.realm.domain

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('zerver', '0044_reaction'),
]
operations = [
migrations.AddField(
model_name='realm',
name='waiting_period_threshold',
field=models.PositiveIntegerField(default=0),
),
]

View File

@ -136,6 +136,7 @@ class Realm(ModelReprMixin, models.Model):
default_language = models.CharField(default=u'en', max_length=MAX_LANGUAGE_ID_LENGTH) # type: Text
authentication_methods = BitField(flags=AUTHENTICATION_FLAGS,
default=2**31 - 1) # type: BitHandler
waiting_period_threshold = models.PositiveIntegerField(default=0) # type: int
DEFAULT_NOTIFICATION_STREAM_NAME = u'announce'
@ -604,10 +605,14 @@ class UserProfile(ModelReprMixin, AbstractBaseUser, PermissionsMixin):
def can_create_streams(self):
# type: () -> bool
if self.is_realm_admin or not self.realm.create_stream_by_admins_only:
diff = (timezone.now() - self.date_joined).days
if self.is_realm_admin:
return True
else:
elif self.realm.create_stream_by_admins_only:
return False
if diff >= self.realm.waiting_period_threshold:
return True
return False
def major_tos_version(self):
# type: () -> int

View File

@ -35,7 +35,7 @@ from zerver.models import (
)
from zerver.lib.actions import (
do_add_default_stream, do_change_is_admin,
do_add_default_stream, do_change_is_admin, do_set_realm_waiting_period_threshold,
do_create_realm, do_remove_default_stream, do_set_realm_create_stream_by_admins_only,
gather_subscriptions_helper, bulk_add_subscriptions, bulk_remove_subscriptions,
gather_subscriptions, get_default_streams_for_realm, get_realm_by_string_id, get_stream,
@ -557,15 +557,15 @@ class StreamAdminTest(ZulipTestCase):
def test_create_stream_by_admins_only_setting(self):
# type: () -> None
"""
When realm.create_stream_by_admins_only setting is active,
non admin users shouldn't be able to create new streams.
When realm.create_stream_by_admins_only setting is active and
the number of days since the user had joined is less than waiting period
threshold, non admin users shouldn't be able to create new streams.
"""
email = 'hamlet@zulip.com'
self.login(email)
user_profile = get_user_profile_by_email(email)
do_change_is_admin(user_profile, False)
self.login(email)
do_set_realm_create_stream_by_admins_only(user_profile.realm, True)
stream_name = ['adminsonlysetting']
result = self.common_subscribe_to_streams(
email,
@ -573,8 +573,33 @@ class StreamAdminTest(ZulipTestCase):
)
self.assert_json_error(result, 'User cannot create streams.')
# Change setting back to default
do_set_realm_create_stream_by_admins_only(user_profile.realm, False)
def test_create_stream_by_waiting_period_threshold(self):
# type: () -> None
"""
Non admin users with account age greater or equal to waiting period
threshold should be able to create new streams.
"""
email = 'hamlet@zulip.com'
user_profile = get_user_profile_by_email(email)
self.login(email)
do_change_is_admin(user_profile, False)
do_set_realm_waiting_period_threshold(user_profile.realm, 10)
stream_name = ['waitingperiodtest']
result = self.common_subscribe_to_streams(
email,
stream_name
)
self.assert_json_error(result, 'User cannot create streams.')
do_set_realm_waiting_period_threshold(user_profile.realm, 0)
result = self.common_subscribe_to_streams(
email,
stream_name
)
self.assert_json_success(result)
def test_remove_already_not_subbed(self):
# type: () -> None

View File

@ -1887,6 +1887,7 @@ class HomeTest(ZulipTestCase):
"realm_name",
"realm_restricted_to_domain",
"realm_uri",
"realm_waiting_period_threshold",
"referrals",
"save_stacktraces",
"server_generation",

View File

@ -591,6 +591,7 @@ def home_real(request):
realm_message_content_edit_limit_seconds = register_ret['realm_message_content_edit_limit_seconds'],
realm_restricted_to_domain = register_ret['realm_restricted_to_domain'],
realm_default_language = register_ret['realm_default_language'],
realm_waiting_period_threshold = register_ret['realm_waiting_period_threshold'],
enter_sends = user_profile.enter_sends,
user_id = user_profile.id,
left_side_userlist = register_ret['left_side_userlist'],

View File

@ -13,6 +13,7 @@ from zerver.lib.actions import (
do_set_realm_message_editing,
do_set_realm_restricted_to_domain,
do_set_realm_default_language,
do_set_realm_waiting_period_threshold,
do_set_realm_authentication_methods
)
from zerver.lib.i18n import get_available_language_codes
@ -31,8 +32,9 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
allow_message_editing=REQ(validator=check_bool, default=None),
message_content_edit_limit_seconds=REQ(converter=to_non_negative_int, default=None),
default_language=REQ(validator=check_string, default=None),
waiting_period_threshold=REQ(converter=to_non_negative_int, default=None),
authentication_methods=REQ(validator=check_dict([]), default=None)):
# type: (HttpRequest, UserProfile, Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[int], Optional[str], Optional[dict]) -> HttpResponse
# type: (HttpRequest, UserProfile, Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[int], Optional[str], Optional[int], Optional[dict]) -> HttpResponse
# Validation for default_language
if default_language is not None and default_language not in get_available_language_codes():
raise JsonableError(_("Invalid language '%s'" % (default_language,)))
@ -73,4 +75,7 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
if default_language is not None and realm.default_language != default_language:
do_set_realm_default_language(realm, default_language)
data['default_language'] = default_language
if waiting_period_threshold is not None and realm.waiting_period_threshold != waiting_period_threshold:
do_set_realm_waiting_period_threshold(realm, waiting_period_threshold)
data['waiting_period_threshold'] = waiting_period_threshold
return json_success(data)