Skip to content

Commit

Permalink
Terms of Service: Add ability to update TOS and have users re-sign.
Browse files Browse the repository at this point in the history
Most directly useful for the migration to zulipchat.com.

Creates a new field in UserProfile to store the tos_version, as well as two
new settings TOS_VERSION and FIRST_TIME_TOS_TEMPLATE. We check for a version
mismatch between what the user has signed and the current
settings.TOS_VERSION whenever the user hits the home page, and redirect them
if needed.

Note that accounts_accept_terms.html and zerver.views.accounts_accept_terms
were unused before this commit (I'm guessing they are from the Dropbox
migration in 2014.)
  • Loading branch information
rishig committed Aug 12, 2016
1 parent 7736925 commit 3408bd0
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 41 deletions.
35 changes: 10 additions & 25 deletions templates/zerver/accounts_accept_terms.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
{% extends "zerver/portico_signup.html" %}
{#
Allow the user to accept the terms, creating an email record of that fact.
Allow the user to accept a TOS, creating an email record of that fact.
Users only hit this page if they are coming from a migration or other update of the TOS;
the registration flow has its own (nearly identical) copy of the fields below in register.html.
#}

{% block for_you %}for {% if company_name %} {{company_name}} {% else %} __________ {% endif %} {% endblock %}
{% block portico_content %}

<p>({{ _("Welcome! We think you'll like it here") }}.)</p>


{% if special_message_template %}
{% include special_message_template %}
{% else %}
<div class="pitch">
<hr/>
<p>{{ _("You're almost there. We just need you to do one last thing") }}.</p>
<h3>{{ _("Accept the Zulip terms of service") }}</h3>
<p>{{ _("There is a new terms of service.") }}</p>
<h3>{{ _("Accept the terms of service") }}</h3>
</div>
{% endif %}

<form method="post" class="form-horizontal" id="registration" action="{{ url('zerver.views.accounts_accept_terms') }}">
{{ csrf_input }}
Expand All @@ -23,19 +25,6 @@ <h3>{{ _("Accept the Zulip terms of service") }}</h3>
<p>{{ email }}</p>
</div>
</div>
<div class="control-group">
<label for="id_full_name" class="control-label">{{ _("Your name") }}</label>
<div class="controls">
<input id="id_full_name" class="required" type="text" name="full_name"
value="{% if form.full_name.value() %}{{ form.full_name.value() }}{% endif %}"
maxlength="100" />
{% if form.full_name.errors %}
{% for error in form.full_name.errors %}
<div class="alert alert-error">{{ error }}</div>
{% endfor %}
{% endif %}
</div>
</div>

<div class="control-group">
<div class="controls">
Expand All @@ -62,14 +51,10 @@ <h3>{{ _("Accept the Zulip terms of service") }}</h3>
<br />
<div class="control-group">
<div class="controls">
<input type="submit" class="btn btn-primary" value="Register" /><br />
<input type="submit" class="btn btn-primary" value="Enter" /><br />
<input type="hidden" name="next" value="{{ next }}" />
</div>
</div>
</form>

<script type="text/javascript">
autofocus('#id_full_name');
</script>

{% endblock %}
7 changes: 7 additions & 0 deletions templates/zerver/zulipchat_migration_tos.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<p>({{ _("Welcome! We think you'll like it here") }}.)</p>
<hr/>

<div class="pitch">
<p>{{ _("Your organization has been migrated from zulip.com to zulipchat.com! New service, same great conversation. We just need you to do one last thing.") }}</p>
<h3>{{ _("Accept the Zulip terms of service") }}</h3>
</div>
1 change: 0 additions & 1 deletion zerver/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class RegistrationForm(forms.Form):
terms = forms.BooleanField(required=True)

class ToSForm(forms.Form):
full_name = forms.CharField(max_length=100)
terms = forms.BooleanField(required=True)

class HomepageForm(forms.Form):
Expand Down
9 changes: 9 additions & 0 deletions zerver/lib/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,15 @@ def do_change_full_name(user_profile, full_name, log=True):
send_event(dict(type='realm_bot', op='update', bot=payload),
bot_owner_userids(user_profile))

def do_change_tos_version(user_profile, tos_version, log=True):
# type: (UserProfile, text_type, bool) -> None
user_profile.tos_version = tos_version
user_profile.save(update_fields=["tos_version"])
if log:
log_event({'type': 'user_change_tos_version',
'user': user_profile.email,
'tos_version': tos_version})

def do_regenerate_api_key(user_profile, log=True):
# type: (UserProfile, bool) -> None
user_profile.api_key = random_api_key()
Expand Down
19 changes: 19 additions & 0 deletions zerver/migrations/0028_userprofile_tos_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('zerver', '0027_realm_default_language'),
]

