Skip to content

Hide "Protected" privacy level from users #5833

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions docs/privacy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Privacy Levels
==============

Read the Docs supports 3 different privacy levels on 2 different objects;
Public, Protected, Private on Projects and Versions.
Public, Private on Projects and Versions.

Understanding the Privacy Levels
--------------------------------
Expand All @@ -12,8 +12,6 @@ Understanding the Privacy Levels
+============+============+===========+===========+=============+
| Private | No | No | No | Yes |
+------------+------------+-----------+-----------+-------------+
| Protected | Yes | No | No | Yes |
+------------+------------+-----------+-----------+-------------+
| Public | Yes | Yes | Yes | Yes |
+------------+------------+-----------+-----------+-------------+

Expand All @@ -27,18 +25,6 @@ Public
This is the easiest and most obvious. It is also the default.
It means that everything is available to be seen by everyone.

Protected
~~~~~~~~~

Protected means that your object won't show up in Listing Pages,
but Detail pages still work. For example, a Project that is Protected will
not show on the homepage Recently Updated list, however,
if you link directly to the project, you will get a 200 and the page will display.

Protected Versions are similar, they won't show up in your version listings,
but will be available once linked to.


Private
~~~~~~~

Expand Down
3 changes: 2 additions & 1 deletion readthedocs/builds/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
from django.utils.translation import ugettext_lazy as _

from readthedocs.builds.models import Version
from readthedocs.core.mixins import HideProtectedLevelMixin
from readthedocs.core.utils import trigger_build


class VersionForm(forms.ModelForm):
class VersionForm(HideProtectedLevelMixin, forms.ModelForm):

class Meta:
model = Version
Expand Down
26 changes: 26 additions & 0 deletions readthedocs/core/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from vanilla import ListView

from readthedocs.projects.constants import PRIVACY_CHOICES, PROTECTED


class ListViewWithForm(ListView):

Expand All @@ -22,3 +25,26 @@ class LoginRequiredMixin:
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)


class HideProtectedLevelMixin:

"""
Hide ``protected`` privacy level from Form.

Remove Protected for now since it causes confusions to users.

If the current ``privacy_level`` is ``protected`` we show it (so users keep
seeing consistency values), and hide it otherwise (so it can't be selected).

There is a better way to manage this by using Version states.
See: https://github.com/rtfd/readthedocs.org/issues/5321
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

if self.instance is None or self.instance.privacy_level != PROTECTED:
privacy_level = list(PRIVACY_CHOICES)
privacy_level.remove((PROTECTED, _('Protected')))
self.fields['privacy_level'].choices = privacy_level
3 changes: 2 additions & 1 deletion readthedocs/projects/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from guardian.shortcuts import assign
from textclassifier.validators import ClassifierValidator

from readthedocs.core.mixins import HideProtectedLevelMixin
from readthedocs.core.utils import slugify, trigger_build
from readthedocs.core.utils.extend import SettingsOverrideObject
from readthedocs.integrations.models import Integration
Expand Down Expand Up @@ -189,7 +190,7 @@ def clean_tags(self):
return tags


class ProjectAdvancedForm(ProjectTriggerBuildMixin, ProjectForm):
class ProjectAdvancedForm(HideProtectedLevelMixin, ProjectTriggerBuildMixin, ProjectForm):

"""Advanced project option form."""

Expand Down
20 changes: 20 additions & 0 deletions readthedocs/projects/migrations/0044_auto_20190703_1300.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-07-03 13:00
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('projects', '0043_add-build-field'),
]

operations = [
migrations.AlterField(
model_name='project',
name='privacy_level',
field=models.CharField(choices=[('public', 'Public'), ('protected', 'Protected'), ('private', 'Private')], default='public', help_text='Level of privacy that you want on the repository.', max_length=20, verbose_name='Privacy Level'),
),
]
3 changes: 1 addition & 2 deletions readthedocs/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,7 @@ class Project(models.Model):
choices=constants.PRIVACY_CHOICES,
default=settings.DEFAULT_PRIVACY_LEVEL,
help_text=_(
'Level of privacy that you want on the repository. '
'Protected means public but not in listings.',
'Level of privacy that you want on the repository.',
),
)
version_privacy_level = models.CharField(
Expand Down
25 changes: 24 additions & 1 deletion readthedocs/rtd_tests/tests/test_project_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
from django.contrib.auth.models import User
from django.test import TestCase
from django.test.utils import override_settings
from django.utils.translation import ugettext_lazy as _
from django_dynamic_fixture import get
from textclassifier.validators import ClassifierValidator

from readthedocs.builds.constants import LATEST, STABLE
from readthedocs.builds.models import Version
from readthedocs.projects.constants import (
PRIVATE,
PRIVACY_CHOICES,
PROTECTED,
PUBLIC,
REPO_TYPE_GIT,
Expand Down Expand Up @@ -272,6 +273,28 @@ def test_default_version_field_if_no_active_version(self):
self.assertTrue(form.fields['default_version'].widget.attrs['readonly'])
self.assertEqual(form.fields['default_version'].initial, 'latest')

def test_hide_protected_privacy_level_new_objects(self):
"""
Test PROTECTED is only allowed in old objects.

New projects are not allowed to set the privacy level as protected.
"""
# New default object
project = get(Project)
form = ProjectAdvancedForm(instance=project)

privacy_choices = list(PRIVACY_CHOICES)
privacy_choices.remove((PROTECTED, _('Protected')))
self.assertEqual(form.fields['privacy_level'].choices, privacy_choices)

# "Old" object with privacy_level previously set as protected
project = get(
Project,
privacy_level=PROTECTED,
)
form = ProjectAdvancedForm(instance=project)
self.assertEqual(form.fields['privacy_level'].choices, list(PRIVACY_CHOICES))


class TestProjectAdvancedFormDefaultBranch(TestCase):

Expand Down