Add realm-level default language setting.

Adds a new field default language in the zerver_realm model.
This realm level default language will be used as default language
for newly created users. Realm level default language can be
changed from the administration page.

Fixes #1372.
This commit is contained in:
Rag Sagar 2016-08-04 19:32:41 +04:00 committed by Tim Abbott
parent 7ec6a394fe
commit 2fef36f15a
13 changed files with 198 additions and 6 deletions

View File

@ -498,6 +498,18 @@ casper.then(function () {
});
});
casper.then(function () {
casper.test.info("Changing realm default language");
casper.evaluate(function () {
$('#id_realm_default_language').val('de').change();
});
casper.click('form.admin-realm-form input.btn');
});
casper.waitUntilVisible('#admin-realm-default-language-status', function () {
casper.test.assertSelectorHasText('#admin-realm-default-language-status', 'Default language changed!');
});
common.then_log_out();
casper.run(function () {

View File

@ -137,6 +137,10 @@ exports.populate_emoji = function (emoji_data) {
loading.destroy_indicator($('#admin_page_emoji_loading_indicator'));
};
exports.reset_realm_default_language = function () {
$("#id_realm_default_language").val(page_params.realm_default_language);
};
function _setup_page() {
var options = {
realm_name: page_params.realm_name,
@ -146,7 +150,9 @@ function _setup_page() {
realm_invite_by_admins_only: page_params.realm_invite_by_admins_only,
realm_create_stream_by_admins_only: page_params.realm_create_stream_by_admins_only,
realm_allow_message_editing: page_params.realm_allow_message_editing,
realm_message_content_edit_limit_minutes: Math.ceil(page_params.realm_message_content_edit_limit_seconds / 60)
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
};
var admin_tab = templates.render('admin_tab', options);
$("#administration").html(admin_tab);
@ -157,10 +163,13 @@ function _setup_page() {
$("#admin-realm-invite-by-admins-only-status").expectOne().hide();
$("#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-emoji-status").expectOne().hide();
$("#admin-emoji-name-status").expectOne().hide();
$("#admin-emoji-url-status").expectOne().hide();
$("#id_realm_default_language").val(page_params.realm_default_language);
// create loading indicators
loading.make_indicator($('#admin_page_users_loading_indicator'));
loading.make_indicator($('#admin_page_bots_loading_indicator'));
@ -360,12 +369,15 @@ function _setup_page() {
var invite_by_admins_only_status = $("#admin-realm-invite-by-admins-only-status").expectOne();
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();
name_status.hide();
restricted_to_domain_status.hide();
invite_required_status.hide();
invite_by_admins_only_status.hide();
create_stream_by_admins_only_status.hide();
message_editing_status.hide();
default_language_status.hide();
e.preventDefault();
e.stopPropagation();
@ -377,6 +389,7 @@ function _setup_page() {
var new_create_stream_by_admins_only = $("#id_realm_create_stream_by_admins_only").prop("checked");
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();
// If allow_message_editing is unchecked, message_content_edit_limit_minutes
// is irrelevant. Hence if allow_message_editing is unchecked, and
@ -398,7 +411,8 @@ function _setup_page() {
invite_by_admins_only: JSON.stringify(new_invite_by_admins_only),
create_stream_by_admins_only: JSON.stringify(new_create_stream_by_admins_only),
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)
message_content_edit_limit_seconds: JSON.stringify(parseInt(new_message_content_edit_limit_minutes, 10) * 60),
default_language: JSON.stringify(new_default_language)
};
channel.patch({
@ -455,6 +469,11 @@ function _setup_page() {
// in this function, so update the field just in case
$("#id_realm_message_content_edit_limit_minutes").val(data_message_content_edit_limit_minutes);
}
if (response_data.default_language !== undefined) {
if (response_data.default_language) {
ui.report_success(i18n.t("Default language changed!"), default_language_status);
}
}
},
error: function (xhr, error) {
ui.report_error(i18n.t("Failed!"), xhr, name_status);

View File

@ -99,6 +99,9 @@ function get_events_success(events) {
$.each(event.data, function (key, value) {
page_params['realm_' + key] = value;
});
} else if (event.op === 'update' && event.property === 'default_language') {
page_params.realm_default_language = event.value;
admin.reset_realm_default_language();
}
break;
case 'realm_user':

View File

@ -36,6 +36,7 @@
<div class="alert" id="admin-realm-invite-by-admins-only-status"></div>
<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>
<label for="realm_name" class="control-label">{{t "Your organization's name" }}</label>
<div class="controls">
<input type="text" id="id_realm_name" name="realm_name" class="admin-realm-name"
@ -116,6 +117,16 @@
{{#unless realm_allow_message_editing}}disabled="disabled"{{/unless}} />
</div>
</div>
<div class="control-group">
<label for="realm_default_language" class="control-label">{{t "Default Language" }}:</label>
<div class="controls">
<select name="realm_default_language" id="id_realm_default_language">
{{#each language_list}}
<option value='{{this.code}}'>{{this.name}}</option>
{{/each}}
</select>
</div>
</div>
<div class="controls organization-submission">
<input type="submit" class="btn btn-big btn-primary" value="{{t 'Save changes' }}" />
</div>

View File

@ -427,6 +427,25 @@ def do_set_realm_message_editing(realm, allow_message_editing, message_content_e
)
send_event(event, active_user_ids(realm))
def do_set_realm_default_language(realm, default_language):
# type: (Realm, text_type) -> None
if default_language == 'zh_CN':
# NB: remove this once we upgrade to Django 1.9
# zh-cn and zh-tw will be replaced by zh-hans and zh-hant in
# Django 1.9
default_language= 'zh_HANS'
realm.default_language = default_language
realm.save(update_fields=['default_language'])
event = dict(
type="realm",
op="update",
property="default_language",
value=default_language
)
send_event(event, active_user_ids(realm))
def do_deactivate_realm(realm):
# type: (Realm) -> None
"""
@ -2710,6 +2729,7 @@ def fetch_initial_state_data(user_profile, event_types, queue_id):
state['realm_create_stream_by_admins_only'] = user_profile.realm.create_stream_by_admins_only
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
if want('realm_domain'):
state['realm_domain'] = user_profile.realm.domain

View File

@ -39,7 +39,8 @@ def create_user_profile(realm, email, password, active, bot_type, full_name,
pointer=-1, is_bot=bool(bot_type), bot_type=bot_type,
is_mirror_dummy=is_mirror_dummy,
enable_stream_desktop_notifications=enable_stream_desktop_notifications,
onboarding_steps=ujson.dumps([]))
onboarding_steps=ujson.dumps([]),
default_language=realm.default_language)
if bot_owner is not None:
# `user_profile.bot_owner = bot_owner` doesn't work on python 3.4
user_profile.bot_owner_id = bot_owner.id

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('zerver', '0026_delete_mituser'),
]
operations = [
migrations.AddField(
model_name='realm',
name='default_language',
field=models.CharField(default='en', max_length=50),
),
]

View File

@ -153,6 +153,7 @@ class Realm(ModelReprMixin, models.Model):
date_created = models.DateTimeField(default=timezone.now) # type: datetime.datetime
notifications_stream = models.ForeignKey('Stream', related_name='+', null=True, blank=True) # type: Optional[Stream]
deactivated = models.BooleanField(default=False) # type: bool
default_language = models.CharField(default=u'en', max_length=MAX_LANGUAGE_ID_LENGTH) # type: text_type
DEFAULT_NOTIFICATION_STREAM_NAME = u'announce'

View File

@ -41,6 +41,7 @@ from zerver.lib.actions import (
do_set_realm_invite_required,
do_set_realm_invite_by_admins_only,
do_set_realm_message_editing,
do_set_realm_default_language,
do_update_message,
do_update_pointer,
do_change_twenty_four_hour_time,
@ -478,6 +479,18 @@ class EventsRegisterTest(AuthedTestCase):
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
def test_change_realm_default_language(self):
# type: () -> None
schema_checker = check_dict([
('type', equals('realm')),
('op', equals('update')),
('property', equals('default_language')),
('value', check_string),
])
events = self.do_test(lambda: do_set_realm_default_language(self.user_profile.realm, 'de'))
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
def test_change_realm_create_stream_by_admins_only(self):
# type: () -> None
schema_checker = check_dict([

View File

@ -18,6 +18,7 @@ from zerver.lib.actions import (
set_default_streams,
)
from zerver.lib.actions import do_set_realm_default_language
from zerver.lib.digest import send_digest_email
from zerver.lib.notifications import enqueue_welcome_emails, one_click_unsubscribe_link
from zerver.lib.test_helpers import AuthedTestCase, find_key_by_email, queries_captured
@ -664,3 +665,45 @@ class RealmCreationTest(AuthedTestCase):
result = self.client_get(result["Location"])
self.assert_in_response("You're the first one here!", result)
class UserSignUpTest(AuthedTestCase):
def test_user_default_language(self):
"""
Check if the default language of new user is the default language
of the realm.
"""
username = "newguy"
email = "newguy@zulip.com"
domain = "zulip.com"
password = "newpassword"
realm = get_realm(domain)
do_set_realm_default_language(realm, "de")
result = self.client_post('/accounts/home/', {'email': email})
self.assertEquals(result.status_code, 302)
self.assertTrue(result["Location"].endswith(
"/accounts/send_confirm/%s@%s" % (username, domain)))
result = self.client_get(result["Location"])
self.assert_in_response("Check your email so we can get started.", result)
# Visit the confirmation link.
from django.core.mail import outbox
for message in reversed(outbox):
if email in message.to:
confirmation_link_pattern = re.compile(settings.EXTERNAL_HOST + "(\S+)>")
confirmation_url = confirmation_link_pattern.search(
message.body).groups()[0]
break
else:
raise ValueError("Couldn't find a confirmation email.")
result = self.client_get(confirmation_url)
self.assertEquals(result.status_code, 200)
# Pick a password and agree to the ToS.
result = self.submit_reg_form_for_user(username, password, domain)
self.assertEquals(result.status_code, 302)
user_profile = get_user_profile_by_email(email)
self.assertEqual(user_profile.default_language, realm.default_language)
outbox.pop()

View File

@ -166,6 +166,41 @@ class RealmTest(AuthedTestCase):
user = get_user_profile_by_email('hamlet@zulip.com')
self.assertTrue(user.realm.deactivated)
def test_do_set_realm_default_language(self):
# type: () -> None
new_lang = "de"
realm = get_realm('zulip.com')
self.assertNotEqual(realm.default_language, new_lang)
# we need an admin user.
email = 'iago@zulip.com'
self.login(email)
req = dict(default_language=ujson.dumps(new_lang))
result = self.client_patch('/json/realm', req)
self.assert_json_success(result)
realm = get_realm('zulip.com')
self.assertEqual(realm.default_language, new_lang)
# Test setting zh_CN, we set zh_HANS instead of zh_CN in db
chinese = "zh_CN"
simplified_chinese = "zh_HANS"
req = dict(default_language=ujson.dumps(chinese))
result = self.client_patch('/json/realm', req)
self.assert_json_success(result)
realm = get_realm('zulip.com')
self.assertEqual(realm.default_language, simplified_chinese)
# Test to make sure that when invalid languages are passed
# as the default realm language, correct validation error is
# raised and the invalid language is not saved in db
invalid_lang = "invalid_lang"
req = dict(default_language=ujson.dumps(invalid_lang))
result = self.client_patch('/json/realm', req)
self.assert_json_error(result, "Invalid language '%s'" % (invalid_lang,))
realm = get_realm('zulip.com')
self.assertNotEqual(realm.default_language, invalid_lang)
class PermissionTest(AuthedTestCase):
def test_get_admin_users(self):
# type: () -> None
@ -1819,6 +1854,7 @@ class HomeTest(AuthedTestCase):
"prompt_for_invites",
"realm_allow_message_editing",
"realm_create_stream_by_admins_only",
"realm_default_language",
"realm_default_streams",
"realm_emoji",
"realm_filters",

View File

@ -942,6 +942,7 @@ def home(request):
realm_allow_message_editing = register_ret['realm_allow_message_editing'],
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'],
enter_sends = user_profile.enter_sends,
left_side_userlist = register_ret['left_side_userlist'],
default_language = register_ret['default_language'],
@ -992,6 +993,7 @@ def home(request):
presence_disabled = user_profile.realm.presence_disabled,
is_zephyr_mirror_realm = user_profile.realm.is_zephyr_mirror_realm,
)
if narrow_stream is not None:
# In narrow_stream context, initial pointer is just latest message
recipient = get_recipient(Recipient.STREAM, narrow_stream.id)

View File

@ -3,6 +3,8 @@ from __future__ import absolute_import
from typing import Any, Optional
from django.http import HttpRequest, HttpResponse
from django.utils.translation import ugettext as _
from zerver.decorator import require_realm_admin, to_non_negative_int
from zerver.lib.actions import (
do_set_realm_create_stream_by_admins_only,
@ -11,8 +13,10 @@ from zerver.lib.actions import (
do_set_realm_invite_required,
do_set_realm_message_editing,
do_set_realm_restricted_to_domain,
do_set_realm_default_language,
)
from zerver.lib.request import has_request_variables, REQ
from zerver.lib.i18n import get_available_language_codes
from zerver.lib.request import has_request_variables, REQ, JsonableError
from zerver.lib.response import json_success, json_error
from zerver.lib.validator import check_string, check_list, check_bool
from zerver.models import UserProfile
@ -25,8 +29,13 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
invite_by_admins_only=REQ(validator=check_bool, default=None),
create_stream_by_admins_only=REQ(validator=check_bool, default=None),
allow_message_editing=REQ(validator=check_bool, default=None),
message_content_edit_limit_seconds=REQ(converter=to_non_negative_int, default=None)):
# type: (HttpRequest, UserProfile, Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[int]) -> HttpResponse
message_content_edit_limit_seconds=REQ(converter=to_non_negative_int, default=None),
default_language=REQ(validator=check_string, default=None)):
# type: (HttpRequest, UserProfile, Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[int], Optional[str]) -> 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,)))
realm = user_profile.realm
data = {} # type: Dict[str, Any]
if name is not None and realm.name != name:
@ -54,4 +63,7 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
do_set_realm_message_editing(realm, allow_message_editing, message_content_edit_limit_seconds)
data['allow_message_editing'] = allow_message_editing
data['message_content_edit_limit_seconds'] = message_content_edit_limit_seconds
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
return json_success(data)