mirror of https://github.com/zulip/zulip.git
[schema] Modify device token to support both iOS and Android
This replaces the AppleDeviceToken table with a generic PushDeviceToken with a `kind` field to make it easier to add functionality like per-device/per-stream settings that share code between Android and iOS devices. The schema must continue to work on prod with the old table name, so we add the new table in parallel and can drop the old table once this code hits prod and any necessary data is copied. (imported from commit 0209a7013f2850ac6311f23c3d6f92c65ffd19e3)
This commit is contained in:
parent
0a35ca2ff7
commit
e4589700b6
|
@ -6,7 +6,7 @@ from django.contrib.sessions.models import Session
|
||||||
from zerver.lib.context_managers import lockfile
|
from zerver.lib.context_managers import lockfile
|
||||||
from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, \
|
from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, \
|
||||||
Subscription, Recipient, Message, UserMessage, valid_stream_name, \
|
Subscription, Recipient, Message, UserMessage, valid_stream_name, \
|
||||||
DefaultStream, UserPresence, Referral, MAX_SUBJECT_LENGTH, \
|
DefaultStream, UserPresence, Referral, PushDeviceToken, MAX_SUBJECT_LENGTH, \
|
||||||
MAX_MESSAGE_LENGTH, get_client, get_stream, get_recipient, get_huddle, \
|
MAX_MESSAGE_LENGTH, get_client, get_stream, get_recipient, get_huddle, \
|
||||||
get_user_profile_by_id, PreregistrationUser, get_display_recipient, \
|
get_user_profile_by_id, PreregistrationUser, get_display_recipient, \
|
||||||
to_dict_cache_key, get_realm, stringify_message_dict, bulk_get_recipients, \
|
to_dict_cache_key, get_realm, stringify_message_dict, bulk_get_recipients, \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from zerver.models import AppleDeviceToken
|
from zerver.models import PushDeviceToken
|
||||||
from zerver.lib.timestamp import timestamp_to_datetime
|
from zerver.lib.timestamp import timestamp_to_datetime
|
||||||
from zerver.decorator import statsd_increment
|
from zerver.decorator import statsd_increment
|
||||||
|
|
||||||
|
@ -17,8 +17,11 @@ connection = None
|
||||||
if settings.APNS_CERT_FILE is not None and os.path.exists(settings.APNS_CERT_FILE):
|
if settings.APNS_CERT_FILE is not None and os.path.exists(settings.APNS_CERT_FILE):
|
||||||
connection = session.get_connection(settings.APNS_SANDBOX, cert_file=settings.APNS_CERT_FILE)
|
connection = session.get_connection(settings.APNS_SANDBOX, cert_file=settings.APNS_CERT_FILE)
|
||||||
|
|
||||||
def num_push_devices_for_user(user_profile):
|
def num_push_devices_for_user(user_profile, kind = None):
|
||||||
return AppleDeviceToken.objects.filter(user=user_profile).count()
|
if kind is None:
|
||||||
|
return PushDeviceToken.objects.filter(user=user_profile).count()
|
||||||
|
else:
|
||||||
|
return PushDeviceToken.objects.filter(user=user_profile, kind=kind).count()
|
||||||
|
|
||||||
# We store the token as b64, but apns-client wants hex strings
|
# We store the token as b64, but apns-client wants hex strings
|
||||||
def b64_to_hex(data):
|
def b64_to_hex(data):
|
||||||
|
@ -34,9 +37,9 @@ def hex_to_b64(data):
|
||||||
def send_apple_push_notification(user, alert, **extra_data):
|
def send_apple_push_notification(user, alert, **extra_data):
|
||||||
if not connection:
|
if not connection:
|
||||||
logging.error("Attempting to send push notification, but no connection was found. This may be because we could not find the APNS Certificate file.")
|
logging.error("Attempting to send push notification, but no connection was found. This may be because we could not find the APNS Certificate file.")
|
||||||
# Sends a push notifications to all the PushClients
|
|
||||||
# Only Apple Push Notifications clients are supported at the moment
|
tokens = [b64_to_hex(device.token) for device in
|
||||||
tokens = [b64_to_hex(device.token) for device in AppleDeviceToken.objects.filter(user=user)]
|
PushDeviceToken.objects.filter(user=user, kind=PushDeviceToken.APNS)]
|
||||||
|
|
||||||
logging.info("Sending apple push notification to devices: %s" % (tokens,))
|
logging.info("Sending apple push notification to devices: %s" % (tokens,))
|
||||||
message = Message(tokens, alert=alert, **extra_data)
|
message = Message(tokens, alert=alert, **extra_data)
|
||||||
|
@ -69,5 +72,5 @@ def check_apns_feedback():
|
||||||
since_date = timestamp_to_datetime(since)
|
since_date = timestamp_to_datetime(since)
|
||||||
logging.info("Found unavailable token %s, unavailable since %s" % (token, since_date))
|
logging.info("Found unavailable token %s, unavailable since %s" % (token, since_date))
|
||||||
|
|
||||||
AppleDeviceToken.objects.filter(token=hex_to_b64(token), last_updates__lt=since_date).delete()
|
PushDeviceToken.objects.filter(token=hex_to_b64(token), last_updates__lt=since_date, type=PushDeviceToken.APNS).delete()
|
||||||
logging.info("Finished checking feedback for stale tokens")
|
logging.info("Finished checking feedback for stale tokens")
|
||||||
|
|
|
@ -0,0 +1,254 @@
|
||||||
|
# -*- 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
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
# Adding model 'PushDeviceToken'
|
||||||
|
db.create_table(u'zerver_pushdevicetoken', (
|
||||||
|
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||||
|
('kind', self.gf('django.db.models.fields.PositiveSmallIntegerField')()),
|
||||||
|
('token', self.gf('django.db.models.fields.CharField')(unique=True, max_length=4096)),
|
||||||
|
('last_updated', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, auto_now=True, blank=True)),
|
||||||
|
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['zerver.UserProfile'])),
|
||||||
|
))
|
||||||
|
db.send_create_signal(u'zerver', ['PushDeviceToken'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
# Deleting model 'PushDeviceToken'
|
||||||
|
db.delete_table(u'zerver_pushdevicetoken')
|
||||||
|
|
||||||
|
|
||||||
|
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'}),
|
||||||
|
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'}),
|
||||||
|
'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'}),
|
||||||
|
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True'}),
|
||||||
|
'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'})
|
||||||
|
},
|
||||||
|
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'}),
|
||||||
|
'email_token': ('django.db.models.fields.CharField', [], {'default': "'9d826bcc3765637684689b002ef9cf45'", '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'}),
|
||||||
|
'color': ('django.db.models.fields.CharField', [], {'default': "'#c2c2c2'", 'max_length': '10'}),
|
||||||
|
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'}),
|
||||||
|
'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'}),
|
||||||
|
'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'}),
|
||||||
|
'enter_sends': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', '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_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'}),
|
||||||
|
'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']
|
|
@ -350,6 +350,8 @@ class PreregistrationUser(models.Model):
|
||||||
|
|
||||||
realm = models.ForeignKey(Realm, null=True)
|
realm = models.ForeignKey(Realm, null=True)
|
||||||
|
|
||||||
|
# Deprecated. Drop this table once prod uses PushDeviceToken and the data has
|
||||||
|
# been copied there.
|
||||||
class AppleDeviceToken(models.Model):
|
class AppleDeviceToken(models.Model):
|
||||||
# The token is a unique device-specific token that is
|
# The token is a unique device-specific token that is
|
||||||
# sent to us from each iOS device, after registering with
|
# sent to us from each iOS device, after registering with
|
||||||
|
@ -360,6 +362,27 @@ class AppleDeviceToken(models.Model):
|
||||||
# The user who's device this is
|
# The user who's device this is
|
||||||
user = models.ForeignKey(UserProfile, db_index=True)
|
user = models.ForeignKey(UserProfile, db_index=True)
|
||||||
|
|
||||||
|
class PushDeviceToken(models.Model):
|
||||||
|
APNS = 1
|
||||||
|
GCM = 2
|
||||||
|
|
||||||
|
KINDS = (
|
||||||
|
(APNS, 'apns'),
|
||||||
|
(GCM, 'gcm'),
|
||||||
|
)
|
||||||
|
|
||||||
|
kind = models.PositiveSmallIntegerField(choices=KINDS)
|
||||||
|
|
||||||
|
# The token is a unique device-specific token that is
|
||||||
|
# sent to us from each device:
|
||||||
|
# - APNS token if kind == APNS
|
||||||
|
# - GCM registration id if kind == GCM
|
||||||
|
token = models.CharField(max_length=4096, unique=True)
|
||||||
|
last_updated = models.DateTimeField(auto_now=True, default=timezone.now)
|
||||||
|
|
||||||
|
# The user who's device this is
|
||||||
|
user = models.ForeignKey(UserProfile, db_index=True)
|
||||||
|
|
||||||
class MitUser(models.Model):
|
class MitUser(models.Model):
|
||||||
email = models.EmailField(unique=True)
|
email = models.EmailField(unique=True)
|
||||||
# status: whether an object has been confirmed.
|
# status: whether an object has been confirmed.
|
||||||
|
|
|
@ -18,12 +18,12 @@ from django.core.mail import send_mail, mail_admins, EmailMessage
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from zerver.models import Message, UserProfile, Stream, Subscription, \
|
from zerver.models import Message, UserProfile, Stream, Subscription, \
|
||||||
Recipient, Realm, UserMessage, bulk_get_recipients, \
|
Recipient, Realm, UserMessage, bulk_get_recipients, \
|
||||||
PreregistrationUser, get_client, MitUser, UserActivity, \
|
PreregistrationUser, get_client, MitUser, UserActivity, PushDeviceToken, \
|
||||||
get_stream, bulk_get_streams, UserPresence, \
|
get_stream, bulk_get_streams, UserPresence, \
|
||||||
get_recipient, valid_stream_name, to_dict_cache_key_id, \
|
get_recipient, valid_stream_name, to_dict_cache_key_id, \
|
||||||
extract_message_dict, stringify_message_dict, parse_usermessage_flags, \
|
extract_message_dict, stringify_message_dict, parse_usermessage_flags, \
|
||||||
split_email_to_domain, resolve_email_to_domain, email_to_username, get_realm, completely_open, \
|
split_email_to_domain, resolve_email_to_domain, email_to_username, get_realm, completely_open, \
|
||||||
is_super_user, AppleDeviceToken, get_active_user_dicts_in_realm, remote_user_to_email
|
is_super_user, get_active_user_dicts_in_realm, remote_user_to_email
|
||||||
from zerver.lib.actions import bulk_remove_subscriptions, \
|
from zerver.lib.actions import bulk_remove_subscriptions, \
|
||||||
do_change_password, create_mirror_user_if_needed, compute_irc_user_fullname, \
|
do_change_password, create_mirror_user_if_needed, compute_irc_user_fullname, \
|
||||||
compute_jabber_user_fullname, do_change_full_name, \
|
compute_jabber_user_fullname, do_change_full_name, \
|
||||||
|
@ -2377,14 +2377,16 @@ def json_set_muted_topics(request, user_profile,
|
||||||
do_set_muted_topics(user_profile, muted_topics)
|
do_set_muted_topics(user_profile, muted_topics)
|
||||||
return json_success()
|
return json_success()
|
||||||
|
|
||||||
@has_request_variables
|
def add_push_device_token(request, user_profile, token, kind):
|
||||||
def add_apns_device_token(request, user_profile, token=REQ):
|
if token == '' or len(token) > 4096:
|
||||||
if token == '' or len(token) > 255:
|
return json_error('Empty or invalid length token')
|
||||||
return json_error('Empty or invalid length APNS token')
|
|
||||||
|
|
||||||
# The iOS app receives the token on each startup, so overwrite with our
|
# If another user was previously logged in on the same device and didn't
|
||||||
# latest value
|
# properly log out, the token will still be registered to the wrong account
|
||||||
token, created = AppleDeviceToken.objects.get_or_create(user=user_profile, token=token)
|
PushDeviceToken.objects.filter(token=token).delete()
|
||||||
|
|
||||||
|
# Overwrite with the latest value
|
||||||
|
token, created = PushDeviceToken.objects.get_or_create(user=user_profile, token=token, kind=kind)
|
||||||
if not created:
|
if not created:
|
||||||
token.last_updated = now()
|
token.last_updated = now()
|
||||||
token.save(update_fields=['last_updated'])
|
token.save(update_fields=['last_updated'])
|
||||||
|
@ -2392,18 +2394,25 @@ def add_apns_device_token(request, user_profile, token=REQ):
|
||||||
return json_success()
|
return json_success()
|
||||||
|
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def remove_apns_device_token(request, user_profile, token=REQ):
|
def add_apns_device_token(request, user_profile, token=REQ):
|
||||||
if token == '' or len(token) > 255:
|
return add_push_device_token(request, user_profile, token, PushDeviceToken.APNS)
|
||||||
return json_error('Empty or invalid length APNS token')
|
|
||||||
|
def remove_push_device_token(request, user_profile, token, kind):
|
||||||
|
if token == '' or len(token) > 4096:
|
||||||
|
return json_error('Empty or invalid length token')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
apns_token = AppleDeviceToken.objects.get(token=token)
|
token = PushDeviceToken.objects.get(token=token, kind=kind)
|
||||||
apns_token.delete()
|
token.delete()
|
||||||
except AppleDeviceToken.DoesNotExist:
|
except PushDeviceToken.DoesNotExist:
|
||||||
return json_error("APNS token does not exist")
|
return json_error("Token does not exist")
|
||||||
|
|
||||||
return json_success()
|
return json_success()
|
||||||
|
|
||||||
|
@has_request_variables
|
||||||
|
def remove_apns_device_token(request, user_profile, token=REQ):
|
||||||
|
return remove_push_device_token(request, user_profile, token, PushDeviceToken.APNS)
|
||||||
|
|
||||||
def generate_204(request):
|
def generate_204(request):
|
||||||
return HttpResponse(content=None, status=204)
|
return HttpResponse(content=None, status=204)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue