Repository: django-cms/django-cms

Issue:
only one apphook works per installation
If you setup multiple apphooks, only one of them works.

I've tested this with a local branch that does not have Moderation in it, but the same problem exists in develop (a75fde989cb398c4cb7bdbc6bc9a01cf80bb91c7).

There is another problem in develop (#1466) wich probably masked this one.



Discussion:

Resolving patch:
diff --git a/cms/appresolver.py b/cms/appresolver.py
--- a/cms/appresolver.py
+++ b/cms/appresolver.py
@@ -48,8 +48,11 @@ def applications_page_check(request, current_page=None, path=None):
     return None
 
 class AppRegexURLResolver(RegexURLResolver):
-    page_id = None
-    url_patterns_dict = {}
+
+    def __init__(self, *args, **kwargs):
+        self.page_id = None
+        self.url_patterns_dict = {}
+        super(AppRegexURLResolver, self).__init__(*args, **kwargs)
 
     @property
     def url_patterns(self):


Test patch:
diff --git a/cms/test_utils/project/sampleapp/cms_app.py b/cms/test_utils/project/sampleapp/cms_app.py
--- a/cms/test_utils/project/sampleapp/cms_app.py
+++ b/cms/test_utils/project/sampleapp/cms_app.py
@@ -11,6 +11,13 @@ class SampleApp(CMSApp):
 apphook_pool.register(SampleApp)
 
 
+class SampleApp2(CMSApp):
+    name = _("Sample App 2")
+    urls = ["cms.test_utils.project.sampleapp.urls2"]
+
+apphook_pool.register(SampleApp2)
+
+
 class NamespacedApp(CMSApp):
     name = _("Namespaced App")
     urls = [
diff --git a/cms/test_utils/project/sampleapp/urls2.py b/cms/test_utils/project/sampleapp/urls2.py
new file mode 100644
--- /dev/null
+++ b/cms/test_utils/project/sampleapp/urls2.py
@@ -0,0 +1,10 @@
+from django.conf.urls.defaults import *
+from django.utils.translation import ugettext_lazy as _
+
+"""
+Also used in cms.tests.ApphooksTestCase
+"""
+
+urlpatterns = patterns('cms.test_utils.project.sampleapp.views',
+    url(r'^$', 'sample_view', {'message': 'sample apphook2 root page',}, name='sample2-root'),
+)
diff --git a/cms/test_utils/project/third_cms_urls_for_apphook_tests.py b/cms/test_utils/project/third_cms_urls_for_apphook_tests.py
new file mode 100644
--- /dev/null
+++ b/cms/test_utils/project/third_cms_urls_for_apphook_tests.py
@@ -0,0 +1,25 @@
+from cms.apphook_pool import apphook_pool
+from cms.views import details
+from django.conf import settings
+from django.conf.urls.defaults import url, patterns
+
+if settings.APPEND_SLASH:
+    reg = url(r'^(?P<slug>[0-9A-Za-z-_.//]+)/$', details, name='pages-details-by-slug')
+else:
+    reg = url(r'^(?P<slug>[0-9A-Za-z-_.//]+)$', details, name='pages-details-by-slug')
+
+urlpatterns = [
+    # Public pages
+    url(r'^$', details, {'slug':''}, name='pages-root'),
+    reg,
+]
+
+if apphook_pool.get_apphooks():
+    """If there are some application urls, add special resolver, so we will
+    have standard reverse support.
+    """
+    from cms.appresolver import get_app_patterns
+    urlpatterns = get_app_patterns() + urlpatterns
+    #urlpatterns = (dynamic_app_regex_url_resolver, ) + urlpatterns
+    
+urlpatterns = patterns('', *urlpatterns)
diff --git a/cms/test_utils/project/third_urls_for_apphook_tests.py b/cms/test_utils/project/third_urls_for_apphook_tests.py
new file mode 100644
--- /dev/null
+++ b/cms/test_utils/project/third_urls_for_apphook_tests.py
@@ -0,0 +1,23 @@
+from django.conf import settings
+from django.conf.urls.defaults import handler500, handler404, patterns, include, \
+    url
+try:
+    from django.conf.urls.i18n import i18n_patterns
+except ImportError:
+    from i18nurls.i18n import i18n_patterns
+from django.contrib import admin
+
+admin.autodiscover()
+
+urlpatterns = patterns('',
+    url(r'^admin/', include(admin.site.urls)),
+    url(r'^jsi18n/(?P<packages>\S+?)/$', 'django.views.i18n.javascript_catalog'),
+    url(r'^media/cms/(?P<path>.*)$', 'django.views.static.serve',
+        {'document_root': settings.CMS_MEDIA_ROOT, 'show_indexes': True}),
+    url(r'^media/(?P<path>.*)$', 'django.views.static.serve',
+        {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
+)
+
+urlpatterns += i18n_patterns('',
+    url(r'^', include('cms.test_utils.project.third_cms_urls_for_apphook_tests')),
+)
diff --git a/cms/tests/apphooks.py b/cms/tests/apphooks.py
--- a/cms/tests/apphooks.py
+++ b/cms/tests/apphooks.py
@@ -111,7 +111,7 @@ def test_implicit_apphooks(self):
             apphook_pool.clear()
             hooks = apphook_pool.get_apphooks()
             app_names = [hook[0] for hook in hooks]
-            self.assertEqual(len(hooks), 2)
+            self.assertEqual(len(hooks), 3)
             self.assertIn(NS_APP_NAME, app_names)
             self.assertIn(APP_NAME, app_names)
             apphook_pool.clear()
@@ -326,6 +326,22 @@ def test_apphooks_receive_url_params(self):
             self.assertTemplateUsed(response, 'sampleapp/home.html')
             self.assertContains(response, 'my_params: is-my-param-really-in-the-context-QUESTIONMARK')
 
+    def test_multiple_apphooks(self):
+        # test for #1538
+        with SettingsOverride(ROOT_URLCONF='cms.test_utils.project.third_urls_for_apphook_tests'):
+            apphook_pool.clear()
+            superuser = User.objects.create_superuser('admin', 'admin@admin.com', 'admin')
+            home_page = create_page("home", "nav_playground.html", "en", created_by=superuser, published=True,)
+            apphook1_page = create_page("apphook1-page", "nav_playground.html", "en",
+                created_by=superuser, published=True, apphook="SampleApp")
+            apphook2_page = create_page("apphook2-page", "nav_playground.html", "en",
+                created_by=superuser, published=True, apphook="SampleApp2")
+
+            reverse('sample-root')
+            reverse('sample2-root')
+
+            apphook_pool.clear()
+
 
 class ApphooksPageLanguageUrlTestCase(SettingsOverrideTestCase):
 
diff --git a/cms/tests/views.py b/cms/tests/views.py
--- a/cms/tests/views.py
+++ b/cms/tests/views.py
@@ -17,7 +17,7 @@
 
 
 class ViewTests(SettingsOverrideTestCase):
-    urls = 'cms.test_utils.project.urls_for_apphook_tests'
+    urls = 'cms.test_utils.project.urls'
 
     settings_overrides = {'CMS_PERMISSION': True}