mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	Terms of Service: Add ability to update TOS and have users re-sign.
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
(they date from c327446537)
			
			
This commit is contained in:
		@@ -1,19 +1,21 @@
 | 
				
			|||||||
{% extends "zerver/portico_signup.html" %}
 | 
					{% 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 for_you %}for {% if company_name %} {{company_name}} {% else %} __________ {% endif %} {% endblock %}
 | 
				
			||||||
{% block portico_content %}
 | 
					{% 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">
 | 
					<div class="pitch">
 | 
				
			||||||
    <hr/>
 | 
					      <p>{{ _("There is a new terms of service.") }}</p>
 | 
				
			||||||
    <p>{{ _("You're almost there. We just need you to do one last thing") }}.</p>
 | 
					      <h3>{{ _("Accept the terms of service") }}</h3>
 | 
				
			||||||
    <h3>{{ _("Accept the Zulip terms of service") }}</h3>
 | 
					 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<form method="post" class="form-horizontal" id="registration" action="{{ url('zerver.views.accounts_accept_terms') }}">
 | 
					<form method="post" class="form-horizontal" id="registration" action="{{ url('zerver.views.accounts_accept_terms') }}">
 | 
				
			||||||
{{ csrf_input }}
 | 
					{{ csrf_input }}
 | 
				
			||||||
@@ -23,19 +25,6 @@ Allow the user to accept the terms, creating an email record of that fact.
 | 
				
			|||||||
            <p>{{ email }}</p>
 | 
					            <p>{{ email }}</p>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </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="control-group">
 | 
				
			||||||
        <div class="controls">
 | 
					        <div class="controls">
 | 
				
			||||||
@@ -62,14 +51,10 @@ Allow the user to accept the terms, creating an email record of that fact.
 | 
				
			|||||||
    <br />
 | 
					    <br />
 | 
				
			||||||
    <div class="control-group">
 | 
					    <div class="control-group">
 | 
				
			||||||
        <div class="controls">
 | 
					        <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 }}" />
 | 
					            <input type="hidden" name="next" value="{{ next }}" />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</form>
 | 
					</form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script type="text/javascript">
 | 
					 | 
				
			||||||
