Repository: django-cms/django-cms

Issue:
maximum recursion depth exceeded in get_available_slug
django-cms 2.4.3 on Linux

Occurs when:
I have a Products page and every product is a subpage of this Products page
1) I clock copy on a product page
2) I clock insert as child on the Products page
3) Loading.... appears in the top right corner
4) HTTP 500 Error appears:
RuntimeError at /en/admin/cms/page/67/copy-page/
maximum recursion depth exceeded

If I choose to insert it as sibling to the Products page it works. We have around 15 products and each one has a unique slug.

From looking at the code, I assume that is_valid_page_slug sometime returns false, although it's a valid slug.

No overwrite_url is set.



Discussion:
Here is the Django Debug output https://gist.github.com/abossard/8001917

Also happens when I move a page.

The pages have an automatically set slug in another language. When I completely removed this language the maximum recursion disappeared. 

Is there a limit on how many -copy-n can be appended?

Can you try to set a new home page? Moving a page to the top?

The problem is with the slug generation when a page is copied, after slug-copy-10 it breaks. It never makes a slug-copy-11.... 

This is how I can reproduce the problem:
1. Make a Page
2. create a child page
3. copy the child page as new child of the parent until it breaks :-)


Resolving patch:
diff --git a/cms/utils/page.py b/cms/utils/page.py
--- a/cms/utils/page.py
+++ b/cms/utils/page.py
@@ -4,7 +4,7 @@
 import re
 
 APPEND_TO_SLUG = "-copy"
-COPY_SLUG_REGEX = re.compile(r'^.*-copy(?:-(\d)*)?$')
+COPY_SLUG_REGEX = re.compile(r'^.*-copy(?:-(\d+)*)?$')
 
 def is_valid_page_slug(page, parent, lang, slug, site, path=None):
     """Validates given slug depending on settings.
@@ -65,9 +65,8 @@ def get_available_slug(title, new_slug=None):
             try:
                 next = int(match.groups()[0]) + 1
                 slug = "-".join(slug.split('-')[:-1]) + "-%d" % next
-            except TypeError:
+            except TypeError as e:
                 slug += "-2"
-
         else:
             slug += APPEND_TO_SLUG
         return get_available_slug(title, slug)


Test patch:
diff --git a/cms/tests/page.py b/cms/tests/page.py
--- a/cms/tests/page.py
+++ b/cms/tests/page.py
@@ -28,7 +28,7 @@
 from cms.test_utils.util.context_managers import (LanguageOverride, SettingsOverride, UserLoginContext)
 from cms.utils import get_cms_setting
 from cms.utils.page_resolver import get_page_from_request, is_valid_url
-from cms.utils.page import is_valid_page_slug
+from cms.utils.page import is_valid_page_slug, get_available_slug
 
 
 class PageMigrationTestCase(CMSTestCase):
@@ -129,6 +129,15 @@ def test_slug_collision(self):
                 # TODO: check for slug collisions after move
                 # TODO: check for slug collisions with different settings
 
+    def test_get_available_slug_recursion(self):
+        """ Checks cms.utils.page.get_available_slug for infinite recursion
+        """
+        for x in range(0, 12):
+            page1 = create_page('test copy', 'nav_playground.html', 'en',
+                                published=True)
+        new_slug = get_available_slug(page1.get_title_obj('en'), 'test-copy')
+        self.assertTrue(new_slug, 'test-copy-11')
+
     def test_slug_collisions_api_1(self):
         """ Checks for slug collisions on sibling pages - uses API to create pages
         """