Add a user-visible setting for 24-hour time display.

(imported from commit d934824fd6b72e64a455aac9ff4585b262145f02)
This commit is contained in:
Kate Buckner 2015-08-19 13:35:46 -07:00 committed by Tim Abbott
parent 9734d1ab3f
commit 4d0f7c7ea4
12 changed files with 402 additions and 10 deletions

View File

@ -3,12 +3,8 @@ var feature_flags = (function () {
var exports = {}; var exports = {};
// Helpers // Helpers
var special_24_hour_people= _.contains([],
page_params.email);
var og_zuliper_emails = []; var og_zuliper_emails = [];
var iceland = page_params.domain === 'customer8.invalid';
var customer4_realms = [ var customer4_realms = [
'customer4.invalid', 'customer4.invalid',
'users.customer4.invalid' 'users.customer4.invalid'
@ -25,7 +21,6 @@ exports.collect_send_times = false;
// Permanent realm-specific stuff: // Permanent realm-specific stuff:
exports.disable_message_editing = _.contains(['mit.edu'], page_params.domain); exports.disable_message_editing = _.contains(['mit.edu'], page_params.domain);
exports.is_og_zulip_user = _.contains(og_zuliper_emails, page_params.email); exports.is_og_zulip_user = _.contains(og_zuliper_emails, page_params.email);
exports.twenty_four_hour_time = special_24_hour_people || iceland;
exports.left_side_userlist = _.contains(['customer7.invalid'], page_params.domain); exports.left_side_userlist = _.contains(['customer7.invalid'], page_params.domain);
exports.enable_new_user_app_alerts = ! _.contains(['employees.customer16.invalid'], page_params.domain); exports.enable_new_user_app_alerts = ! _.contains(['employees.customer16.invalid'], page_params.domain);

View File

@ -17,7 +17,7 @@ function MessageListView(list, table_name, collapse_messages) {
(function () { (function () {
function stringify_time(time) { function stringify_time(time) {
if (feature_flags.twenty_four_hour_time) { if (page_params.twenty_four_hour_time) {
return time.toString('HH:mm'); return time.toString('HH:mm');
} }
return time.toString('h:mm TT'); return time.toString('h:mm TT');

View File

@ -180,6 +180,9 @@ function get_events_success(events) {
notifications.handle_global_notification_updates(event.notification_name, notifications.handle_global_notification_updates(event.notification_name,
event.setting); event.setting);
break; break;
case 'update_display_settings':
page_params.twenty_four_hour_time = event.twenty_four_hour_time;
break;
} }
}; };

View File

@ -151,6 +151,7 @@ exports.setup_page = function () {
$("#settings").html(settings_tab); $("#settings").html(settings_tab);
$("#settings-status").hide(); $("#settings-status").hide();
$("#notify-settings-status").hide(); $("#notify-settings-status").hide();
$("#display-settings-status").hide();
$("#ui-settings-status").hide(); $("#ui-settings-status").hide();
alert_words_ui.set_up_alert_words(); alert_words_ui.set_up_alert_words();
@ -383,6 +384,41 @@ exports.setup_page = function () {
update_audible_notification_setting); update_audible_notification_setting);
}); });
$("#twenty_four_hour_time").change(function () {
var time_checkbox = $("#twenty_four_hour_time").is(":checked");
var data = {};
data.twenty_four_hour_time = JSON.stringify(time_checkbox);
channel.patch({
url: '/json/time_setting',
data: data,
success: function (resp, statusText, xhr, form) {
var message = "Updated display settings!";
var result = $.parseJSON(xhr.responseText);
var display_settings_status = $('#display-settings-status').expectOne();
display_settings_status.removeClass(status_classes)
.addClass('alert-success')
.text(message).stop(true).fadeTo(0,1);
},
error: function (xhr, error_type, xhn) {
var response = "Error updating display settings";
var display_settings_status = $('#display-settings-status').expectOne();
if (xhr.status.toString().charAt(0) === "4") {
// Only display the error response for 4XX, where we've crafted
// a nice response.
response += ": " + $.parseJSON(xhr.responseText).msg;
}
display_settings_status.removeClass(status_classes)
.addClass('alert-error')
.text(response).stop(true).fadeTo(0,1);
}
});
});
$("#get_api_key_box").hide(); $("#get_api_key_box").hide();
$("#show_api_key_box").hide(); $("#show_api_key_box").hide();
$("#get_api_key_box form").ajaxForm({ $("#get_api_key_box form").ajaxForm({

View File

@ -3299,7 +3299,7 @@ div.edit_bot {
position: relative; position: relative;
} }
#settings #notify-settings-status,#ui-settings-status { #settings #notify-settings-status,#ui-settings-status,#display-settings-status {
width: 80%; width: 80%;
text-align: center; text-align: center;
margin: auto; margin: auto;
@ -3324,7 +3324,7 @@ div.edit_bot {
left: 20px; left: 20px;
} }
#settings .settings-section .notification-settings-form,.ui-settings-form { #settings .settings-section .notification-settings-form,.ui-settings-form,.display-settings-form {
width: 400px; width: 400px;
margin: auto; margin: auto;
} }
@ -3343,6 +3343,7 @@ div.edit_bot {
} }
#settings .settings-section .notification-settings-form .controls, #settings .settings-section .notification-settings-form .controls,
#settings .settings-section .display-settings-form .controls,
#settings .settings-section .ui-settings-form .controls { #settings .settings-section .ui-settings-form .controls {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
@ -3354,7 +3355,12 @@ div.edit_bot {
margin-right: 12px; margin-right: 12px;
} }
#settings .settings-section .display-settings-form .controls {
margin-left: 0;
}
#settings .settings-section .notification-settings-form .controls input[type='checkbox'], #settings .settings-section .notification-settings-form .controls input[type='checkbox'],
#settings .settings-section .display-settings-form .controls input[type='checkbox'],
#settings .settings-section .ui-settings-form .controls input[type='checkbox'] { #settings .settings-section .ui-settings-form .controls input[type='checkbox'] {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
@ -3364,7 +3370,8 @@ div.edit_bot {
} }
#settings .settings-section .notification-settings-form .control-label, #settings .settings-section .notification-settings-form .control-label,
#settings .settings-section .ui-settings-form .control-label { #settings .settings-section .ui-settings-form .control-label,
#settings .settings-section .display-settings-form .control-label {
float: none; float: none;
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
@ -3373,6 +3380,10 @@ div.edit_bot {
width: 240px; width: 240px;
} }
#settings .settings-section .display-settings-form .control-label {
width: 360px;
}
#settings .settings-section .notification-settings-form .notification-submission, #settings .settings-section .notification-settings-form .notification-submission,
#settings .settings-section .ui-settings-form .ui-submission { #settings .settings-section .ui-settings-form .ui-submission {
margin-left: 140px; margin-left: 140px;
@ -3477,6 +3488,7 @@ div.edit_bot {
#settings .settings-section .account-settings-form, #settings .settings-section .account-settings-form,
#settings .settings-section .new-bot-form, #settings .settings-section .new-bot-form,
#settings .settings-section .notification-settings-form, #settings .settings-section .notification-settings-form,
#settings .settings-section .display-settings-form,
#settings .settings-section .edit-bot-form-box { #settings .settings-section .edit-bot-form-box {
width: 100%; width: 100%;
} }

View File

@ -86,6 +86,28 @@
</div> </div>
</div> </div>
<div>
<div id="display-settings" class="settings-section">
<div class="settings-section-title"><i class="icon-vector-time settings-section-icon"></i>
Display Settings</div>
<div class="alert" id="display-settings-status"></div>
<div class="display-settings-form">
<div class="control-group">
<div class="controls">
<input type="checkbox" name="twenty_four_hour_time"
id="twenty_four_hour_time"
{{#if page_params.twenty_four_hour_time}}
checked="yes"
{{/if}} />
</div>
<label for="twenty_four_hour_time" class="control-label">
Display time in 24-hour format (17:00 instead of 5:00 PM)
</label>
</div>
</div>
</div>
</div>
<div> <div>
<div id="notification-settings" class="settings-section"> <div id="notification-settings" class="settings-section">
<div class="settings-section-title"><i class="icon-vector-warning-sign settings-section-icon"></i>Notifications</div> <div class="settings-section-title"><i class="icon-vector-warning-sign settings-section-icon"></i>Notifications</div>

View File

@ -1717,6 +1717,16 @@ def do_change_default_desktop_notifications(user_profile, default_desktop_notifi
user_profile.default_desktop_notifications = default_desktop_notifications user_profile.default_desktop_notifications = default_desktop_notifications
user_profile.save(update_fields=["default_desktop_notifications"]) user_profile.save(update_fields=["default_desktop_notifications"])
def do_change_twenty_four_hour_time(user_profile, twenty_four_hour_time, log=True):
user_profile.twenty_four_hour_time = twenty_four_hour_time
user_profile.save(update_fields=["twenty_four_hour_time"])
event = {'type': 'update_display_settings',
'user': user_profile.email,
'setting': twenty_four_hour_time}
if log:
log_event(event)
send_event(event, [user_profile.id])
def set_default_streams(realm, stream_names): def set_default_streams(realm, stream_names):
DefaultStream.objects.filter(realm=realm).delete() DefaultStream.objects.filter(realm=realm).delete()
for stream_name in stream_names: for stream_name in stream_names:
@ -2359,6 +2369,9 @@ def fetch_initial_state_data(user_profile, event_types, queue_id):
if want('stream'): if want('stream'):
state['streams'] = do_get_streams(user_profile) state['streams'] = do_get_streams(user_profile)
if want('update_display_settings'):
state['twenty_four_hour_time'] = user_profile.twenty_four_hour_time
return state return state
def apply_events(state, events, user_profile): def apply_events(state, events, user_profile):
@ -2489,6 +2502,8 @@ def apply_events(state, events, user_profile):
state['muted_topics'] = event["muted_topics"] state['muted_topics'] = event["muted_topics"]
elif event['type'] == "realm_filters": elif event['type'] == "realm_filters":
state['realm_filters'] = event["realm_filters"] state['realm_filters'] = event["realm_filters"]
elif event['type'] == "update_display_settings":
state['twenty_four_hour_time'] == event["twenty_four_hour_time"]
else: else:
raise ValueError("Unexpected event type %s" % (event['type'],)) raise ValueError("Unexpected event type %s" % (event['type'],))

View File

@ -0,0 +1,275 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
from django.conf import settings
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'UserProfile.twenty_four_hour_time'
db.add_column(u'zerver_userprofile', 'twenty_four_hour_time',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=True)
if settings.DEPLOYED and not settings.ENTERPRISE:
db.execute("UPDATE zerver_userprofile SET twenty_four_hour_time = TRUE WHERE realm_id IN " +
"(SELECT id FROM zerver_realm WHERE domain='customer8.invalid')")
db.execute("UPDATE zerver_userprofile SET twenty_four_hour_time = TRUE WHERE email IN ()")
def backwards(self, orm):
# Deleting field 'UserProfile.twenty_four_hour_time'
db.delete_column(u'zerver_userprofile', 'twenty_four_hour_time')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'zerver.appledevicetoken': {
'Meta': {'object_name': 'AppleDeviceToken'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.client': {
'Meta': {'object_name': 'Client'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30', 'db_index': 'True'})
},
u'zerver.defaultstream': {
'Meta': {'unique_together': "(('realm', 'stream'),)", 'object_name': 'DefaultStream'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Stream']"})
},
u'zerver.huddle': {
'Meta': {'object_name': 'Huddle'},
'huddle_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'zerver.message': {
'Meta': {'object_name': 'Message'},
'content': ('django.db.models.fields.TextField', [], {}),
'edit_history': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'has_attachment': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'has_image': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'has_link': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_edit_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'pub_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Recipient']"}),
'rendered_content': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'rendered_content_version': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"}),
'sending_client': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Client']"}),
'subject': ('django.db.models.fields.CharField', [], {'max_length': '60', 'db_index': 'True'})
},
u'zerver.mituser': {
'Meta': {'object_name': 'MitUser'},
'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
u'zerver.preregistrationuser': {
'Meta': {'object_name': 'PreregistrationUser'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invited_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']", 'null': 'True'}),
'referred_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']", 'null': 'True'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['zerver.Stream']", 'null': 'True', 'symmetrical': 'False'})
},
u'zerver.pushdevicetoken': {
'Meta': {'object_name': 'PushDeviceToken'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ios_app_id': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'kind': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
'last_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.realm': {
'Meta': {'object_name': 'Realm'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'deactivated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invite_by_admins_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'invite_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'mandatory_topics': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True'}),
'name_changes_disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'notifications_stream': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': u"orm['zerver.Stream']"}),
'restricted_to_domain': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'show_digest_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'zerver.realmalias': {
'Meta': {'object_name': 'RealmAlias'},
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']", 'null': 'True'})
},
u'zerver.realmemoji': {
'Meta': {'unique_together': "(('realm', 'name'),)", 'object_name': 'RealmEmoji'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'img_url': ('django.db.models.fields.TextField', [], {}),
'name': ('django.db.models.fields.TextField', [], {}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"})
},
u'zerver.realmfilter': {
'Meta': {'unique_together': "(('realm', 'pattern'),)", 'object_name': 'RealmFilter'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'pattern': ('django.db.models.fields.TextField', [], {}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"}),
'url_format_string': ('django.db.models.fields.TextField', [], {})
},
u'zerver.recipient': {
'Meta': {'unique_together': "(('type', 'type_id'),)", 'object_name': 'Recipient'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'db_index': 'True'}),
'type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'})
},
u'zerver.referral': {
'Meta': {'object_name': 'Referral'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.scheduledjob': {
'Meta': {'object_name': 'ScheduledJob'},
'data': ('django.db.models.fields.TextField', [], {}),
'filter_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'filter_string': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'scheduled_timestamp': ('django.db.models.fields.DateTimeField', [], {}),
'type': ('django.db.models.fields.PositiveSmallIntegerField', [], {})
},
u'zerver.stream': {
'Meta': {'unique_together': "(('name', 'realm'),)", 'object_name': 'Stream'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'deactivated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'description': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'email_token': ('django.db.models.fields.CharField', [], {'default': "'db559172cf87493869af805bd51da2d4'", 'max_length': '32'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invite_only': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '60', 'db_index': 'True'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"})
},
u'zerver.streamcolor': {
'Meta': {'object_name': 'StreamColor'},
'color': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Subscription']"})
},
u'zerver.subscription': {
'Meta': {'unique_together': "(('user_profile', 'recipient'),)", 'object_name': 'Subscription'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'audible_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'color': ('django.db.models.fields.CharField', [], {'default': "'#c2c2c2'", 'max_length': '10'}),
'desktop_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'in_home_view': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}),
'notifications': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Recipient']"}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.useractivity': {
'Meta': {'unique_together': "(('user_profile', 'client', 'query'),)", 'object_name': 'UserActivity'},
'client': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Client']"}),
'count': ('django.db.models.fields.IntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_visit': ('django.db.models.fields.DateTimeField', [], {}),
'query': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.useractivityinterval': {
'Meta': {'object_name': 'UserActivityInterval'},
'end': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'start': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.usermessage': {
'Meta': {'unique_together': "(('user_profile', 'message'),)", 'object_name': 'UserMessage'},
'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Message']"}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.userpresence': {
'Meta': {'unique_together': "(('user_profile', 'client'),)", 'object_name': 'UserPresence'},
'client': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Client']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'status': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'alert_words': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'api_key': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'autoscroll_forever': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'avatar_source': ('django.db.models.fields.CharField', [], {'default': "'G'", 'max_length': '1'}),
'bot_owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'default_all_public_streams': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'default_desktop_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'default_events_register_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['zerver.Stream']"}),
'default_sending_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['zerver.Stream']"}),
'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'db_index': 'True'}),
'enable_desktop_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_digest_emails': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_offline_email_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_offline_push_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_sounds': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_stream_desktop_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_stream_sounds': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enter_sends': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}),
'full_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invites_granted': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'invites_used': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_bot': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_mirror_dummy': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_pointer_updater': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'last_reminder': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}),
'muted_topics': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'onboarding_steps': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'pointer': ('django.db.models.fields.IntegerField', [], {}),
'rate_limits': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"}),
'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'tutorial_status': ('django.db.models.fields.CharField', [], {'default': "'W'", 'max_length': '1'}),
'twenty_four_hour_time': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"})
}
}
complete_apps = ['zerver']

View File

@ -328,6 +328,9 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
enter_sends = models.NullBooleanField(default=True) enter_sends = models.NullBooleanField(default=True)
autoscroll_forever = models.BooleanField(default=False) autoscroll_forever = models.BooleanField(default=False)
# display settings
twenty_four_hour_time = models.BooleanField(default=False)
# Hours to wait before sending another email to a user # Hours to wait before sending another email to a user
EMAIL_REMINDER_WAITPERIOD = 24 EMAIL_REMINDER_WAITPERIOD = 24
# Minutes to wait before warning a bot owner that her bot sent a message # Minutes to wait before warning a bot owner that her bot sent a message

View File

@ -34,6 +34,7 @@ from zerver.lib.actions import (
do_set_realm_invite_by_admins_only, do_set_realm_invite_by_admins_only,
do_update_message, do_update_message,
do_update_pointer, do_update_pointer,
do_change_twenty_four_hour_time,
fetch_initial_state_data, fetch_initial_state_data,
) )
@ -434,6 +435,21 @@ class EventsRegisterTest(AuthedTestCase):
error = schema_checker('events[0]', events[0]) error = schema_checker('events[0]', events[0])
self.assert_on_error(error) self.assert_on_error(error)
def test_change_twenty_four_hour_time(self):
schema_checker = check_dict([
('type', equals('update_display_settings')),
('op', equals('update')),
('person', check_dict([
('email', check_string),
('twenty_four_hour_time', check_bool),
])),
])
# The first False is probably a noop, then we get transitions in both directions.
for twenty_four_hour_time in [False, True, False]:
events = self.do_test(lambda: do_change_twenty_four_hour_time(self.user_profile, twenty_four_hour_time))
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
def test_realm_emoji_events(self): def test_realm_emoji_events(self):
schema_checker = check_dict([ schema_checker = check_dict([
('type', equals('realm_emoji')), ('type', equals('realm_emoji')),
@ -463,6 +479,7 @@ class EventsRegisterTest(AuthedTestCase):
error = schema_checker('events[0]', events[0]) error = schema_checker('events[0]', events[0])
self.assert_on_error(error) self.assert_on_error(error)
def test_create_bot(self): def test_create_bot(self):
bot_created_checker = check_dict([ bot_created_checker = check_dict([
('type', equals('realm_bot')), ('type', equals('realm_bot')),

View File

@ -46,7 +46,7 @@ from zerver.lib.actions import bulk_remove_subscriptions, do_change_password, \
do_change_enable_stream_desktop_notifications, do_change_enable_stream_sounds, \ do_change_enable_stream_desktop_notifications, do_change_enable_stream_sounds, \
do_change_stream_description, do_get_streams, do_make_stream_private, \ do_change_stream_description, do_get_streams, do_make_stream_private, \
do_regenerate_api_key, do_remove_default_stream, do_update_pointer, \ do_regenerate_api_key, do_remove_default_stream, do_update_pointer, \
do_change_avatar_source do_change_avatar_source, do_change_twenty_four_hour_time
from zerver.lib.create_user import random_api_key from zerver.lib.create_user import random_api_key
from zerver.lib.push_notifications import num_push_devices_for_user from zerver.lib.push_notifications import num_push_devices_for_user
@ -1071,6 +1071,7 @@ def home(request):
user_profile.enable_offline_email_notifications, user_profile.enable_offline_email_notifications,
enable_offline_push_notifications = enable_offline_push_notifications =
user_profile.enable_offline_push_notifications, user_profile.enable_offline_push_notifications,
twenty_four_hour_time = register_ret['twenty_four_hour_time'],
enable_digest_emails = user_profile.enable_digest_emails, enable_digest_emails = user_profile.enable_digest_emails,
event_queue_id = register_ret['queue_id'], event_queue_id = register_ret['queue_id'],
@ -1710,6 +1711,18 @@ def json_change_settings(request, user_profile,
return json_success(result) return json_success(result)
@authenticated_json_post_view
@has_request_variables
def json_time_setting(request, user_profile, twenty_four_hour_time=REQ(validator=check_bool,default=None)):
result = {}
if twenty_four_hour_time is not None and \
user_profile.twenty_four_hour_time != twenty_four_hour_time:
do_change_twenty_four_hour_time(user_profile, twenty_four_hour_time)
result['twenty_four_hour_time'] = twenty_four_hour_time
return json_success(result)
@authenticated_json_post_view @authenticated_json_post_view
@has_request_variables @has_request_variables
def json_change_notify_settings(request, user_profile, def json_change_notify_settings(request, user_profile,

View File

@ -145,6 +145,7 @@ urlpatterns += patterns('zerver.views',
url(r'^json/set_alert_words$', 'json_set_alert_words'), url(r'^json/set_alert_words$', 'json_set_alert_words'),
url(r'^json/set_muted_topics$', 'json_set_muted_topics'), url(r'^json/set_muted_topics$', 'json_set_muted_topics'),
url(r'^json/set_avatar$', 'json_set_avatar'), url(r'^json/set_avatar$', 'json_set_avatar'),
url(r'^json/time_setting$', 'json_time_setting'),
# This json format view is used by the LEGACY pre-REST API. It # This json format view is used by the LEGACY pre-REST API. It
# requires an API key. # requires an API key.