autofocus('#id_full_name');
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +0,0 @@
 | 
				
			|||||||
{#
 | 
					 | 
				
			||||||
Mail sent to us when a user accepts the ToS
 | 
					 | 
				
			||||||
#}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Hello,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{{ name }} <{{ email }}> just accepted the Zulip Terms of Service.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{{ ip }} submitted /accounts/accept_tos from {{ browser }}.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Cheers,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Zulip Legal Bot
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										7
									
								
								templates/zerver/zulipchat_migration_tos.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								templates/zerver/zulipchat_migration_tos.html
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
				
			||||||
@@ -78,7 +78,6 @@ class RegistrationForm(forms.Form):
 | 
				
			|||||||
        terms = forms.BooleanField(required=True)
 | 
					        terms = forms.BooleanField(required=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ToSForm(forms.Form):
 | 
					class ToSForm(forms.Form):
 | 
				
			||||||
    full_name = forms.CharField(max_length=100)
 | 
					 | 
				
			||||||
    terms = forms.BooleanField(required=True)
 | 
					    terms = forms.BooleanField(required=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HomepageForm(forms.Form):
 | 
					class HomepageForm(forms.Form):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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),
 | 
					        send_event(dict(type='realm_bot', op='update', bot=payload),
 | 
				
			||||||
                   bot_owner_userids(user_profile))
 | 
					                   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):
 | 
					def do_regenerate_api_key(user_profile, log=True):
 | 
				
			||||||
    # type: (UserProfile, bool) -> None
 | 
					    # type: (UserProfile, bool) -> None
 | 
				
			||||||
    user_profile.api_key = random_api_key()
 | 
					    user_profile.api_key = random_api_key()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										19
									
								
								zerver/migrations/0028_userprofile_tos_version.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								zerver/migrations/0028_userprofile_tos_version.py
									
									
									
									
									
										Normal file
									
								
							@@ -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(default=None, max_length=10, null=True),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@@ -391,6 +391,7 @@ class UserProfile(ModelReprMixin, AbstractBaseUser, PermissionsMixin):
 | 
				
			|||||||
    last_pointer_updater = models.CharField(max_length=64) # type: text_type
 | 
					    last_pointer_updater = models.CharField(max_length=64) # type: text_type
 | 
				
			||||||
    realm = models.ForeignKey(Realm) # type: Realm
 | 
					    realm = models.ForeignKey(Realm) # type: Realm
 | 
				
			||||||
    api_key = models.CharField(max_length=32) # type: text_type
 | 
					    api_key = models.CharField(max_length=32) # type: text_type
 | 
				
			||||||
 | 
					    tos_version = models.CharField(null=True, max_length=10, default=settings.TOS_VERSION) # type: text_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ### Notifications settings. ###
 | 
					    ### Notifications settings. ###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -511,6 +512,13 @@ class UserProfile(ModelReprMixin, AbstractBaseUser, PermissionsMixin):
 | 
				
			|||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return False
 | 
					            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):
 | 
					def receives_offline_notifications(user_profile):
 | 
				
			||||||
    # type: (UserProfile) -> bool
 | 
					    # type: (UserProfile) -> bool
 | 
				
			||||||
    return ((user_profile.enable_offline_email_notifications or
 | 
					    return ((user_profile.enable_offline_email_notifications or
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,7 @@ from zerver.lib.actions import do_change_password, do_change_full_name, do_chang
 | 
				
			|||||||
    do_activate_user, do_create_user, do_create_realm, set_default_streams, \
 | 
					    do_activate_user, do_create_user, do_create_realm, set_default_streams, \
 | 
				
			||||||
    internal_send_message, update_user_presence, do_events_register, \
 | 
					    internal_send_message, update_user_presence, do_events_register, \
 | 
				
			||||||
    do_change_enable_offline_email_notifications, \
 | 
					    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, \
 | 
					    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, \
 | 
					    compute_mit_user_fullname, do_set_muted_topics, clear_followup_emails_queue, \
 | 
				
			||||||
    do_update_pointer, realm_user_count
 | 
					    do_update_pointer, realm_user_count
 | 
				
			||||||
@@ -224,6 +224,7 @@ def accounts_register(request):
 | 
				
			|||||||
            user_profile = do_create_user(email, password, realm, full_name, short_name,
 | 
					            user_profile = do_create_user(email, password, realm, full_name, short_name,
 | 
				
			||||||
                                          prereg_user=prereg_user,
 | 
					                                          prereg_user=prereg_user,
 | 
				
			||||||
                                          newsletter_data={"IP": request.META['REMOTE_ADDR']})
 | 
					                                          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
 | 
					        # This logs you in using the ZulipDummyBackend, since honestly nothing
 | 
				
			||||||
        # more fancy than this is required.
 | 
					        # more fancy than this is required.
 | 
				
			||||||
@@ -253,27 +254,22 @@ def accounts_register(request):
 | 
				
			|||||||
@zulip_login_required
 | 
					@zulip_login_required
 | 
				
			||||||
def accounts_accept_terms(request):
 | 
					def accounts_accept_terms(request):
 | 
				
			||||||
    # type: (HttpRequest) -> HttpResponse
 | 
					    # type: (HttpRequest) -> HttpResponse
 | 
				
			||||||
    email = request.user.email
 | 
					 | 
				
			||||||
    domain = resolve_email_to_domain(email)
 | 
					 | 
				
			||||||
    if request.method == "POST":
 | 
					    if request.method == "POST":
 | 
				
			||||||
        form = ToSForm(request.POST)
 | 
					        form = ToSForm(request.POST)
 | 
				
			||||||
        if form.is_valid():
 | 
					        if form.is_valid():
 | 
				
			||||||
            full_name = form.cleaned_data['full_name']
 | 
					            do_change_tos_version(request.user, settings.TOS_VERSION)
 | 
				
			||||||
            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)
 | 
					 | 
				
			||||||
            return redirect(home)
 | 
					            return redirect(home)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        form = ToSForm()
 | 
					        form = ToSForm()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    email = request.user.email
 | 
				
			||||||
 | 
					    domain = resolve_email_to_domain(email)
 | 
				
			||||||
 | 
					    special_message_template = None
 | 
				
			||||||
 | 
					    if request.user.tos_version is None and settings.FIRST_TIME_TOS_TEMPLATE is not None:
 | 
				
			||||||
 | 
					        special_message_template = 'zerver/' + settings.FIRST_TIME_TOS_TEMPLATE
 | 
				
			||||||
    return render_to_response('zerver/accounts_accept_terms.html',
 | 
					    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)
 | 
					        request=request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from zerver.lib.ccache import make_ccache
 | 
					from zerver.lib.ccache import make_ccache
 | 
				
			||||||
@@ -838,6 +834,11 @@ def home(request):
 | 
				
			|||||||
    request._email = request.user.email
 | 
					    request._email = request.user.email
 | 
				
			||||||
    request.client = get_client("website")
 | 
					    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 = [] # type: List[List[text_type]]
 | 
				
			||||||
    narrow_stream = None
 | 
					    narrow_stream = None
 | 
				
			||||||
    narrow_topic = request.GET.get("topic")
 | 
					    narrow_topic = request.GET.get("topic")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,6 +69,11 @@ else:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Terms of Service
 | 
					# Terms of Service
 | 
				
			||||||
TERMS_OF_SERVICE = 'corporate/terms.md'
 | 
					TERMS_OF_SERVICE = 'corporate/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 = '1.0'
 | 
				
			||||||
 | 
					# FIRST_TIME_TOS_TEMPLATE = 'zulipchat_migration_tos.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Legacy zulip.com bucket used for old-style S3 uploads.
 | 
					# Legacy zulip.com bucket used for old-style S3 uploads.
 | 
				
			||||||
S3_BUCKET="humbug-user-uploads"
 | 
					S3_BUCKET="humbug-user-uploads"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -166,6 +166,8 @@ DEFAULT_SETTINGS = {'TWITTER_CONSUMER_KEY': '',
 | 
				
			|||||||
                    'DEFAULT_NEW_REALM_STREAMS': ["social", "general", "zulip"],
 | 
					                    'DEFAULT_NEW_REALM_STREAMS': ["social", "general", "zulip"],
 | 
				
			||||||
                    'REALM_CREATION_LINK_VALIDITY_DAYS': 7,
 | 
					                    'REALM_CREATION_LINK_VALIDITY_DAYS': 7,
 | 
				
			||||||
                    'TERMS_OF_SERVICE': None,
 | 
					                    'TERMS_OF_SERVICE': None,
 | 
				
			||||||
 | 
					                    'TOS_VERSION': None,
 | 
				
			||||||
 | 
					                    'FIRST_TIME_TOS_TEMPLATE': None
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for setting_name, setting_val in six.iteritems(DEFAULT_SETTINGS):
 | 
					for setting_name, setting_val in six.iteritems(DEFAULT_SETTINGS):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user