Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] AppHook'ed Views can't use placeholders in their templates #7712

Open
1 of 2 tasks
jrief opened this issue Dec 6, 2023 · 10 comments
Open
1 of 2 tasks

[BUG] AppHook'ed Views can't use placeholders in their templates #7712

jrief opened this issue Dec 6, 2023 · 10 comments
Labels

Comments

@jrief
Copy link
Contributor

jrief commented Dec 6, 2023

Description

An AppHook'ed View can't use any CMS placeholders in their templates anymore.

This used to work in CMS version 3.

Steps to reproduce

  • Create an AppHook class and register it
  • Method get_urls of that class returns a list of urlpatterns.
  • The URL attached to the CMS page typically looks like
@apphook_pool.register
class MyApp(CMSApp):
    def get_urls(self, page=None, language=None, **kwargs):
        return [
            ...,
            path('', MyView.as_view()),
            ...,
        ]

Now if the MyView class defines a template and that template contains a CMS {% placeholder … %}-templatetag, then function get_declared_placeholders_for_obj raises an exception.

There is a workaround by populating the toolbar with the current PageContent object, but in my opinion this should be part of the CMS logic and must not be done by the application.

Expected behaviour

No exception and the name placeholder should be editable.

Do you want to help fix this issue?

  • Yes, I want to help fix this issue and I will join #workgroup-pr-review on Slack to confirm with the community that a PR is welcome.
  • No, I only want to report the issue.
@fsbraun fsbraun added the 4.1 label Dec 7, 2023
@fsbraun
Copy link
Sponsor Member

fsbraun commented Dec 7, 2023

Apphooks should use the render_placeholder template tag. The reason is that the placeholders must be attached to the right model - and for your apphook it should be its model (not the PageContent). In django CMS 4 also models that are not rendered in views can be frontend editable (such as alias).

To resolve this, you will need to add a get_template() method to the model of your apphook, like, e.g.,

def get_template(self):
    return "my_app/model_content.html"

and in my_app/model_content.html you'll just place the placeholder (nothing else), e.g. (taken from djangocms-alias),

{% load cms_tags %}{% placeholder "content" %}

Your apphook will have to have a model that has a PlaceholderRelationField as described in the docs here.

The get_template part is something that still needs to go into the docs...

@jrief
Copy link
Contributor Author

jrief commented Dec 7, 2023

Apphooks should use the render_placeholder template tag.

But how does one then edit the content of this placeholder? In CMS3 this content then was stored together with the page (aka. PageTitle).

I know that it also is possible to create placeholders outside the PageContent model, but that means adding another field, etc.

Anyway, I found a not very elegant solution. I created a mixin class to be added to the MyView class as specified above. This mixin class looks like:

class PageContentMixin:
    def render_to_response(self, context, **response_kwargs):
        if hasattr(self.request, 'toolbar'):
            match = resolve(self.request.path)
            if match.url_name == 'cms_placeholder_render_object_edit':
                page_content = PageContent.admin_manager.get(id=match.args[1])
            else:
                language = get_language_from_request(self.request, check_path=True)
                if hasattr(self, 'page'):
                    page_content = self.page.pagecontent_set.get(language=language)
                else:
                    page_content = self.request.current_page.pagecontent_set.get(language=language)
            self.request.toolbar.set_object(page_content)
        return super().render_to_response(context, **response_kwargs)

do you see any problems using this solution?

@fsbraun
Copy link
Sponsor Member

fsbraun commented Dec 7, 2023

The toolbar is not necessarily available (or rather be an EmptyToolbar), I fear. Will that work with published pages and no user logged in? Also, it means that you cannot frontend edit your app's model since you replace the toolbar object.

While storing the placeholder in the page content apparently was possible, I am not sure if it ever was a documented feature. Additionally, there is only one page content object for the whole of the app which might provide many more URLs. The "right" way for me is to add the field to the model and have that migration. If you've got prepopulated fields, the migration might need some additional code to move the placeholders to the new model.

@jrief
Copy link
Contributor Author

jrief commented Dec 7, 2023

The "right" way for me is to add the field to the model and have that migration.

For a possible "Detail" view, adding a PlaceholderField to a model might be a viable solution. But how would you proceed if the Apphook's "root"-URL points onto a list view? This btw. is a typical use-case for Apphooks.

@marksweb
Copy link
Member

marksweb commented Dec 7, 2023

As far as I was aware, placeholders couldn't/shouldn't have been used in CMS apps in version 3.

See the docs for "placeholders outside the cms" - https://docs.django-cms.org/en/latest/how_to/placeholders.html?highlight=placeholders%20outside%20cms

Placeholders can be viewed as containers for CMSPlugin instances, and can be used outside the CMS in custom applications using the PlaceholderField.

@jrief
Copy link
Contributor Author

jrief commented Dec 12, 2023

@marksweb The documentation you're referring to is for CMS3 and there I have no problems. I'm using CMS4 with versioning and there the trouble starts.

@makjaveli
Copy link

Your apphook will have to have a model that has a PlaceholderRelationField as described in the docs here.

The get_template part is something that still needs to go into the docs...

Ok, is there a working example of PlaceholderRelationField?

I unfortunately could not create one following the referred docs using django-cms 4.1.0 with a clean project and a simple apphook.

The main problem is that the resulting page is not 'editable' in the toolbar. I can provide my current non working example if useful.

@fsbraun
Copy link
Sponsor Member

fsbraun commented Jan 3, 2024

@makjaveli I agree, a working example in the docs would make things a lot easier. It would be a great addition for the how-to section! For now, I am happy to try and help you out with your example.

@makjaveli
Copy link

@makjaveli I agree, a working example in the docs would make things a lot easier. It would be a great addition for the how-to section! For now, I am happy to try and help you out with your example.

That would be great. Here is the url to a minimal django-cms4 app_hook and render_placeholder example:

https://github.com/makjaveli/djangocms_apphook_renderplaceholder

It should be runable out of the box. All steps of the current documentation were applied but currently it is not working. The visible reason is that the page is not editable in the cms toolbar so no content can be added to the placeholder.

djangocms_renderplaceholder_not_editable

Any hints what is missing would be appreciated. I would also file a new issue if this is not directly related to this issue.

@fsbraun
Copy link
Sponsor Member

fsbraun commented Jan 4, 2024

I've added this short issue here: #7736 Please feel free to comment.

To get you going quickly, I've created a PR for your example repo that does the trick. A few steps were necessary:

  • Declaration of the Product model as frontend-editable in cms_config.py
  • Adding a get_template() method to the model
  • Adding a structure mode template for identifying the placeholders (shop_app/product.html)
  • Adding a rendering method for the preview and edit endpoints
  • Finally, to make the Preview and Edit buttons appear on the published URL: Add the product instance to the toolbar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants