Delete the old zulip.com "referrals" system.

This system hasn't been in active use for several years, and had some 
problems with it's design.  So it makes sense to just remove it to declutter
the codebase.

Fixes #5655.
This commit is contained in:
Vaida 2017-07-07 23:59:18 +02:00 committed by Tim Abbott
parent fcd944447e
commit d5517bae36
24 changed files with 34 additions and 432 deletions

View File

@ -95,7 +95,6 @@
"realm_icon": false, "realm_icon": false,
"feature_flags": false, "feature_flags": false,
"search_suggestion": false, "search_suggestion": false,
"referral": false,
"notifications": false, "notifications": false,
"message_flags": false, "message_flags": false,
"bot_data": false, "bot_data": false,

View File

@ -278,14 +278,6 @@ var event_fixtures = {
}, },
}, },
referral: {
type: 'referral',
referrals: {
granted: 10,
used: 5,
},
},
restart: { restart: {
type: 'restart', type: 'restart',
immediate: true, immediate: true,
@ -659,18 +651,6 @@ with_overrides(function (override) {
}); });
}); });
with_overrides(function (override) {
// referral
var event = event_fixtures.referral;
global.with_stub(function (stub) {
override('referral.update_state', stub.f);
dispatch(event);
var args = stub.get_args('granted', 'used');
assert_same(args.granted, event.referrals.granted);
assert_same(args.used, event.referrals.used);
});
});
with_overrides(function (override) { with_overrides(function (override) {
// restart // restart
var event = event_fixtures.restart; var event = event_fixtures.restart;

View File

@ -1,122 +0,0 @@
var referral = (function () {
var exports = {};
var placeholder_invitees = ['nikola.tesla@example.com',
'sam.morse@example.com',
'c.shannon@example.com',
'hedy.lamarr@example.com',
'grace.hopper@example.com',
'ada.lovelace@example.com'];
var last_granted;
var last_used;
var ever_had_invites = false;
exports.update_state = function (granted, used) {
if (last_granted === granted && last_used === used) {
return;
}
last_granted = granted;
last_used = used;
if (granted <= 0 || !page_params.share_the_love) {
$("#share-the-love").hide();
} else {
$("#referral-form input").attr('placeholder', _.shuffle(placeholder_invitees).pop());
$("#invite-hearts").empty();
var i;
for (i = 0; i < used; i += 1) {
$("#invite-hearts").append($('<i class="icon-vector-heart"> </i>'));
}
var invites_left = Math.max(0, granted - used);
for (i = 0; i < invites_left; i += 1) {
$("#invite-hearts").append($('<i class="icon-vector-heart-empty"> </i>'));
}
var invites_left_text = i18n.t('__count__ invite remaining', {count: invites_left});
$(".invite-count").text(invites_left_text);
if (invites_left > 0) {
ever_had_invites = true;
$(".still-have-invites").show();
$(".no-more-invites").hide();
} else {
$(".still-have-invites").hide();
$("#referral-form input").blur();
if (ever_had_invites) {
$(".no-more-invites").show();
}
}
if (used > 0) {
$("#encouraging-invite-message").hide();
}
$("#share-the-love").show();
}
resize.resize_page_components();
};
function show_and_fade_elem(elem) {
elem.stop();
elem.css({opacity: 100});
elem.show().delay(4000).fadeOut(1000, ui.resize_page_components);
}
$(function () {
var validator = $("#referral-form").validate({
errorClass: 'text-error',
submitHandler: function () {
channel.post({
url: "/json/refer_friend",
data: { email: $("#referral-form input").val() },
error: function () {
// We ignore errors from the server because
// they're unlikely and we'll get an email either
// way
},
});
show_and_fade_elem($("#tell-a-friend-success"));
$("#referral-form input").val('');
exports.update_state(last_granted, last_used + 1);
},
success: function () {
resize.resize_page_components();
},
showErrors: function () {
this.defaultShowErrors();
resize.resize_page_components();
},
});
$("#referral-form input").on('blur', function () {
if ($("#referral-form input").val() === '') {
validator.resetForm();
resize.resize_page_components();
}
});
$("#referral-form").on("click", function (e) {
e.stopPropagation();
});
$("#share-the-love-expand-collapse").click(function (e) {
$("#share-the-love-contents").toggle();
$("#share-the-love-expand-collapse .toggle").toggleClass('icon-vector-caret-right icon-vector-caret-down');
resize.resize_page_components();
e.stopPropagation();
});
exports.update_state(page_params.referrals.granted, page_params.referrals.used);
});
return exports;
}());
if (typeof module !== 'undefined') {
module.exports = referral;
}

View File

@ -66,12 +66,6 @@ function get_new_heights() {
- $("#streams_header").outerHeight(true) - $("#streams_header").outerHeight(true)
- 10; // stream_filters margin-bottom - 10; // stream_filters margin-bottom
if ($("#share-the-love").is(":visible")) {
res.stream_filters_max_height -=
$("#share-the-love").outerHeight(true)
+ 20; // share-the-love margins + 10px of ??
}
// Don't let us crush the stream sidebar completely out of view // Don't let us crush the stream sidebar completely out of view
res.stream_filters_max_height = Math.max(80, res.stream_filters_max_height); res.stream_filters_max_height = Math.max(80, res.stream_filters_max_height);
@ -239,7 +233,6 @@ exports.resize_page_components = function () {
sidebar = $(".bottom_sidebar").expectOne(); sidebar = $(".bottom_sidebar").expectOne();
sidebar.append($("#user-list").expectOne()); sidebar.append($("#user-list").expectOne());
sidebar.append($("#group-pm-list").expectOne()); sidebar.append($("#group-pm-list").expectOne());
sidebar.append($("#share-the-love").expectOne());
$("#user_presences").css("margin", "0px"); $("#user_presences").css("margin", "0px");
$("#group-pms").css("margin", "0px"); $("#group-pms").css("margin", "0px");
$("#userlist-toggle").css("display", "none"); $("#userlist-toggle").css("display", "none");

View File

@ -169,10 +169,6 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) {
} }
break; break;
case 'referral':
referral.update_state(event.referrals.granted, event.referrals.used);
break;
case 'stream': case 'stream':
if (event.op === 'update') { if (event.op === 'update') {
// Legacy: Stream properties are still managed by subs.js on the client side. // Legacy: Stream properties are still managed by subs.js on the client side.

View File

@ -410,8 +410,7 @@ li.expanded_private_message a {
margin-bottom: 20px; margin-bottom: 20px;
} }
#topics_header, #topics_header {
#sharethelove-header {
border-top: 1px solid hsl(0, 0%, 88%); border-top: 1px solid hsl(0, 0%, 88%);
margin-top: 5px; margin-top: 5px;
margin-right: 10px; margin-right: 10px;
@ -430,76 +429,6 @@ li.expanded_private_message a {
display: none; display: none;
} }
#share-the-love {
margin-left: 0px;
margin-right: 0px;
margin-bottom: 5px;
line-height: 18px;
display: none;
}
#share-the-love-contents {
display: none;
}
#share-the-love-expand-collapse {
position: relative;
cursor: pointer;
}
#share-the-love-expand-collapse h4 {
padding-left: 1em;
}
#share-the-love-expand-collapse .toggle {
position: absolute;
left: 0px;
top: 50%;
margin-top: -8px;
}
#share-the-love input,
#share-the-love p {
margin-top: 5px;
margin-bottom: 5px;
}
#referral-form label {
margin: 0;
}
#share-the-love .icon-vector-heart {
color: red;
}
#share-the-love .still-have-invites {
clear: both;
margin-right: 10px;
}
#share-the-love .no-more-invites {
clear: both;
display: none;
margin-right: 10px;
}
#share-the-love .invite-count-area {
margin-right: 10px;
}
#share-the-love .alert {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
#referral-form {
margin-bottom: 0;
}
#tell-a-friend-success {
display: none;
}
li.show-more-topics a { li.show-more-topics a {
font-size: 75%; font-size: 75%;
} }

