diff --git a/.eslintrc.json b/.eslintrc.json index db35a472f2..15056240e0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -95,7 +95,6 @@ "realm_icon": false, "feature_flags": false, "search_suggestion": false, - "referral": false, "notifications": false, "message_flags": false, "bot_data": false, diff --git a/frontend_tests/node_tests/dispatch.js b/frontend_tests/node_tests/dispatch.js index 94db0eb11c..69f9709f79 100644 --- a/frontend_tests/node_tests/dispatch.js +++ b/frontend_tests/node_tests/dispatch.js @@ -278,14 +278,6 @@ var event_fixtures = { }, }, - referral: { - type: 'referral', - referrals: { - granted: 10, - used: 5, - }, - }, - restart: { type: 'restart', 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) { // restart var event = event_fixtures.restart; diff --git a/static/js/referral.js b/static/js/referral.js deleted file mode 100644 index 24d56e6ace..0000000000 --- a/static/js/referral.js +++ /dev/null @@ -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($(' ')); - } - - var invites_left = Math.max(0, granted - used); - for (i = 0; i < invites_left; i += 1) { - $("#invite-hearts").append($(' ')); - } - - 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; -} diff --git a/static/js/resize.js b/static/js/resize.js index 4ce7bb11af..f40d6a9fe0 100644 --- a/static/js/resize.js +++ b/static/js/resize.js @@ -66,12 +66,6 @@ function get_new_heights() { - $("#streams_header").outerHeight(true) - 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 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.append($("#user-list").expectOne()); sidebar.append($("#group-pm-list").expectOne()); - sidebar.append($("#share-the-love").expectOne()); $("#user_presences").css("margin", "0px"); $("#group-pms").css("margin", "0px"); $("#userlist-toggle").css("display", "none"); diff --git a/static/js/server_events_dispatch.js b/static/js/server_events_dispatch.js index 0bab9301d8..5a503374f6 100644 --- a/static/js/server_events_dispatch.js +++ b/static/js/server_events_dispatch.js @@ -169,10 +169,6 @@ exports.dispatch_normal_event = function dispatch_normal_event(event) { } break; - case 'referral': - referral.update_state(event.referrals.granted, event.referrals.used); - break; - case 'stream': if (event.op === 'update') { // Legacy: Stream properties are still managed by subs.js on the client side. diff --git a/static/styles/left-sidebar.css b/static/styles/left-sidebar.css index 20cb202178..f86d052a52 100644 --- a/static/styles/left-sidebar.css +++ b/static/styles/left-sidebar.css @@ -410,8 +410,7 @@ li.expanded_private_message a { margin-bottom: 20px; } -#topics_header, -#sharethelove-header { +#topics_header { border-top: 1px solid hsl(0, 0%, 88%); margin-top: 5px; margin-right: 10px; @@ -430,76 +429,6 @@ li.expanded_private_message a { 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 { font-size: 75%; } diff --git a/static/swagger/zulip.yaml b/static/swagger/zulip.yaml index dfa058f71f..04301e72b8 100644 --- a/static/swagger/zulip.yaml +++ b/static/swagger/zulip.yaml @@ -478,8 +478,6 @@ paths: * `realm_waiting_period_threshold`: - * `referrals`: - * `streams`: * `twenty_four_hour_time`: @@ -1015,8 +1013,6 @@ definitions: realm_waiting_period_threshold: type: integer format: int64 - referrals: - type: string streams: type: array items: @@ -1089,7 +1085,6 @@ definitions: #realm_domains #realm_emoji #realm_filters - #referrals #subscriptions #unsubscribed diff --git a/templates/zerver/left_sidebar.html b/templates/zerver/left_sidebar.html index ba93f1bbcb..8fc4fe7495 100644 --- a/templates/zerver/left_sidebar.html +++ b/templates/zerver/left_sidebar.html @@ -73,43 +73,5 @@ -
-
-
-
-
-
- {% trans %}Thanks! A hand-crafted, artisanal invite is on the way.{% endtrans %} -
-
-

- {{ _("Know someone who would love Zulip for their company or group? Invite 'em!") }} -

-
-
-

- {% trans %} - We'll have more invites for - you soon, but for now, enjoy - this song - that expresses how we feel when you're - logged out. - {% endtrans %} -

-
-
- {# Many of these values are set by the initialization code in referral.js #} -
- - -
-
-
- - 0 {{ _('invites remaining') }} -
-
-
diff --git a/tools/js-dep-visualizer.py b/tools/js-dep-visualizer.py index 722a4fbadf..eacb909c2d 100644 --- a/tools/js-dep-visualizer.py +++ b/tools/js-dep-visualizer.py @@ -127,7 +127,6 @@ def find_edges_to_remove(graph, methods): ('pm_list', 'resize'), ('notifications', 'navigate'), ('compose', 'socket'), - ('referral', 'resize'), ('stream_muting', 'message_util'), ('subs', 'stream_list'), ('ui', 'message_fetch'), diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index b89f3594f6..ffa8a5c182 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -34,7 +34,7 @@ from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, RealmDomain, \ Subscription, Recipient, Message, Attachment, UserMessage, RealmAuditLog, \ UserHotspot, \ - Client, DefaultStream, UserPresence, Referral, PushDeviceToken, \ + Client, DefaultStream, UserPresence, PushDeviceToken, \ MAX_SUBJECT_LENGTH, \ MAX_MESSAGE_LENGTH, get_client, get_stream, get_recipient, get_huddle, \ 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 -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" ' - 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): # type: (Realm) -> None event = dict(type="realm_emoji", op="update", diff --git a/zerver/lib/events.py b/zerver/lib/events.py index 7c5ea0cbf3..ac4c1b7203 100644 --- a/zerver/lib/events.py +++ b/zerver/lib/events.py @@ -144,10 +144,6 @@ def fetch_initial_state_data(user_profile, event_types, queue_id, if want('realm_bot'): 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'): subscriptions, unsubscribed, never_subscribed = gather_subscriptions_helper( user_profile, include_subscribers=include_subscribers) @@ -415,8 +411,6 @@ def apply_event(state, event, user_profile, include_subscribers): elif event['type'] == "reaction": # The client will get the message with the reactions directly pass - elif event['type'] == "referral": - state['referrals'] = event['referrals'] elif event['type'] == 'typing': # Typing notification events are transient and thus ignored pass diff --git a/zerver/lib/export.py b/zerver/lib/export.py index 0e41154c79..d86fcd51a4 100644 --- a/zerver/lib/export.py +++ b/zerver/lib/export.py @@ -77,7 +77,6 @@ ALL_ZERVER_TABLES = [ 'zerver_realmemoji', 'zerver_realmfilter', 'zerver_recipient', - 'zerver_referral', 'zerver_scheduledjob', 'zerver_stream', 'zerver_subscription', @@ -97,7 +96,6 @@ NON_EXPORTED_TABLES = [ 'zerver_preregistrationuser', 'zerver_preregistrationuser_streams', 'zerver_pushdevicetoken', - 'zerver_referral', 'zerver_scheduledjob', 'zerver_userprofile_groups', 'zerver_userprofile_user_permissions', diff --git a/zerver/management/commands/export.py b/zerver/management/commands/export.py index 9a26e73a68..38474c3dd3 100644 --- a/zerver/management/commands/export.py +++ b/zerver/management/commands/export.py @@ -36,7 +36,6 @@ class Command(BaseCommand): * 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) * ScheduledJob (Not relevant on a new server) - * Referral (Unused) * Deployment (Unused) * third_party_api_results cache (this means rerending all old messages could be expensive) diff --git a/zerver/migrations/0088_remove_referral_and_invites.py b/zerver/migrations/0088_remove_referral_and_invites.py new file mode 100644 index 0000000000..2d0ffdb27b --- /dev/null +++ b/zerver/migrations/0088_remove_referral_and_invites.py @@ -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', + ), + ] diff --git a/zerver/models.py b/zerver/models.py index 74afb8f79d..13cfb770f4 100644 --- a/zerver/models.py +++ b/zerver/models.py @@ -647,9 +647,6 @@ class UserProfile(ModelReprMixin, AbstractBaseUser, PermissionsMixin): # completed. 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 # Contains serialized JSON of the form: @@ -1677,11 +1674,6 @@ class DefaultStream(models.Model): class Meta(object): 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): scheduled_timestamp = models.DateTimeField(auto_now_add=False, null=False) # type: datetime.datetime type = models.PositiveSmallIntegerField() # type: int diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py index 1fc3aab44e..07b34936cb 100644 --- a/zerver/tests/test_events.py +++ b/zerver/tests/test_events.py @@ -46,7 +46,6 @@ from zerver.lib.actions import ( do_delete_message, do_mark_hotspot_as_read, do_reactivate_user, - do_refer_friend, do_regenerate_api_key, do_remove_alert_words, do_remove_default_stream, @@ -723,19 +722,6 @@ class EventsRegisterTest(ZulipTestCase): error = schema_checker('events[0]', events[0]) 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): # type: () -> None realm_user_add_checker = self.check_events_dict([ diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py index 672482a071..c24f8d19a4 100644 --- a/zerver/tests/test_home.py +++ b/zerver/tests/test_home.py @@ -31,7 +31,6 @@ class HomeTest(ZulipTestCase): 'Manage streams', 'Narrow by topic', 'Next message', - 'SHARE THE LOVE', 'Search streams', 'Welcome to Zulip', 'pygments.css', @@ -130,13 +129,11 @@ class HomeTest(ZulipTestCase): "realm_uri", "realm_users", "realm_waiting_period_threshold", - "referrals", "save_stacktraces", "server_generation", "server_inline_image_preview", "server_inline_url_embed_preview", "server_uri", - "share_the_love", "subscriptions", "test_suite", "timezone", diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py index 4e499f7f52..141e193375 100644 --- a/zerver/tests/test_signup.py +++ b/zerver/tests/test_signup.py @@ -27,7 +27,7 @@ from zerver.models import ( get_unique_open_realm, get_unique_non_system_realm, completely_open, get_recipient, PreregistrationUser, Realm, RealmDomain, Recipient, Message, - Referral, ScheduledJob, UserProfile, UserMessage, + ScheduledJob, UserProfile, UserMessage, Stream, Subscription, ScheduledJob, flush_per_request_caches ) 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])) - 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): # type: () -> None from django.core.mail import outbox diff --git a/zerver/views/home.py b/zerver/views/home.py index 11d8dc9fd8..97f96c2770 100644 --- a/zerver/views/home.py +++ b/zerver/views/home.py @@ -184,7 +184,6 @@ def home_real(request): # These end up in a global JavaScript Object named 'page_params'. page_params = dict( # Server settings. - share_the_love = settings.SHARE_THE_LOVE, development_environment = settings.DEVELOPMENT, debug_mode = settings.DEBUG, test_suite = settings.TEST_SUITE, diff --git a/zerver/views/invite.py b/zerver/views/invite.py index 5df65ecd5c..5220c68925 100644 --- a/zerver/views/invite.py +++ b/zerver/views/invite.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _ from typing import List, Optional, Set, Text 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 from zerver.lib.request import REQ, has_request_variables, JsonableError 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') invitee_emails.add(email.strip()) 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() diff --git a/zilencer/management/commands/grant_invites.py b/zilencer/management/commands/grant_invites.py deleted file mode 100644 index 59d3e17b3d..0000000000 --- a/zilencer/management/commands/grant_invites.py +++ /dev/null @@ -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='', type=str, - help="user to grant invites to") - parser.add_argument('num_invites', metavar='', 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) diff --git a/zproject/legacy_urls.py b/zproject/legacy_urls.py index 373f336a76..9c9b9a207e 100644 --- a/zproject/legacy_urls.py +++ b/zproject/legacy_urls.py @@ -15,7 +15,6 @@ import zerver.views.muting legacy_urls = [ # 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/refer_friend$', zerver.views.invite.json_refer_friend), url(r'^json/settings/change$', zerver.views.user_settings.json_change_settings), # We should remove this endpoint and all code related to it. diff --git a/zproject/local_settings.py b/zproject/local_settings.py index cf8cac3746..b278562b39 100644 --- a/zproject/local_settings.py +++ b/zproject/local_settings.py @@ -29,7 +29,6 @@ if not ZULIP_COM: raise Exception("You should create your own local settings from prod_settings_template.") ZULIP_FRIENDS_LIST_ID = '84b2f3da6b' -SHARE_THE_LOVE = True SHOW_OSS_ANNOUNCEMENT = True REGISTER_LINK_DISABLED = True CUSTOM_LOGO_URL = "/static/images/logo/zulip-dropbox.png" diff --git a/zproject/settings.py b/zproject/settings.py index f7c374b4f3..6d6399d4d6 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -153,7 +153,6 @@ DEFAULT_SETTINGS = {'TWITTER_CONSUMER_KEY': '', 'FEEDBACK_BOT': 'feedback@zulip.com', 'FEEDBACK_BOT_NAME': 'Zulip Feedback Bot', 'ADMINS': '', - 'SHARE_THE_LOVE': False, 'INLINE_IMAGE_PREVIEW': True, 'INLINE_URL_EMBED_PREVIEW': False, 'CAMO_URI': '', @@ -949,7 +948,6 @@ JS_SPECS = { 'js/admin.js', 'js/tab_bar.js', 'js/emoji.js', - 'js/referral.js', 'js/custom_markdown.js', 'js/bot_data.js', 'js/reactions.js',