operations = [
migrations.AddField(
model_name='userprofile',
name='tos_version',
field=models.CharField(max_length=10, null=True),
),
]
8 changes: 8 additions & 0 deletions zerver/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ class UserProfile(ModelReprMixin, AbstractBaseUser, PermissionsMixin):
last_pointer_updater = models.CharField(max_length=64) # type: text_type
realm = models.ForeignKey(Realm) # type: Realm
api_key = models.CharField(max_length=32) # type: text_type
tos_version = models.CharField(null=True, max_length=10) # type: text_type

### Notifications settings. ###

Expand Down Expand Up @@ -511,6 +512,13 @@ def can_create_streams(self):
else:
return False

def major_tos_version(self):
# type: () -> int
if self.tos_version is not None:
return int(self.tos_version.split('.')[0])
else:
return -1

def receives_offline_notifications(user_profile):
# type: (UserProfile) -> bool
return ((user_profile.enable_offline_email_notifications or
Expand Down
31 changes: 16 additions & 15 deletions zerver/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
do_activate_user, do_create_user, do_create_realm, set_default_streams, \
internal_send_message, update_user_presence, do_events_register, \
do_change_enable_offline_email_notifications, \
do_change_enable_digest_emails, \
do_change_enable_digest_emails, do_change_tos_version, \
get_default_subs, user_email_is_unique, do_invite_users, do_refer_friend, \
compute_mit_user_fullname, do_set_muted_topics, clear_followup_emails_queue, \
do_update_pointer, realm_user_count
Expand Down Expand Up @@ -223,6 +223,7 @@ def accounts_register(request):
user_profile = do_create_user(email, password, realm, full_name, short_name,
prereg_user=prereg_user,
newsletter_data={"IP": request.META['REMOTE_ADDR']})
do_change_tos_version(user_profile, settings.TOS_VERSION)

# This logs you in using the ZulipDummyBackend, since honestly nothing
# more fancy than this is required.
Expand Down Expand Up @@ -252,27 +253,22 @@ def accounts_register(request):
@zulip_login_required
def accounts_accept_terms(request):
# type: (HttpRequest) -> HttpResponse
email = request.user.email
domain = resolve_email_to_domain(email)
if request.method == "POST":
form = ToSForm(request.POST)
if form.is_valid():
full_name = form.cleaned_data['full_name']
send_mail('Terms acceptance for ' + full_name,
loader.render_to_string('zerver/tos_accept_body.txt',
{'name': full_name,
'email': email,
'ip': request.META['REMOTE_ADDR'],
'browser': request.META.get('HTTP_USER_AGENT', "Unspecified")}),
settings.EMAIL_HOST_USER,
["all@zulip.com"])
do_change_full_name(request.user, full_name)
do_change_tos_version(request.user, settings.TOS_VERSION)
return redirect(home)

else:
form = ToSForm()

email = request.user.email
domain = resolve_email_to_domain(email)
special_message_template = None
if request.user.tos_version is None and hasattr(settings, 'FIRST_TIME_TOS_TEMPLATE'):
special_message_template = 'zerver/' + settings.FIRST_TIME_TOS_TEMPLATE
return render_to_response('zerver/accounts_accept_terms.html',
{ 'form': form, 'company_name': domain, 'email': email },
{ 'form': form, 'company_name': domain, 'email': email, \
'special_message_template' : special_message_template },
request=request)

from zerver.lib.ccache import make_ccache
Expand Down Expand Up @@ -837,6 +833,11 @@ def home(request):
request._email = request.user.email
request.client = get_client("website")

# If a user hasn't signed the current Terms of Service, send them there
if settings.TERMS_OF_SERVICE is not None and settings.TOS_VERSION is not None and \
int(settings.TOS_VERSION.split('.')[0]) > user_profile.major_tos_version():
return accounts_accept_terms(request)

narrow = [] # type: List[List[text_type]]
narrow_stream = None
narrow_topic = request.GET.get("topic")
Expand Down
5 changes: 5 additions & 0 deletions zproject/local_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@

# Terms of Service
CUSTOM_TOS = '/etc/zulip/terms.md'
# Major version number (the stuff before the first '.') has to be an integer.
# Users will be asked to re-sign the TOS only when the major version number increases.
# A TOS_VERSION of None has a major version number of -1.
# TOS_VERSION = '0.0'
# FIRST_TIME_TOS_TEMPLATE = 'zulipchat_migration_tos.html'

# Legacy zulip.com bucket used for old-style S3 uploads.
S3_BUCKET="humbug-user-uploads"
Expand Down
2 changes: 2 additions & 0 deletions zproject/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ def get_secret(key):
'DEFAULT_NEW_REALM_STREAMS': ["social", "general", "zulip"],
'REALM_CREATION_LINK_VALIDITY_DAYS': 7,
'TERMS_OF_SERVICE': None,
'TOS_VERSION': None,
'FIRST_TIME_TOS_TEMPLATE': None
}

for setting_name, setting_val in six.iteritems(DEFAULT_SETTINGS):
Expand Down

0 comments on commit 3408bd0

Please sign in to comment.