View File

@ -478,8 +478,6 @@ paths:
* `realm_waiting_period_threshold`: * `realm_waiting_period_threshold`:
* `referrals`:
* `streams`: * `streams`:
* `twenty_four_hour_time`: * `twenty_four_hour_time`:
@ -1015,8 +1013,6 @@ definitions:
realm_waiting_period_threshold: realm_waiting_period_threshold:
type: integer type: integer
format: int64 format: int64
referrals:
type: string
streams: streams:
type: array type: array
items: items:
@ -1089,7 +1085,6 @@ definitions:
#realm_domains #realm_domains
#realm_emoji #realm_emoji
#realm_filters #realm_filters
#referrals
#subscriptions #subscriptions
#unsubscribed #unsubscribed

View File

@ -73,43 +73,5 @@
<ul id="stream_filters" class="filters"></ul> <ul id="stream_filters" class="filters"></ul>
</div> </div>
</div> </div>
<div id="share-the-love">
<div id="share-the-love-expand-collapse">
<i class="toggle icon-vector-caret-right"></i><div id="sharethelove-header"><h4 class="sidebar-title">{{ _('SHARE THE LOVE') }}<span class="still-have-invites"> (<span class="invite-count">0</span>)</span></h4></div>
</div>
<div id="share-the-love-contents">
<div id="tell-a-friend-success" class="alert alert-success">
{% trans %}<strong>Thanks!</strong> A hand-crafted, artisanal invite is on the way.{% endtrans %}
</div>
<div class="still-have-invites" id="encouraging-invite-message">
<p>
{{ _("Know someone who would love Zulip for their company or group? Invite 'em!") }}
</p>
</div>
<div class="no-more-invites">
<p>
{% trans %}
We'll have more invites for
you soon, but for now, enjoy
this <a target="_blank"
href="http://www.youtube.com/watch?v=PW71En5Pa5s#t=2m01s">song
that expresses how we feel when you're
logged out</a>.
{% endtrans %}
</p>
</div>
<div class="still-have-invites">
{# Many of these values are set by the initialization code in referral.js #}
<form id="referral-form">
<input class="input-block-level required" type="email" name="email" />
<label for="email" generated="true" class="text-error"></label>
</form>
</div>
<div class="invite-count-area">
<span id="invite-hearts"></span>
<small class="pull-right"><span class="invite-count">0 {{ _('invites remaining') }}</span></small>
</div>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -127,7 +127,6 @@ def find_edges_to_remove(graph, methods):
('pm_list', 'resize'), ('pm_list', 'resize'),
('notifications', 'navigate'), ('notifications', 'navigate'),
('compose', 'socket'), ('compose', 'socket'),
('referral', 'resize'),
('stream_muting', 'message_util'), ('stream_muting', 'message_util'),
('subs', 'stream_list'), ('subs', 'stream_list'),
('ui', 'message_fetch'), ('ui', 'message_fetch'),

View File

@ -34,7 +34,7 @@ from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity,
RealmDomain, \ RealmDomain, \
Subscription, Recipient, Message, Attachment, UserMessage, RealmAuditLog, \ Subscription, Recipient, Message, Attachment, UserMessage, RealmAuditLog, \
UserHotspot, \ UserHotspot, \
Client, DefaultStream, UserPresence, Referral, PushDeviceToken, \ Client, DefaultStream, UserPresence, PushDeviceToken, \
MAX_SUBJECT_LENGTH, \ 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, \
@ -3155,33 +3155,6 @@ def do_invite_users(user_profile, invitee_emails, streams, body=None):
return ret_error, ret_error_data return ret_error, ret_error_data
def send_referral_event(user_profile):
# type: (UserProfile) -> None
event = dict(type="referral",
referrals=dict(granted=user_profile.invites_granted,
used=user_profile.invites_used))
send_event(event, [user_profile.id])
def do_refer_friend(user_profile, email):
# type: (UserProfile, Text) -> None
content = ('Referrer: "%s" <%s>\n'
'Realm: %s\n'
'Referred: %s') % (user_profile.full_name, user_profile.email,
user_profile.realm.string_id, email)
subject = "Zulip referral: %s" % (email,)
from_email = '"%s" <%s>' % (user_profile.full_name, 'referrals@zulip.com')
to_email = '"Zulip Referrals" <zulip+referrals@zulip.com>'
headers = {'Reply-To': '"%s" <%s>' % (user_profile.full_name, user_profile.email,)}
msg = EmailMessage(subject, content, from_email, [to_email], headers=headers)
msg.send()
referral = Referral(user_profile=user_profile, email=email)
referral.save()
user_profile.invites_used += 1
user_profile.save(update_fields=['invites_used'])
send_referral_event(user_profile)
def notify_realm_emoji(realm): def notify_realm_emoji(realm):
# type: (Realm) -> None # type: (Realm) -> None
event = dict(type="realm_emoji", op="update", event = dict(type="realm_emoji", op="update",

View File

@ -144,10 +144,6 @@ def fetch_initial_state_data(user_profile, event_types, queue_id,
if want('realm_bot'): if want('realm_bot'):
state['realm_bots'] = get_owned_bot_dicts(user_profile) state['realm_bots'] = get_owned_bot_dicts(user_profile)
if want('referral'):
state['referrals'] = {'granted': user_profile.invites_granted,
'used': user_profile.invites_used}
if want('subscription'): if want('subscription'):
subscriptions, unsubscribed, never_subscribed = gather_subscriptions_helper( subscriptions, unsubscribed, never_subscribed = gather_subscriptions_helper(
user_profile, include_subscribers=include_subscribers) user_profile, include_subscribers=include_subscribers)
@ -415,8 +411,6 @@ def apply_event(state, event, user_profile, include_subscribers):
elif event['type'] == "reaction": elif event['type'] == "reaction":
# The client will get the message with the reactions directly # The client will get the message with the reactions directly
pass pass
elif event['type'] == "referral":
state['referrals'] = event['referrals']
elif event['type'] == 'typing': elif event['type'] == 'typing':
# Typing notification events are transient and thus ignored # Typing notification events are transient and thus ignored
pass pass

View File

@ -77,7 +77,6 @@ ALL_ZERVER_TABLES = [
'zerver_realmemoji', 'zerver_realmemoji',
'zerver_realmfilter', 'zerver_realmfilter',
'zerver_recipient', 'zerver_recipient',
'zerver_referral',
'zerver_scheduledjob', 'zerver_scheduledjob',
'zerver_stream', 'zerver_stream',
'zerver_subscription', 'zerver_subscription',
@ -97,7 +96,6 @@ NON_EXPORTED_TABLES = [
'zerver_preregistrationuser', 'zerver_preregistrationuser',
'zerver_preregistrationuser_streams', 'zerver_preregistrationuser_streams',
'zerver_pushdevicetoken', 'zerver_pushdevicetoken',
'zerver_referral',
'zerver_scheduledjob', 'zerver_scheduledjob',
'zerver_userprofile_groups', 'zerver_userprofile_groups',
'zerver_userprofile_user_permissions', 'zerver_userprofile_user_permissions',

View File

@ -36,7 +36,6 @@ class Command(BaseCommand):
* Users' passwords and API keys (users will need to use SSO or reset password) * Users' passwords and API keys (users will need to use SSO or reset password)
* Mobile tokens for APNS/GCM (users will need to reconnect their mobile devices) * Mobile tokens for APNS/GCM (users will need to reconnect their mobile devices)
* ScheduledJob (Not relevant on a new server) * ScheduledJob (Not relevant on a new server)
* Referral (Unused)
* Deployment (Unused) * Deployment (Unused)
* third_party_api_results cache (this means rerending all old * third_party_api_results cache (this means rerending all old
messages could be expensive) messages could be expensive)

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-07-07 08:34
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('zerver', '0087_remove_old_scheduled_jobs'),
]
operations = [
migrations.RemoveField(
model_name='referral',
name='user_profile',
),
migrations.RemoveField(
model_name='userprofile',
name='invites_granted',
),
migrations.RemoveField(
model_name='userprofile',
name='invites_used',
),
migrations.DeleteModel(
name='Referral',
),
]

View File

@ -647,9 +647,6 @@ class UserProfile(ModelReprMixin, AbstractBaseUser, PermissionsMixin):
# completed. # completed.
onboarding_steps = models.TextField(default=u'[]') # type: Text onboarding_steps = models.TextField(default=u'[]') # type: Text
invites_granted = models.IntegerField(default=0) # type: int
invites_used = models.IntegerField(default=0) # type: int
alert_words = models.TextField(default=u'[]') # type: Text # json-serialized list of strings alert_words = models.TextField(default=u'[]') # type: Text # json-serialized list of strings
# Contains serialized JSON of the form: # Contains serialized JSON of the form:
@ -1677,11 +1674,6 @@ class DefaultStream(models.Model):
class Meta(object): class Meta(object):
unique_together = ("realm", "stream") unique_together = ("realm", "stream")
class Referral(models.Model):
user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) # type: UserProfile
email = models.EmailField(blank=False, null=False) # type: Text
timestamp = models.DateTimeField(auto_now_add=True, null=False) # type: datetime.datetime
class ScheduledJob(models.Model): class ScheduledJob(models.Model):
scheduled_timestamp = models.DateTimeField(auto_now_add=False, null=False) # type: datetime.datetime scheduled_timestamp = models.DateTimeField(auto_now_add=False, null=False) # type: datetime.datetime
type = models.PositiveSmallIntegerField() # type: int type = models.PositiveSmallIntegerField() # type: int

View File

@ -46,7 +46,6 @@ from zerver.lib.actions import (
do_delete_message, do_delete_message,
do_mark_hotspot_as_read, do_mark_hotspot_as_read,
do_reactivate_user, do_reactivate_user,
do_refer_friend,
do_regenerate_api_key, do_regenerate_api_key,
do_remove_alert_words, do_remove_alert_words,
do_remove_default_stream, do_remove_default_stream,
@ -723,19 +722,6 @@ class EventsRegisterTest(ZulipTestCase):
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_referral_events(self):
# type: () -> None
schema_checker = self.check_events_dict([
('type', equals('referral')),
('referrals', check_dict_only([
('granted', check_int),
('used', check_int),
])),
])
events = self.do_test(lambda: do_refer_friend(self.user_profile, "friend@example.com"))
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
def test_register_events(self): def test_register_events(self):
# type: () -> None # type: () -> None
realm_user_add_checker = self.check_events_dict([ realm_user_add_checker = self.check_events_dict([

View File

@ -31,7 +31,6 @@ class HomeTest(ZulipTestCase):
'Manage streams', 'Manage streams',
'Narrow by topic', 'Narrow by topic',
'Next message', 'Next message',
'SHARE THE LOVE',
'Search streams', 'Search streams',
'Welcome to Zulip', 'Welcome to Zulip',
'pygments.css', 'pygments.css',
@ -130,13 +129,11 @@ class HomeTest(ZulipTestCase):
"realm_uri", "realm_uri",
"realm_users", "realm_users",
"realm_waiting_period_threshold", "realm_waiting_period_threshold",
"referrals",
"save_stacktraces", "save_stacktraces",
"server_generation", "server_generation",
"server_inline_image_preview", "server_inline_image_preview",
"server_inline_url_embed_preview", "server_inline_url_embed_preview",
"server_uri", "server_uri",
"share_the_love",
"subscriptions", "subscriptions",
"test_suite", "test_suite",
"timezone", "timezone",

View File

@ -27,7 +27,7 @@ from zerver.models import (
get_unique_open_realm, get_unique_non_system_realm, get_unique_open_realm, get_unique_non_system_realm,
completely_open, get_recipient, completely_open, get_recipient,
PreregistrationUser, Realm, RealmDomain, Recipient, Message, PreregistrationUser, Realm, RealmDomain, Recipient, Message,
Referral, ScheduledJob, UserProfile, UserMessage, ScheduledJob, UserProfile, UserMessage,
Stream, Subscription, ScheduledJob, flush_per_request_caches Stream, Subscription, ScheduledJob, flush_per_request_caches
) )
from zerver.lib.actions import ( from zerver.lib.actions import (
@ -675,55 +675,6 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
self.assert_json_success(self.invite(invitee, [stream_name])) self.assert_json_success(self.invite(invitee, [stream_name]))
def test_refer_friend(self):
# type: () -> None
self.login(self.example_email("hamlet"))
user = self.example_user('hamlet')
user.invites_granted = 1
user.invites_used = 0
user.save()
invitee = "alice-test@zulip.com"
result = self.client_post('/json/refer_friend', dict(email=invitee))
self.assert_json_success(result)
# verify this works
Referral.objects.get(user_profile=user, email=invitee)
user = self.example_user('hamlet')
self.assertEqual(user.invites_used, 1)
def test_refer_friend_no_email(self):
# type: () -> None
self.login(self.example_email("hamlet"))
user = self.example_user('hamlet')
user.invites_granted = 1
user.invites_used = 0
user.save()
self.assert_json_error(
self.client_post('/json/refer_friend', dict(email='')),
"No email address specified")
user = self.example_user('hamlet')
self.assertEqual(user.invites_used, 0)
def test_refer_friend_no_invites(self):
# type: () -> None
self.login(self.example_email("hamlet"))
user = self.example_user('hamlet')
user.invites_granted = 1
user.invites_used = 1
user.save()
invitee = "alice-test@zulip.com"
self.assert_json_error(
self.client_post('/json/refer_friend', dict(email=invitee)),
"Insufficient invites")
user = self.example_user('hamlet')
self.assertEqual(user.invites_used, 1)
def test_invitation_reminder_email(self): def test_invitation_reminder_email(self):
# type: () -> None # type: () -> None
from django.core.mail import outbox from django.core.mail import outbox

View File

@ -184,7 +184,6 @@ def home_real(request):
# These end up in a global JavaScript Object named 'page_params'. # These end up in a global JavaScript Object named 'page_params'.
page_params = dict( page_params = dict(
# Server settings. # Server settings.
share_the_love = settings.SHARE_THE_LOVE,
development_environment = settings.DEVELOPMENT, development_environment = settings.DEVELOPMENT,
debug_mode = settings.DEBUG, debug_mode = settings.DEBUG,
test_suite = settings.TEST_SUITE, test_suite = settings.TEST_SUITE,

View File

@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _
from typing import List, Optional, Set, Text from typing import List, Optional, Set, Text
from zerver.decorator import authenticated_json_post_view from zerver.decorator import authenticated_json_post_view
from zerver.lib.actions import do_invite_users, do_refer_friend, \ from zerver.lib.actions import do_invite_users, \
get_default_subs, internal_send_message get_default_subs, internal_send_message
from zerver.lib.request import REQ, has_request_variables, JsonableError from zerver.lib.request import REQ, has_request_variables, JsonableError
from zerver.lib.response import json_success, json_error from zerver.lib.response import json_success, json_error
@ -67,16 +67,3 @@ def get_invitee_emails_set(invitee_emails_raw):
email = is_email_with_name.group('email') email = is_email_with_name.group('email')
invitee_emails.add(email.strip()) invitee_emails.add(email.strip())
return invitee_emails return invitee_emails
@authenticated_json_post_view
@has_request_variables
def json_refer_friend(request, user_profile, email=REQ()):
# type: (HttpRequest, UserProfile, str) -> HttpResponse
if not email:
return json_error(_("No email address specified"))
if user_profile.invites_granted - user_profile.invites_used <= 0:
return json_error(_("Insufficient invites"))
do_refer_friend(user_profile, email)
return json_success()

View File

@ -1,31 +0,0 @@
from __future__ import absolute_import
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.actions import send_referral_event
from zerver.models import get_user_profile_by_email
class Command(BaseCommand):
help = """Grants a user invites and resets the number of invites they've used."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('email', metavar='<email>', type=str,
help="user to grant invites to")
parser.add_argument('num_invites', metavar='<num invites>', type=int,
help="number of invites to grant")
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
email = options['email']
num_invites = options['num_invites']
user_profile = get_user_profile_by_email(email)
user_profile.invites_granted = num_invites
user_profile.invites_used = 0
user_profile.save(update_fields=['invites_granted', 'invites_used'])
send_referral_event(user_profile)

View File

@ -15,7 +15,6 @@ import zerver.views.muting
legacy_urls = [ legacy_urls = [
# These are json format views used by the web client. They require a logged in browser. # These are json format views used by the web client. They require a logged in browser.
url(r'^json/invite_users$', zerver.views.invite.json_invite_users), url(r'^json/invite_users$', zerver.views.invite.json_invite_users),
url(r'^json/refer_friend$', zerver.views.invite.json_refer_friend),
url(r'^json/settings/change$', zerver.views.user_settings.json_change_settings), url(r'^json/settings/change$', zerver.views.user_settings.json_change_settings),
# We should remove this endpoint and all code related to it. # We should remove this endpoint and all code related to it.

View File

@ -29,7 +29,6 @@ if not ZULIP_COM:
raise Exception("You should create your own local settings from prod_settings_template.") raise Exception("You should create your own local settings from prod_settings_template.")
ZULIP_FRIENDS_LIST_ID = '84b2f3da6b' ZULIP_FRIENDS_LIST_ID = '84b2f3da6b'
SHARE_THE_LOVE = True
SHOW_OSS_ANNOUNCEMENT = True SHOW_OSS_ANNOUNCEMENT = True
REGISTER_LINK_DISABLED = True REGISTER_LINK_DISABLED = True
CUSTOM_LOGO_URL = "/static/images/logo/zulip-dropbox.png" CUSTOM_LOGO_URL = "/static/images/logo/zulip-dropbox.png"

View File

@ -153,7 +153,6 @@ DEFAULT_SETTINGS = {'TWITTER_CONSUMER_KEY': '',
'FEEDBACK_BOT': 'feedback@zulip.com', 'FEEDBACK_BOT': 'feedback@zulip.com',
'FEEDBACK_BOT_NAME': 'Zulip Feedback Bot', 'FEEDBACK_BOT_NAME': 'Zulip Feedback Bot',
'ADMINS': '', 'ADMINS': '',
'SHARE_THE_LOVE': False,
'INLINE_IMAGE_PREVIEW': True, 'INLINE_IMAGE_PREVIEW': True,
'INLINE_URL_EMBED_PREVIEW': False, 'INLINE_URL_EMBED_PREVIEW': False,
'CAMO_URI': '', 'CAMO_URI': '',
@ -949,7 +948,6 @@ JS_SPECS = {
'js/admin.js', 'js/admin.js',
'js/tab_bar.js', 'js/tab_bar.js',
'js/emoji.js', 'js/emoji.js',
'js/referral.js',
'js/custom_markdown.js', 'js/custom_markdown.js',
'js/bot_data.js', 'js/bot_data.js',
'js/reactions.js', 'js/reactions.js',