Move action functions from models.py to zephyr/lib/actions.py.

(imported from commit 9d577dd53ce7d4c9faf6cc8a56129d684a50811b)
This commit is contained in:
Tim Abbott 2013-01-10 16:01:33 -05:00
parent b977bce998
commit 1a82741650
13 changed files with 318 additions and 300 deletions

View File

@ -6,7 +6,8 @@ from django.utils.safestring import mark_safe
from django.contrib.auth.forms import SetPasswordForm from django.contrib.auth.forms import SetPasswordForm
from humbug import settings from humbug import settings
from zephyr.models import Realm, do_change_password from zephyr.models import Realm
from zephyr.lib.actions import do_change_password
def is_unique(value): def is_unique(value):
try: try:

View File

@ -21,9 +21,8 @@ class AdminHumbugHandler(logging.Handler):
def emit(self, record): def emit(self, record):
# We have to defer imports to avoid circular imports in settings.py. # We have to defer imports to avoid circular imports in settings.py.
from zephyr.models import Message, UserProfile, Recipient, \ from zephyr.models import Recipient
create_stream_if_needed, get_client, internal_send_message from zephyr.lib.actions import internal_send_message
from django.conf import settings
subject = '%s: %s' % (platform.node(), record.getMessage()) subject = '%s: %s' % (platform.node(), record.getMessage())
try: try:

288
zephyr/lib/actions.py Normal file
View File

@ -0,0 +1,288 @@
from django.conf import settings
from django.contrib.auth.models import User
from zephyr.lib.context_managers import lockfile
from zephyr.models import Realm, Stream, UserProfile, \
Subscription, Recipient, Message, UserMessage, \
DefaultStream, \
MAX_MESSAGE_LENGTH, get_client
from django.db import transaction, IntegrityError
from zephyr.lib.initial_password import initial_password
from zephyr.lib.cache import cache_with_key
from django.utils import timezone
from django.contrib.auth.models import UserManager
import subprocess
import simplejson
import time
import traceback
import re
import requests
import hashlib
import base64
# Store an event in the log for re-importing messages
def log_event(event):
if "timestamp" not in event:
event["timestamp"] = time.time()
with lockfile(settings.MESSAGE_LOG + '.lock'):
with open(settings.MESSAGE_LOG, 'a') as log:
log.write(simplejson.dumps(event) + '\n')
# create_user_hack is the same as Django's User.objects.create_user,
# except that we don't save to the database so it can used in
# bulk_creates
def create_user_hack(username, password, email, active):
now = timezone.now()
email = UserManager.normalize_email(email)
user = User(username=username, email=email,
is_staff=False, is_active=active, is_superuser=False,
last_login=now, date_joined=now)
if active:
user.set_password(password)
else:
user.set_unusable_password()
return user
def create_user_base(email, password, active=True):
# NB: the result of Base32 + truncation is not a valid Base32 encoding.
# It's just a unique alphanumeric string.
# Use base32 instead of base64 so we don't have to worry about mixed case.
# Django imposes a limit of 30 characters on usernames.
email_hash = hashlib.sha256(settings.HASH_SALT + email).digest()
username = base64.b32encode(email_hash)[:30]
return create_user_hack(username, password, email, active)
def create_user(email, password, realm, full_name, short_name,
active=True):
user = create_user_base(email=email, password=password,
active=active)
user.save()
return UserProfile.create(user, realm, full_name, short_name)
def do_create_user(email, password, realm, full_name, short_name,
active=True):
log_event({'type': 'user_created',
'timestamp': time.time(),
'full_name': full_name,
'short_name': short_name,
'user': email})
return create_user(email, password, realm, full_name, short_name, active)
def compute_mit_user_fullname(email):
try:
# Input is either e.g. starnine@mit.edu or user|CROSSREALM.INVALID@mit.edu
match_user = re.match(r'^([a-zA-Z0-9_.-]+)(\|.+)?@mit\.edu$', email.lower())
if match_user and match_user.group(2) is None:
dns_query = "%s.passwd.ns.athena.mit.edu" % (match_user.group(1),)
proc = subprocess.Popen(['host', '-t', 'TXT', dns_query],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, _err_unused = proc.communicate()
if proc.returncode == 0:
# Parse e.g. 'starnine:*:84233:101:Athena Consulting Exchange User,,,:/mit/starnine:/bin/bash'
# for the 4th passwd entry field, aka the person's name.
hesiod_name = out.split(':')[4].split(',')[0].strip()
if hesiod_name == "":
return email
return hesiod_name
elif match_user:
return match_user.group(1).lower() + "@" + match_user.group(2).upper()[1:]
except:
print ("Error getting fullname for %s:" % (email,))
traceback.print_exc()
return email.lower()
def create_mit_user_if_needed(realm, email):
try:
return UserProfile.objects.get(user__email=email)
except UserProfile.DoesNotExist:
try:
# Forge a user for this person
return create_user(email, initial_password(email), realm,
compute_mit_user_fullname(email), email.split("@")[0],
active=False)
except IntegrityError:
# Unless we raced with another thread doing the same
# thing, in which case we should get the user they made
transaction.commit()
return UserProfile.objects.get(user__email=email)
def log_message(message):
if not message.sending_client.name.startswith("test:"):
log_event(message.to_log_dict())
user_hash = {}
def get_user_profile_by_id(uid):
if uid in user_hash:
return user_hash[uid]
return UserProfile.objects.select_related().get(id=uid)
def do_send_message(message, no_log=False):
# Log the message to our message log for populate_db to refill
if not no_log:
log_message(message)
if message.recipient.type == Recipient.PERSONAL:
recipients = list(set([get_user_profile_by_id(message.recipient.type_id),
get_user_profile_by_id(message.sender_id)]))
# For personals, you send out either 1 or 2 copies of the message, for
# personals to yourself or to someone else, respectively.
assert((len(recipients) == 1) or (len(recipients) == 2))
elif (message.recipient.type == Recipient.STREAM or
message.recipient.type == Recipient.HUDDLE):
recipients = [s.user_profile for
s in Subscription.objects.select_related().filter(recipient=message.recipient, active=True)]
else:
raise ValueError('Bad recipient type')
# Save the message receipts in the database
# TODO: Use bulk_create here
with transaction.commit_on_success():
message.save()
for user_profile in recipients:
# Only deliver messages to "active" user accounts
if user_profile.user.is_active:
UserMessage(user_profile=user_profile, message=message).save()
# We can only publish messages to longpolling clients if the Tornado server is running.
if settings.TORNADO_SERVER:
# Render Markdown etc. here, so that the single-threaded Tornado server doesn't have to.
# TODO: Reduce duplication in what we send.
rendered = { 'text/html': message.to_dict(apply_markdown=True),
'text/x-markdown': message.to_dict(apply_markdown=False) }
requests.post(settings.TORNADO_SERVER + '/notify_new_message', data=dict(
secret = settings.SHARED_SECRET,
message = message.id,
rendered = simplejson.dumps(rendered),
users = simplejson.dumps([str(user.id) for user in recipients])))
def create_stream_if_needed(realm, stream_name):
(stream, created) = Stream.objects.get_or_create(
realm=realm, name__iexact=stream_name,
defaults={'name': stream_name})
if created:
Recipient.objects.create(type_id=stream.id, type=Recipient.STREAM)
return stream
def internal_send_message(sender_email, recipient_type, recipient_name,
subject, content):
if len(content) > MAX_MESSAGE_LENGTH:
content = content[0:3900] + "\n\n[message was too long and has been truncated]"
message = Message()
message.sender = UserProfile.objects.get(user__email=sender_email)
message.recipient = Recipient.objects.get(type_id=create_stream_if_needed(
message.sender.realm, recipient_name).id, type=recipient_type)
message.subject = subject
message.content = content
message.pub_date = timezone.now()
message.sending_client = get_client("Internal")
do_send_message(message)
def do_add_subscription(user_profile, stream, no_log=False):
recipient = Recipient.objects.get(type_id=stream.id,
type=Recipient.STREAM)
(subscription, created) = Subscription.objects.get_or_create(
user_profile=user_profile, recipient=recipient,
defaults={'active': True})
did_subscribe = created
if not subscription.active:
did_subscribe = True
subscription.active = True
subscription.save()
if did_subscribe and not no_log:
log_event({'type': 'subscription_added',
'user': user_profile.user.email,
'name': stream.name,
'domain': stream.realm.domain})
return did_subscribe
def do_remove_subscription(user_profile, stream, no_log=False):
recipient = Recipient.objects.get(type_id=stream.id,
type=Recipient.STREAM)
maybe_sub = Subscription.objects.filter(user_profile=user_profile,
recipient=recipient)
if len(maybe_sub) == 0:
return False
subscription = maybe_sub[0]
did_remove = subscription.active
subscription.active = False
subscription.save()
if did_remove and not no_log:
log_event({'type': 'subscription_removed',
'user': user_profile.user.email,
'name': stream.name,
'domain': stream.realm.domain})
return did_remove
def log_subscription_property_change(user_email, property, property_dict):
event = {'type': 'subscription_property',
'property': property,
'user': user_email}
event.update(property_dict)
log_event(event)
def do_activate_user(user, log=True, join_date=timezone.now()):
user.is_active = True
user.set_password(initial_password(user.email))
user.date_joined = join_date
user.save()
if log:
log_event({'type': 'user_activated',
'user': user.email})
def do_change_password(user, password, log=True, commit=True):
user.set_password(password)
if commit:
user.save()
if log:
log_event({'type': 'user_change_password',
'user': user.email,
'pwhash': user.password})
def do_change_full_name(user_profile, full_name, log=True):
user_profile.full_name = full_name
user_profile.save()
if log:
log_event({'type': 'user_change_full_name',
'user': user_profile.user.email,
'full_name': full_name})
def do_create_realm(domain, replay=False):
realm, created = Realm.objects.get_or_create(domain=domain)
if created and not replay:
# Log the event
log_event({"type": "realm_created",
"domain": domain})
# Sent a notification message
message = Message()
message.sender = UserProfile.objects.get(user__email="humbug+signups@humbughq.com")
message.recipient = Recipient.objects.get(type_id=create_stream_if_needed(
message.sender.realm, "signups").id, type=Recipient.STREAM)
message.subject = domain
message.content = "Signups enabled."
message.pub_date = timezone.now()
message.sending_client = get_client("Internal")
do_send_message(message)
return (realm, created)
def do_change_enable_desktop_notifications(user_profile, enable_desktop_notifications, log=True):
user_profile.enable_desktop_notifications = enable_desktop_notifications
user_profile.save()
if log:
log_event({'type': 'enable_desktop_notifications_changed',
'user': user_profile.user.email,
'enable_desktop_notifications': enable_desktop_notifications})
def set_default_streams(realm, stream_names):
DefaultStream.objects.filter(realm=realm).delete()
for stream_name in stream_names:
stream = create_stream_if_needed(realm, stream_name)
DefaultStream.objects.create(stream=stream, realm=realm)
def add_default_subs(user_profile):
for default in DefaultStream.objects.filter(realm=user_profile.realm):
do_add_subscription(user_profile, default.stream)

View File

@ -1,8 +1,10 @@
from django.conf import settings from django.conf import settings
from zephyr.lib.initial_password import initial_password, initial_api_key
from zephyr.models import Realm, Stream, User, UserProfile, Huddle, \ from zephyr.models import Realm, Stream, User, UserProfile, Huddle, \
Subscription, Recipient, Client, Message, \ Subscription, Recipient, Client, Message, \
create_user_base, get_huddle_hash, initial_api_key, initial_password get_huddle_hash
from zephyr.lib.actions import create_user_base
# batch_bulk_create should become obsolete with Django 1.5, when the # batch_bulk_create should become obsolete with Django 1.5, when the
# Django bulk_create method accepts a batch_size directly. # Django bulk_create method accepts a batch_size directly.

View File

@ -2,8 +2,8 @@ from optparse import make_option
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from zephyr.models import create_stream_if_needed, do_add_subscription, Realm, \ from zephyr.lib.actions import create_stream_if_needed, do_add_subscription
User, UserProfile from zephyr.models import Realm, User, UserProfile
class Command(BaseCommand): class Command(BaseCommand):
help = """Add some or all users in a realm to a set of streams.""" help = """Add some or all users in a realm to a set of streams."""

View File

@ -1,8 +1,5 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from zephyr.models import Realm, Message, UserProfile, Recipient, create_stream_if_needed, \ from zephyr.lib.actions import do_create_realm
get_client, do_create_realm
from zephyr.views import do_send_message
from django.utils.timezone import now
class Command(BaseCommand): class Command(BaseCommand):
help = "Create a realm for the specified domain(s)." help = "Create a realm for the specified domain(s)."

View File

@ -6,8 +6,9 @@ from django.db.utils import IntegrityError
from django.utils.timezone import now from django.utils.timezone import now
from django.core import validators from django.core import validators
from zephyr.models import Realm, do_create_user from zephyr.models import Realm
from zephyr.views import do_send_message, notify_new_user from zephyr.lib.actions import do_send_message, do_create_user
from zephyr.views import notify_new_user
from zephyr.lib.initial_password import initial_password from zephyr.lib.initial_password import initial_password
class Command(BaseCommand): class Command(BaseCommand):

View File

@ -4,9 +4,10 @@ from django.utils.timezone import utc, now
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from zephyr.models import Message, UserProfile, Stream, Recipient, Client, \ from zephyr.models import Message, UserProfile, Stream, Recipient, Client, \
Subscription, Huddle, get_huddle, Realm, UserMessage, get_user_profile_by_id, \ Subscription, Huddle, get_huddle, Realm, UserMessage, StreamColor, \
do_send_message, clear_database, StreamColor, set_default_streams, \ get_huddle_hash, clear_database, get_client
get_huddle_hash, get_client, do_activate_user from zephyr.lib.actions import get_user_profile_by_id, \
do_send_message, set_default_streams, do_activate_user
from zephyr.lib.parallel import run_parallel from zephyr.lib.parallel import run_parallel
from django.db import transaction, connection from django.db import transaction, connection
from django.conf import settings from django.conf import settings

View File

@ -1,6 +1,7 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from zephyr.models import Realm, set_default_streams, log_event from zephyr.models import Realm
from zephyr.lib.actions import set_default_streams, log_event
from optparse import make_option from optparse import make_option
import sys import sys

View File

@ -1,7 +1,9 @@
from optparse import make_option from optparse import make_option
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from zephyr.models import UserProfile, compute_mit_user_fullname from zephyr.models import UserProfile
from zephyr.lib.actions import compute_mit_user_fullname
# Helper to be used with manage.py shell to fix bad names on prod. # Helper to be used with manage.py shell to fix bad names on prod.
def update_mit_fullnames(change=False): def update_mit_fullnames(change=False):
for u in UserProfile.objects.select_related().all(): for u in UserProfile.objects.select_related().all():

View File

@ -2,23 +2,14 @@ from django.db import models
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
import hashlib import hashlib
import base64
from zephyr.lib.cache import cache_with_key from zephyr.lib.cache import cache_with_key
from zephyr.lib.initial_password import initial_password, initial_api_key from zephyr.lib.initial_password import initial_api_key
import os import os
import simplejson
from django.db import transaction, IntegrityError from django.db import transaction, IntegrityError
from zephyr.lib import bugdown from zephyr.lib import bugdown
from zephyr.lib.avatar import gravatar_hash from zephyr.lib.avatar import gravatar_hash
from zephyr.lib.context_managers import lockfile
import requests
from django.contrib.auth.models import UserManager
from django.utils import timezone from django.utils import timezone
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
import time
import subprocess
import traceback
import re
from django.utils.html import escape from django.utils.html import escape
from zephyr.lib.timestamp import datetime_to_timestamp from zephyr.lib.timestamp import datetime_to_timestamp
@ -94,104 +85,6 @@ class MitUser(models.Model):
# if confirmed, set to confirmation.settings.STATUS_ACTIVE # if confirmed, set to confirmation.settings.STATUS_ACTIVE
status = models.IntegerField(default=0) status = models.IntegerField(default=0)
# create_user_hack is the same as Django's User.objects.create_user,
# except that we don't save to the database so it can used in
# bulk_creates
def create_user_hack(username, password, email, active):
now = timezone.now()
email = UserManager.normalize_email(email)
user = User(username=username, email=email,
is_staff=False, is_active=active, is_superuser=False,
last_login=now, date_joined=now)
if active:
user.set_password(password)
else:
user.set_unusable_password()
return user
def set_default_streams(realm, stream_names):
DefaultStream.objects.filter(realm=realm).delete()
for stream_name in stream_names:
stream = create_stream_if_needed(realm, stream_name)
DefaultStream.objects.create(stream=stream, realm=realm)
def add_default_subs(user_profile):
for default in DefaultStream.objects.filter(realm=user_profile.realm):
do_add_subscription(user_profile, default.stream)
def create_user_base(email, password, active=True):
# NB: the result of Base32 + truncation is not a valid Base32 encoding.
# It's just a unique alphanumeric string.
# Use base32 instead of base64 so we don't have to worry about mixed case.
# Django imposes a limit of 30 characters on usernames.
email_hash = hashlib.sha256(settings.HASH_SALT + email).digest()
username = base64.b32encode(email_hash)[:30]
return create_user_hack(username, password, email, active)
def create_user(email, password, realm, full_name, short_name,
active=True):
user = create_user_base(email=email, password=password,
active=active)
user.save()
return UserProfile.create(user, realm, full_name, short_name)
def do_create_user(email, password, realm, full_name, short_name,
active=True):
log_event({'type': 'user_created',
'timestamp': time.time(),
'full_name': full_name,
'short_name': short_name,
'user': email})
return create_user(email, password, realm, full_name, short_name, active)
def compute_mit_user_fullname(email):
try:
# Input is either e.g. starnine@mit.edu or user|CROSSREALM.INVALID@mit.edu
match_user = re.match(r'^([a-zA-Z0-9_.-]+)(\|.+)?@mit\.edu$', email.lower())
if match_user and match_user.group(2) is None:
dns_query = "%s.passwd.ns.athena.mit.edu" % (match_user.group(1),)
proc = subprocess.Popen(['host', '-t', 'TXT', dns_query],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, _err_unused = proc.communicate()
if proc.returncode == 0:
# Parse e.g. 'starnine:*:84233:101:Athena Consulting Exchange User,,,:/mit/starnine:/bin/bash'
# for the 4th passwd entry field, aka the person's name.
hesiod_name = out.split(':')[4].split(',')[0].strip()
if hesiod_name == "":
return email
return hesiod_name
elif match_user:
return match_user.group(1).lower() + "@" + match_user.group(2).upper()[1:]
except:
print ("Error getting fullname for %s:" % (email,))
traceback.print_exc()
return email.lower()
def create_mit_user_if_needed(realm, email):
try:
return UserProfile.objects.get(user__email=email)
except UserProfile.DoesNotExist:
try:
# Forge a user for this person
return create_user(email, initial_password(email), realm,
compute_mit_user_fullname(email), email.split("@")[0],
active=False)
except IntegrityError:
# Unless we raced with another thread doing the same
# thing, in which case we should get the user they made
transaction.commit()
return UserProfile.objects.get(user__email=email)
def create_stream_if_needed(realm, stream_name):
(stream, created) = Stream.objects.get_or_create(
realm=realm, name__iexact=stream_name,
defaults={'name': stream_name})
if created:
Recipient.objects.create(type_id=stream.id, type=Recipient.STREAM)
return stream
class Stream(models.Model): class Stream(models.Model):
name = models.CharField(max_length=30, db_index=True) name = models.CharField(max_length=30, db_index=True)
realm = models.ForeignKey(Realm, db_index=True) realm = models.ForeignKey(Realm, db_index=True)
@ -362,78 +255,6 @@ class UserMessage(models.Model):
display_recipient = get_display_recipient(self.message.recipient) display_recipient = get_display_recipient(self.message.recipient)
return "<UserMessage: %s / %s>" % (display_recipient, self.user_profile.user.email) return "<UserMessage: %s / %s>" % (display_recipient, self.user_profile.user.email)
user_hash = {}
def get_user_profile_by_id(uid):
if uid in user_hash:
return user_hash[uid]
return UserProfile.objects.select_related().get(id=uid)
# Store an event in the log for re-importing messages
def log_event(event):
if "timestamp" not in event:
event["timestamp"] = time.time()
with lockfile(settings.MESSAGE_LOG + '.lock'):
with open(settings.MESSAGE_LOG, 'a') as log:
log.write(simplejson.dumps(event) + '\n')
def log_message(message):
if not message.sending_client.name.startswith("test:"):
log_event(message.to_log_dict())
def do_send_message(message, no_log=False):
# Log the message to our message log for populate_db to refill
if not no_log:
log_message(message)
if message.recipient.type == Recipient.PERSONAL:
recipients = list(set([get_user_profile_by_id(message.recipient.type_id),
get_user_profile_by_id(message.sender_id)]))
# For personals, you send out either 1 or 2 copies of the message, for
# personals to yourself or to someone else, respectively.
assert((len(recipients) == 1) or (len(recipients) == 2))
elif (message.recipient.type == Recipient.STREAM or
message.recipient.type == Recipient.HUDDLE):
recipients = [s.user_profile for
s in Subscription.objects.select_related().filter(recipient=message.recipient, active=True)]
else:
raise ValueError('Bad recipient type')
# Save the message receipts in the database
# TODO: Use bulk_create here
with transaction.commit_on_success():
message.save()
for user_profile in recipients:
# Only deliver messages to "active" user accounts
if user_profile.user.is_active:
UserMessage(user_profile=user_profile, message=message).save()
# We can only publish messages to longpolling clients if the Tornado server is running.
if settings.TORNADO_SERVER:
# Render Markdown etc. here, so that the single-threaded Tornado server doesn't have to.
# TODO: Reduce duplication in what we send.
rendered = { 'text/html': message.to_dict(apply_markdown=True),
'text/x-markdown': message.to_dict(apply_markdown=False) }
requests.post(settings.TORNADO_SERVER + '/notify_new_message', data=dict(
secret = settings.SHARED_SECRET,
message = message.id,
rendered = simplejson.dumps(rendered),
users = simplejson.dumps([str(user.id) for user in recipients])))
def internal_send_message(sender_email, recipient_type, recipient_name,
subject, content):
if len(content) > MAX_MESSAGE_LENGTH:
content = content[0:3900] + "\n\n[message was too long and has been truncated]"
message = Message()
message.sender = UserProfile.objects.get(user__email=sender_email)
message.recipient = Recipient.objects.get(type_id=create_stream_if_needed(
message.sender.realm, recipient_name).id, type=recipient_type)
message.subject = subject
message.content = content
message.pub_date = timezone.now()
message.sending_client = get_client("Internal")
do_send_message(message)
class Subscription(models.Model): class Subscription(models.Model):
user_profile = models.ForeignKey(UserProfile) user_profile = models.ForeignKey(UserProfile)
recipient = models.ForeignKey(Recipient) recipient = models.ForeignKey(Recipient)
@ -447,103 +268,6 @@ class Subscription(models.Model):
def __str__(self): def __str__(self):
return self.__repr__() return self.__repr__()
def do_add_subscription(user_profile, stream, no_log=False):
recipient = Recipient.objects.get(type_id=stream.id,
type=Recipient.STREAM)
(subscription, created) = Subscription.objects.get_or_create(
user_profile=user_profile, recipient=recipient,
defaults={'active': True})
did_subscribe = created
if not subscription.active:
did_subscribe = True
subscription.active = True
subscription.save()
if did_subscribe and not no_log:
log_event({'type': 'subscription_added',
'user': user_profile.user.email,
'name': stream.name,
'domain': stream.realm.domain})
return did_subscribe
def do_remove_subscription(user_profile, stream, no_log=False):
recipient = Recipient.objects.get(type_id=stream.id,
type=Recipient.STREAM)
maybe_sub = Subscription.objects.filter(user_profile=user_profile,
recipient=recipient)
if len(maybe_sub) == 0:
return False
subscription = maybe_sub[0]
did_remove = subscription.active
subscription.active = False
subscription.save()
if did_remove and not no_log:
log_event({'type': 'subscription_removed',
'user': user_profile.user.email,
'name': stream.name,
'domain': stream.realm.domain})
return did_remove
def log_subscription_property_change(user_email, property, property_dict):
event = {'type': 'subscription_property',
'property': property,
'user': user_email}
event.update(property_dict)
log_event(event)
def do_activate_user(user, log=True, join_date=timezone.now()):
user.is_active = True
user.set_password(initial_password(user.email))
user.date_joined = join_date
user.save()
if log:
log_event({'type': 'user_activated',
'user': user.email})
def do_change_password(user, password, log=True, commit=True):
user.set_password(password)
if commit:
user.save()
if log:
log_event({'type': 'user_change_password',
'user': user.email,
'pwhash': user.password})
def do_change_full_name(user_profile, full_name, log=True):
user_profile.full_name = full_name
user_profile.save()
if log:
log_event({'type': 'user_change_full_name',
'user': user_profile.user.email,
'full_name': full_name})
def do_create_realm(domain, replay=False):
realm, created = Realm.objects.get_or_create(domain=domain)
if created and not replay:
# Log the event
log_event({"type": "realm_created",
"domain": domain})
# Sent a notification message
message = Message()
message.sender = UserProfile.objects.get(user__email="humbug+signups@humbughq.com")
message.recipient = Recipient.objects.get(type_id=create_stream_if_needed(
message.sender.realm, "signups").id, type=Recipient.STREAM)
message.subject = domain
message.content = "Signups enabled."
message.pub_date = timezone.now()
message.sending_client = get_client("Internal")
do_send_message(message)
return (realm, created)
def do_change_enable_desktop_notifications(user_profile, enable_desktop_notifications, log=True):
user_profile.enable_desktop_notifications = enable_desktop_notifications
user_profile.save()
if log:
log_event({'type': 'enable_desktop_notifications_changed',
'user': user_profile.user.email,
'enable_desktop_notifications': enable_desktop_notifications})
class Huddle(models.Model): class Huddle(models.Model):
# TODO: We should consider whether using # TODO: We should consider whether using
# CommaSeparatedIntegerField would be better. # CommaSeparatedIntegerField would be better.

View File

@ -5,11 +5,12 @@ from django.utils.timezone import now
from django.db.models import Q from django.db.models import Q
from zephyr.models import Message, UserProfile, Stream, Recipient, Subscription, \ from zephyr.models import Message, UserProfile, Stream, Recipient, Subscription, \
filter_by_subscriptions, get_display_recipient, Realm, do_send_message, Client filter_by_subscriptions, get_display_recipient, Realm, Client
from zephyr.tornadoviews import json_get_updates, api_get_messages from zephyr.tornadoviews import json_get_updates, api_get_messages
from zephyr.views import gather_subscriptions from zephyr.views import gather_subscriptions
from zephyr.decorator import RespondAsynchronously, RequestVariableConversionError from zephyr.decorator import RespondAsynchronously, RequestVariableConversionError
from zephyr.lib.initial_password import initial_password, initial_api_key from zephyr.lib.initial_password import initial_password, initial_api_key
from zephyr.lib.actions import do_send_message
import simplejson import simplejson
import subprocess import subprocess

View File

@ -12,13 +12,14 @@ from django.db.models import Q
from django.core.mail import send_mail from django.core.mail import send_mail
from zephyr.models import Message, UserProfile, Stream, Subscription, \ from zephyr.models import Message, UserProfile, Stream, Subscription, \
Recipient, get_display_recipient, get_huddle, Realm, UserMessage, \ Recipient, get_display_recipient, get_huddle, Realm, UserMessage, \
do_add_subscription, do_remove_subscription, do_change_password, \ StreamColor, PreregistrationUser, get_client, MitUser, User, UserActivity, \
MAX_SUBJECT_LENGTH, MAX_MESSAGE_LENGTH
from zephyr.lib.actions import do_add_subscription, do_remove_subscription, \
do_change_password, create_mit_user_if_needed, \
do_change_full_name, do_change_enable_desktop_notifications, \ do_change_full_name, do_change_enable_desktop_notifications, \
do_activate_user, add_default_subs, do_create_user, do_send_message, \ do_activate_user, add_default_subs, do_create_user, do_send_message, \
create_mit_user_if_needed, create_stream_if_needed, StreamColor, \
PreregistrationUser, get_client, MitUser, User, UserActivity, \
log_subscription_property_change, internal_send_message, \ log_subscription_property_change, internal_send_message, \
MAX_SUBJECT_LENGTH, MAX_MESSAGE_LENGTH create_stream_if_needed
from zephyr.forms import RegistrationForm, HomepageForm, ToSForm, is_unique, \ from zephyr.forms import RegistrationForm, HomepageForm, ToSForm, is_unique, \
is_active is_active
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt