diff --git a/readthedocs/api/v2/views/footer_views.py b/readthedocs/api/v2/views/footer_views.py index 09709ac1bb1..04d4fe8210b 100644 --- a/readthedocs/api/v2/views/footer_views.py +++ b/readthedocs/api/v2/views/footer_views.py @@ -25,12 +25,15 @@ def get_version_compare_data(project, base_version=None): :param base_version: We assert whether or not the base_version is also the highest version in the resulting "is_highest" value. """ - versions_qs = project.versions.public().filter(active=True) + versions_qs = Version.objects.public(project=project) # Take preferences over tags only if the project has at least one tag if versions_qs.filter(type=TAG).exists(): versions_qs = versions_qs.filter(type=TAG) + # Optimization + versions_qs = versions_qs.select_related('project') + highest_version_obj, highest_version_comparable = highest_version( versions_qs, ) diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 390063f256a..cd5ab8fdb64 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -202,7 +202,11 @@ def serve_docs( if not version_slug: version_slug = project.get_default_version() try: - version = project.versions.public(request.user).get(slug=version_slug) + version = ( + Version.objects + .public(user=request.user, project=project) + .get(slug=version_slug) + ) except Version.DoesNotExist: # Properly raise a 404 if the version doesn't exist (or is inactive) and # a 401 if it does @@ -409,8 +413,10 @@ def changefreqs_generator(): if project.translations.exists(): for translation in project.translations.all(): - translation_versions = translation.versions.public( - ).values_list('slug', flat=True) + translation_versions = ( + Version.objects.public(project=translation) + .values_list('slug', flat=True) + ) if version.slug in translation_versions: href = project.get_docs_url( version_slug=version.slug, diff --git a/readthedocs/rtd_tests/tests/test_doc_serving.py b/readthedocs/rtd_tests/tests/test_doc_serving.py index 134b191235c..4f6cb93990c 100644 --- a/readthedocs/rtd_tests/tests/test_doc_serving.py +++ b/readthedocs/rtd_tests/tests/test_doc_serving.py @@ -1,16 +1,16 @@ - import os import django_dynamic_fixture as fixture import mock from django.conf import settings from django.contrib.auth.models import User -from django.http import Http404 +from django.http import Http404, HttpResponse from django.test import RequestFactory, TestCase from django.test.utils import override_settings from django.urls import reverse from mock import mock_open, patch +from readthedocs.builds.constants import LATEST from readthedocs.builds.models import Version from readthedocs.core.middleware import SubdomainMiddleware from readthedocs.core.views import server_error_404_subdomain @@ -293,3 +293,39 @@ def test_sitemap_xml(self): # hreflang should use hyphen instead of underscore # in language and country value. (zh_CN should be zh-CN) self.assertContains(response, 'zh-CN') + + @override_settings( + PYTHON_MEDIA=True, + USE_SUBDOMAIN=False, + ) + @patch( + 'readthedocs.core.views.serve._serve_symlink_docs', + new=mock.MagicMock(return_value=HttpResponse(content_type='text/html')), + ) + def test_user_with_multiple_projects_serve_from_same_domain(self): + project = fixture.get( + Project, + main_language_project=None, + users=[self.eric], + ) + other_project = fixture.get( + Project, + main_language_project=None, + users=[self.eric], + ) + + # Trigger the save method of the versions + project.versions.get(slug=LATEST).save() + other_project.versions.get(slug=LATEST).save() + + # Two projects of one owner with versions with the same slug + self.assertIn(self.eric, project.users.all()) + self.assertIn(self.eric, other_project.users.all()) + self.assertTrue(project.versions.filter(slug=LATEST).exists()) + self.assertTrue(other_project.versions.filter(slug=LATEST).exists()) + + self.client.force_login(self.eric) + request = self.client.get( + f'/docs/{project.slug}/en/latest/' + ) + self.assertEqual(request.status_code, 200)