Search code examples
pythondjangodjango-cms

DjangoCMS - multiple sites with shared pages


I'm trying to build several websites with DjangoCMS with some shared pages. Is it possible to create a page which is shared across all django Sites?

With a basic DjangoCMS configuration, when a page is published on a Site it does not appear on other Site. I am wondering if this is configurable in any way.

When looking at the code I've seen that the TreeNode is linked to a specific Site (https://github.com/divio/django-cms/blob/develop/cms/models/pagemodel.py#L52), so I guess that if it's possible it won't be that simple.


class TreeNode(MP_Node):
    # [...]
    site = models.ForeignKey(
        Site,
        on_delete=models.CASCADE,
        verbose_name=_("site"),
        related_name='djangocms_nodes',
        db_index=True,
    )
    # [...]

I'd be fine with an external module if DjangoCMS does not handle this, or even some ideas or lead of how to approach this, I really don't have a clue.

Thanks a lot!


Solution

  • I've solved this issue with some ugly patches in the DjangoCMS code itself.

    from cms import cms_menus
    from cms.templatetags import cms_tags
    from cms.utils import page
    
    
    # Ugly-patching DjangoCMS so that a page from another Django Site can be displayed
    def new_get_page_from_path(site, path, preview=False, draft=False):
        """
        Resolves a url path to a single page object.
        Returns None if page does not exist
        """
        from cms.models import Title
    
        titles = Title.objects.select_related('page__node')
        published_only = not draft and not preview
    
        if draft:
            titles = titles.filter(publisher_is_draft=True)
        elif preview:
            titles = titles.filter(publisher_is_draft=False)
        else:
            titles = titles.filter(published=True, publisher_is_draft=False)
        titles = titles.filter(path=(path or ''))
        titles = list(titles.iterator())
    
        for title in titles:
            if title.page.node.site_id != site.pk:
                continue
    
            if published_only and not page._page_is_published(title.page):
                continue
    
            title.page.title_cache = {title.language: title}
            return title.page
    
        # This is the different part from the DjangoCMS code:
        #    re do the same loop, but this time ignore the Site filtering
        for title in titles:
            if published_only and not page._page_is_published(title.page):
                continue
    
            title.page.title_cache = {title.language: title}
            return title.page
        return
    
    
    # Ugly-patching DjangoCMS so that a page from another Django Site can fetched
    # using {% pageurl %} (for example)
    def new_get_page_queryset(site, draft=True, published=False):
        from cms.models import Page
    
        if draft:
            pages = Page.objects.drafts().on_site(site)
            if pages:
                return pages
    
        if published:
            pages = Page.objects.public().published(site)
            if pages:
                return pages
    
        pages = Page.objects.public().on_site(site)
        if pages:
            return pages
    
        # This is the different part from the DjangoCMS code:
        #    re do the same logic, but this time ignore the Site filtering
    
        if draft:
            return Page.objects.drafts()
    
        if published:
            return Page.objects.public().published()
        return Page.objects.public()
    
    
    page.get_page_from_path = new_get_page_from_path
    page.get_page_queryset = new_get_page_queryset
    cms_tags.get_page_queryset = new_get_page_queryset
    cms_menus.get_page_queryset = new_get_page_queryset
    

    Then I'm importing this file before the urlpatterns variable in the urls.py file (warned you it was ugly).

    What DjangoCMS does is that it tries to find the Page with the Site given in the request. If the Page is not found, DjangoCMS would raise a 404 error, but in our case we re-do the same query but this time without the Site filter.

    This way a Page created on one Site is accessible on each sub-Site.

    I then needed some Page accessible on every Site with most of the content identical, but SOME different content. I've solved this issue by using the static_placeholder tag which can be specified per sub-Site. http://docs.django-cms.org/en/latest/reference/templatetags.html#static-placeholder