Skip to content

Implement hidden state for versions #6792

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 33 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1bebda0
Add tests
stsewd Mar 18, 2020
e60d997
Add hidden attribute
stsewd Mar 18, 2020
04ff029
Update form
stsewd Mar 18, 2020
645308d
Update queryset
stsewd Mar 18, 2020
526a023
Filter hidden versions from footer and search
stsewd Mar 18, 2020
46c59bf
Add hidden attribute to API v3
stsewd Mar 18, 2020
2326598
Update docs
stsewd Mar 18, 2020
8537331
Migrate protected versions to be hidden
stsewd Mar 18, 2020
6b04db1
Merge branch 'master' into implement-hidden-state-for-versions
stsewd Mar 23, 2020
2768158
Apply suggestions from code review
stsewd Mar 24, 2020
c2b9350
Use include_hidden
stsewd Mar 24, 2020
055d862
Improve docs
stsewd Mar 24, 2020
ee486ba
Merge branch 'master' into implement-hidden-state-for-versions
stsewd Mar 24, 2020
17a28eb
Improve docs
stsewd Mar 24, 2020
dfedc8b
Add guide
stsewd Mar 24, 2020
3bcf9fe
Try using crispy forms for better UX
stsewd Mar 24, 2020
5f3444f
Merge branch 'master' into implement-hidden-state-for-versions
stsewd Mar 24, 2020
7058768
Fix migrations
stsewd Mar 24, 2020
60e12d5
Merge branch 'master' into implement-hidden-state-for-versions
stsewd Mar 30, 2020
5303da4
Merge branch 'master' into implement-hidden-state-for-versions
stsewd Apr 1, 2020
27f8311
Merge branch 'master' into implement-hidden-state-for-versions
stsewd Apr 7, 2020
ea1f8ba
Apply suggestions from code review
stsewd Apr 20, 2020
2419877
Merge branch 'master' into implement-hidden-state-for-versions
stsewd Apr 20, 2020
bc30c5e
Extension
stsewd Apr 20, 2020
a4c59ce
Fix migrations
stsewd Apr 20, 2020
649c90f
Avoid downtime
stsewd Apr 20, 2020
ced02ad
Fix form
stsewd Apr 20, 2020
a78d500
Merge branch 'master' into implement-hidden-state-for-versions
stsewd Apr 28, 2020
c69ad79
Update migrations
stsewd Apr 28, 2020
a85c1f1
Disallow hidden versions on robots.txt
stsewd Apr 28, 2020
bd476a7
Mention robots.txt in docs
stsewd Apr 28, 2020
a640499
Add comments to robots.txt
ericholscher Apr 28, 2020
965dfc0
Fix tests
ericholscher Apr 28, 2020
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
4 changes: 3 additions & 1 deletion docs/api/v3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ Version detail
"ref": "19.0.2",
"built": true,
"active": true,
"hidden": false,
"type": "tag",
"last_build": "{BUILD}",
"downloads": {
Expand Down Expand Up @@ -460,7 +461,8 @@ Version update
.. sourcecode:: json

{
"active": true
"active": true,
"hidden": false
}

:statuscode 204: Updated successfully
Expand Down
37 changes: 37 additions & 0 deletions docs/versions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,43 @@ they will be redirected to the **Default version**.
This defaults to **latest**,
but could also point to your latest released version.

States
------

You can change the states from a version from the :guilabel:`Versions` tab of your project.

Active
~~~~~~

- **Active**

- Docs for this version are visible
- Builds can be triggered for this version

- **Inactive**

- Docs for this version aren't visible
- Builds can't be triggered for this version

When you inactivate a version, its docs are removed.

Hidden
~~~~~~

- **Not hidden**

- This version is listed on the footer
- This version is show in search results

- **Hidden**

- This version isn't listed on the footer
- This version isn't show in search results from another version
(like on search results from a superproject)

Hidden a version is useful when you no longer support a version,
but you don't want to remove its docs.

Version warning
---------------

Expand Down
1 change: 1 addition & 0 deletions readthedocs/api/v2/views/footer_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def _get_active_versions_sorted(self):
project = self._get_project()
versions = project.ordered_active_versions(
user=self.request.user,
include_hidden=False,
)
return versions

Expand Down
1 change: 1 addition & 0 deletions readthedocs/api/v3/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ class Meta:
'ref',
'built',
'active',
'hidden',
'type',
'downloads',
'urls',
Expand Down
1 change: 1 addition & 0 deletions readthedocs/api/v3/tests/responses/projects-detail.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"active_versions": [
{
"active": true,
"hidden": false,
"built": true,
"downloads": {
"epub": "https://project.readthedocs.io/_/downloads/en/v1.0/epub/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"triggered": true,
"version": {
"active": true,
"hidden": false,
"built": true,
"downloads": {
"epub": "https://project.readthedocs.io/_/downloads/en/v1.0/epub/",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"active": true,
"hidden": false,
"built": true,
"downloads": {
"epub": "https://project.readthedocs.io/_/downloads/en/v1.0/epub/",
Expand Down
2 changes: 1 addition & 1 deletion readthedocs/builds/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class VersionForm(HideProtectedLevelMixin, forms.ModelForm):

class Meta:
model = Version
fields = ['active', 'privacy_level']
fields = ['active', 'hidden', 'privacy_level']

def clean_active(self):
active = self.cleaned_data['active']
Expand Down
18 changes: 18 additions & 0 deletions readthedocs/builds/migrations/0015_add_hidden_field_to_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.2.11 on 2020-03-18 01:47

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('builds', '0014_migrate-doctype-from-project-to-version'),
]

operations = [
migrations.AddField(
model_name='version',
name='hidden',
field=models.BooleanField(default=False, help_text='Hide this version from the footer and search results?', verbose_name='Hidden'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 2.2.11 on 2020-03-18 18:27

from django.db import migrations


def forwards_func(apps, schema_editor):
"""Migrate all protected versions to be hidden."""
Version = apps.get_model('builds', 'Version')
Version.objects.filter(privacy_level='protected').update(hidden=True)


class Migration(migrations.Migration):

dependencies = [
('builds', '0015_add_hidden_field_to_version'),
]

operations = [
migrations.RunPython(forwards_func),
]
5 changes: 5 additions & 0 deletions readthedocs/builds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ class Version(models.Model):
default=settings.DEFAULT_VERSION_PRIVACY_LEVEL,
help_text=_('Level of privacy for this Version.'),
)
hidden = models.BooleanField(
_('Hidden'),
default=False,
help_text=_('Hide this version from the footer and search results?')
)
machine = models.BooleanField(_('Machine Created'), default=False)

# Whether the latest successful build for this version contains certain media types
Expand Down
4 changes: 3 additions & 1 deletion readthedocs/builds/querysets.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ def _add_user_repos(self, queryset, user):
queryset = user_queryset | queryset
return queryset

def public(self, user=None, project=None, only_active=True):
def public(self, user=None, project=None, only_active=True, include_hidden=True):
queryset = self.filter(privacy_level=constants.PUBLIC)
if user:
queryset = self._add_user_repos(queryset, user)
if project:
queryset = queryset.filter(project=project)
if only_active:
queryset = queryset.filter(active=True)
if not include_hidden:
queryset = queryset.filter(hidden=False)
return queryset.distinct()

def protected(self, user=None, project=None, only_active=True):
Expand Down
27 changes: 27 additions & 0 deletions readthedocs/rtd_tests/tests/test_footer.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,33 @@ def test_index_pages_sphinx_htmldir(self):
self.assertNotIn('/en/latest/foo/index/bar.html', response.data['html'])
self.assertNotIn('/en/latest/foo/index/bar/index.html', response.data['html'])

def test_hidden_versions(self):
hidden_version = get(
Version,
slug='2.0',
hidden=True,
privacy_level=PUBLIC,
project=self.pip,
)

# The hidden version doesn't appear on the footer
self.url = (
reverse('footer_html') +
f'?project={self.pip.slug}&version={self.latest.slug}&page=index&docroot=/'
)
response = self.render()
self.assertIn('/en/latest/', response.data['html'])
self.assertNotIn('/en/2.0/', response.data['html'])

# We can access the hidden version, but it doesn't appear on the footer
self.url = (
reverse('footer_html') +
f'?project={self.pip.slug}&version={hidden_version.slug}&page=index&docroot=/'
)
response = self.render()
self.assertIn('/en/latest/', response.data['html'])
self.assertNotIn('/en/2.0/', response.data['html'])


class TestFooterHTML(BaseTestFooterHTML, TestCase):

Expand Down
6 changes: 4 additions & 2 deletions readthedocs/search/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,17 @@ def get_all_projects(self):
main_version = self._get_version()
main_project = self._get_project()

all_projects = [main_project]

subprojects = Project.objects.filter(
superprojects__parent_id=main_project.id,
)
all_projects = []
for project in list(subprojects) + [main_project]:
for project in subprojects:
version = (
Version.internal
.public(user=self.request.user, project=project)
.filter(slug=main_version.slug)
.filter(hidden=False)
.first()
)
if version:
Expand Down
41 changes: 41 additions & 0 deletions readthedocs/search/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,47 @@ def test_get_all_projects_returns_empty_results(self, api_client, project):
data = resp.data['results']
assert len(data) == 0

def test_doc_search_hidden_versions(self, api_client, all_projects):
"""Test Document search return results from subprojects also"""
project = all_projects[0]
subproject = all_projects[1]
version = project.versions.all()[0]
# Add another project as subproject of the project
project.add_subproject(subproject)

version_subproject = subproject.versions.first()
version_subproject.hidden = True
version_subproject.save()

# Now search with subproject content but explicitly filter by the parent project
query = get_search_query_from_project_file(project_slug=subproject.slug)
search_params = {
'q': query,
'project': project.slug,
'version': version.slug
}
resp = self.get_search(api_client, search_params)
assert resp.status_code == 200

# The version from the subproject is hidden, so isn't show on the results.
data = resp.data['results']
assert len(data) == 0

# Now search on the subproject with hidden version
query = get_search_query_from_project_file(project_slug=subproject.slug)
search_params = {
'q': query,
'project': subproject.slug,
'version': version_subproject.slug
}
resp = self.get_search(api_client, search_params)
assert resp.status_code == 200
# We can still search inside the hidden version
data = resp.data['results']
assert len(data) == 1
first_result = data[0]
assert first_result['project'] == subproject.slug


class TestDocumentSearch(BaseTestDocumentSearch):

Expand Down