Repository: django-cms/django-cms

Issue:
Code in #1283 does not handle multi-site
Code in #1283 incorrectly reports as clashing pages with the same url in different sites managed by the same django-cms project.
Fix is to make `util.page_resolver.get_page_queryset_from_path` site-aware.
Test and fix will follow soon

Code in #1283 does not handle multi-site
Code in #1283 incorrectly reports as clashing pages with the same url in different sites managed by the same django-cms project.
Fix is to make `util.page_resolver.get_page_queryset_from_path` site-aware.
Test and fix will follow soon



Discussion:

Resolving patch:
diff --git a/cms/utils/page_resolver.py b/cms/utils/page_resolver.py
--- a/cms/utils/page_resolver.py
+++ b/cms/utils/page_resolver.py
@@ -18,7 +18,7 @@
 ADMIN_PAGE_RE = re.compile(ADMIN_PAGE_RE_PATTERN)
 
 
-def get_page_queryset_from_path(path, preview=False):
+def get_page_queryset_from_path(path, preview=False, site=None):
     """ Returns a queryset of pages corresponding to the path given
     In may returns None or a single page is no page is present or root path is given
     """
@@ -26,8 +26,9 @@ def get_page_queryset_from_path(path, preview=False):
         admin_base = reverse('admin:index').lstrip('/')
     else:
         admin_base = None
-    
-    site = Site.objects.get_current()
+
+    if not site:
+        site = Site.objects.get_current()
     # Check if this is called from an admin request
     if admin_base and path.startswith(admin_base):
         # if so, get the page ID to query the page
@@ -47,10 +48,13 @@ def get_page_queryset_from_path(path, preview=False):
     else:
         pages = Page.objects.public()
 
+    # PageQuerySet.published filter on page site.
+    # We have to explicitly filter on site only in preview mode
+    # we can skip pages.filter
     if not preview:
-        pages = pages.published()
-
-    pages = pages.filter(site=site)
+        pages = pages.published(site)
+    else:
+        pages = pages.filter(site_id=site.pk)
 
     # Check if there are any pages
     if not pages.all_root().exists():
@@ -133,16 +137,20 @@ def get_page_from_request(request, use_path=None):
     return page
 
 
-def is_valid_url(url,instance,create_links=True):
+def is_valid_url(url,instance,create_links=True, site=None):
     """ Checks for conflicting urls
     """
     if url:
         # Url sanity check via regexp
         if not any_path_re.match(url):
             raise ValidationError(_('Invalid URL, use /my/url format.'))
+        # We only check page FK to site object to allow is_valid_url check on
+        # incomplete Page instances
+        if not site and (instance.site_id):
+            site = instance.site
         # Retrieve complete queryset of pages with corresponding URL
         # This uses the same resolving function as ``get_page_from_path``
-        page_qs = get_page_queryset_from_path(url.strip('/'))
+        page_qs = get_page_queryset_from_path(url.strip('/'), site=site)
         url_clashes = []
         # If queryset has pages checks for conflicting urls
         if page_qs is not None:


Test patch:
diff --git a/cms/tests/page.py b/cms/tests/page.py
--- a/cms/tests/page.py
+++ b/cms/tests/page.py
@@ -693,6 +693,18 @@ def test_slug_url_overwrite_clash(self):
                 bar.save()
             self.assertFalse(bar.published)
 
+    def test_valid_url_multisite(self):
+        with SettingsOverride(CMS_MODERATOR=False):
+            site1 = Site.objects.get_current()
+            site3 = Site.objects.create(domain="sample3.com", name="sample3.com")
+            home = create_page('home', 'nav_playground.html', 'en', published=True, site=site1)
+            bar = create_page('bar', 'nav_playground.html', 'en', slug="bar", published=True, parent=home, site=site1)
+            home_s3= create_page('home', 'nav_playground.html', 'en', published=True, site=site3)
+            bar_s3 = create_page('bar', 'nav_playground.html', 'en', slug="bar", published=True, parent=home_s3, site=site3)
+
+            self.assertTrue(is_valid_url(bar.get_absolute_url('en'), bar))
+            self.assertTrue(is_valid_url(bar_s3.get_absolute_url('en'), bar_s3))
+
     def test_home_slug_not_accessible(self):
         with SettingsOverride(CMS_MODERATOR=False, CMS_PERMISSION=False):
             page = create_page('page', 'nav_playground.html', 'en', published=True)