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

Issue with custom TwigFunction after CKEditor 4.1.0 #248

Closed
thomascoppein opened this issue Jun 13, 2024 · 19 comments
Closed

Issue with custom TwigFunction after CKEditor 4.1.0 #248

thomascoppein opened this issue Jun 13, 2024 · 19 comments
Labels

Comments

@thomascoppein
Copy link

thomascoppein commented Jun 13, 2024

Description

Since the latest update to CKEditor 4.1.0, I have encountered an issue with a custom module that provides a Twig function for use in my longform components. This functionality was working correctly in the previous version of CKEditor.

Steps to reproduce

  1. Create custom module with TwigFunction
  2. Init module
  3. Call Twig function in component

Additional info

  • Craft version: 5.2.0
  • PHP version: 8.2
  • Database driver & version: MYSQL
  • Plugins & versions:
@i-just
Copy link
Contributor

i-just commented Jun 14, 2024

Hi, @thomascoppein, what’s the issue you’ve encountered? Can you please provide more info on this?

@thomascoppein
Copy link
Author

thomascoppein commented Jun 14, 2024

Hi @i-just , when adding a new Entry to the CKEditor field I get an Twig Syntax Error that my function is unknown.
While in the previous version I never encountered this problem. The function mentioned in the error is from a custom module thats loaded into my project.

@i-just
Copy link
Contributor

i-just commented Jun 14, 2024

Can you please share the stack trace for this?

@thomascoppein
Copy link
Author

Yes, it appears when I save in the admin environment.

Twig\Error\SyntaxError: Unknown "classNames" function. Did you mean "className"? in /Users/user/Documents/workspace/project-name/templates/components/button/index.twig:32
Stack trace:
#0 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/ExpressionParser.php(477): Twig\ExpressionParser->getFunctionNodeClass('classNames', 32)
#1 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/ExpressionParser.php(240): Twig\ExpressionParser->getFunctionNode('classNames', 32)
#2 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/ExpressionParser.php(177): Twig\ExpressionParser->parsePrimaryExpression()
#3 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/ExpressionParser.php(72): Twig\ExpressionParser->getPrimary()
#4 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/ExpressionParser.php(406): Twig\ExpressionParser->parseExpression()
#5 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/ExpressionParser.php(283): Twig\ExpressionParser->parseHashExpression()
#6 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/ExpressionParser.php(177): Twig\ExpressionParser->parsePrimaryExpression()
#7 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/ExpressionParser.php(72): Twig\ExpressionParser->getPrimary()
#8 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/web/twig/tokenparsers/TagTokenParser.php(45): Twig\ExpressionParser->parseExpression()
#9 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Parser.php(170): craft\web\twig\tokenparsers\TagTokenParser->parse(Object(Twig\Token))
#10 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Parser.php(83): Twig\Parser->subparse(NULL, false)
#11 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Environment.php(490): Twig\Parser->parse(Object(Twig\TokenStream))
#12 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Environment.php(518): Twig\Environment->parse(Object(Twig\TokenStream))
#13 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/web/twig/Environment.php(39): Twig\Environment->compileSource(Object(Twig\Source))
#14 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Environment.php(350): craft\web\twig\Environment->compileSource(Object(Twig\Source))
#15 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Template.php(322): Twig\Environment->loadTemplate('__TwigTemplate_...', 'components/butt...', NULL)
#16 /Users/user/Documents/workspace/project-name/storage/runtime/compiled_templates/62/62cc9a51907b70483f70365af88c7367.php(38): Twig\Template->loadTemplate('components/butt...', '_partials/entry...', 1)
#17 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Template.php(394): __TwigTemplate_13d572a7bf5b754e860f9e2582dae3a6->doDisplay(Array, Array)
#18 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Template.php(367): Twig\Template->displayWithErrorHandling(Array, Array)
#19 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Template.php(379): Twig\Template->display(Array)
#20 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/TemplateWrapper.php(38): Twig\Template->render(Array)
#21 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Environment.php(280): Twig\TemplateWrapper->render(Array)
#22 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/web/View.php(488): Twig\Environment->render('_partials/entry...', Array)
#23 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/helpers/ElementHelper.php(930): craft\web\View->renderTemplate('_partials/entry...', Array, 'site')
#24 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/base/Element.php(6117): craft\helpers\ElementHelper::renderElements(Array, Array)
#25 /Users/user/Documents/workspace/project-name/vendor/craftcms/ckeditor/src/data/Entry.php(37): craft\base\Element->render()
#26 /Users/user/Documents/workspace/project-name/vendor/craftcms/ckeditor/src/data/FieldData.php(175): craft\ckeditor\data\Entry->getHtml()
#27 [internal function]: craft\ckeditor\data\FieldData->craft\ckeditor\data\{closure}(Object(craft\ckeditor\data\Entry), 1)
#28 /Users/user/Documents/workspace/project-name/vendor/illuminate/collections/Arr.php(600): array_map(Object(Closure), Array, Array)
#29 /Users/user/Documents/workspace/project-name/vendor/illuminate/collections/Collection.php(777): Illuminate\Support\Arr::map(Array, Object(Closure))
#30 /Users/user/Documents/workspace/project-name/vendor/craftcms/ckeditor/src/data/FieldData.php(175): Illuminate\Support\Collection->map(Object(Closure))
#31 /Users/user/Documents/workspace/project-name/vendor/craftcms/ckeditor/src/data/FieldData.php(49): craft\ckeditor\data\FieldData->render()
#32 [internal function]: craft\ckeditor\data\FieldData->__toString()
#33 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Extension/CoreExtension.php(1175): strip_tags(Object(craft\ckeditor\data\FieldData), NULL)
#34 /Users/user/Documents/workspace/project-name/storage/runtime/compiled_templates/44/44d436787c4005f7838d411241f81c4a.php(38): twig_striptags(Object(craft\ckeditor\data\FieldData))
#35 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Template.php(394): __TwigTemplate_0e2889f8bb2ebc0c7e3db1851f091f3b->doDisplay(Array, Array)
#36 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Template.php(367): Twig\Template->displayWithErrorHandling(Array, Array)
#37 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/Template.php(379): Twig\Template->display(Array)
#38 /Users/user/Documents/workspace/project-name/vendor/twig/twig/src/TemplateWrapper.php(38): Twig\Template->render(Array)
#39 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/web/View.php(671): Twig\TemplateWrapper->render(Array)
#40 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/elements/Entry.php(2162): craft\web\View->renderObjectTemplate('{{ (_variables....', Object(craft\elements\Entry))
#41 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/elements/Entry.php(2287): craft\elements\Entry->updateTitle()
#42 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/services/Elements.php(3438): craft\elements\Entry->beforeSave(false)
#43 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/services/Elements.php(1251): craft\services\Elements->_saveElementInternal(Object(craft\elements\Entry), true, true, NULL, NULL, false, true, true)
#44 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/controllers/ElementsController.php(1246): craft\services\Elements->saveElement(Object(craft\elements\Entry), true, true, NULL, false, true)
#45 [internal function]: craft\controllers\ElementsController->actionSave()
#46 /Users/user/Documents/workspace/project-name/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#47 /Users/user/Documents/workspace/project-name/vendor/yiisoft/yii2/base/Controller.php(178): yii\base\InlineAction->runWithParams(Array)
#48 /Users/user/Documents/workspace/project-name/vendor/yiisoft/yii2/base/Module.php(552): yii\base\Controller->runAction('save', Array)
#49 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/web/Application.php(349): yii\base\Module->runAction('elements/save', Array)
#50 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/web/Application.php(650): craft\web\Application->runAction('elements/save', Array)
#51 /Users/user/Documents/workspace/project-name/vendor/craftcms/cms/src/web/Application.php(311): craft\web\Application->_processActionRequest(Object(craft\web\Request))
#52 /Users/user/Documents/workspace/project-name/vendor/yiisoft/yii2/base/Application.php(384): craft\web\Application->handleRequest(Object(craft\web\Request))
#53 /Users/user/Documents/workspace/project-name/web/index.php(17): yii\base\Application->run()
#54 /Users/user/.composer/vendor/laravel/valet/server.php(110): require('/Users/user...')
#55 {main}

@mmikkel
Copy link

mmikkel commented Jun 14, 2024

Ran into something similar. In my case the issue was that I was conditionally bootstrapping my custom Twig extension for site requests only, i.e.

if (Craft::$app->getRequest()->getIsSiteRequest()) {
    Craft::$app->getView()->registerTwigExtension(new CustomTwigExtension());
}

Removing that condition (making sure that the custom Twig extension is registered for any and all requests) fixed the issue on my end.

@i-just
Copy link
Contributor

i-just commented Jun 14, 2024

Thanks, @mmikkel - that’s a very good point.

@thomascoppein - based on the stack trace, I can see that you’re using an Entry Type with an auto-generated title. To be clear, do you get this error when saving the nested entry? What’s the “Title Format” that entry?

@thomascoppein
Copy link
Author

thomascoppein commented Jun 14, 2024

@i-just - yes, that's correct. The error persists even after updating the title to static text. The error now occurs differently; it is now present in the entry card. See screenshot below.

The title of this entry type is 'Button'. Previously: {buttonLabel} | {buttonEntry.one().url|default('')}{buttonAsset.url|default('')}{buttonUrl|default('')}

Screenshot 2024-06-14 at 13 20 34

Update: by changing the code as @mmikkel suggested, this error is resolved. However, a new error has appeared. I am using the nocache plugin, and now this behavior is 'unexpected'.

Screenshot 2024-06-14 at 13 27 57

@brandonkelly
Copy link
Member

@thomascoppein Based on your stack trace, this is happening for an entry type whose title format begins with {{ (_variables.. Can you post the full title format?

@thomascoppein
Copy link
Author

@brandonkelly yes sure: {buttonLabel} | {buttonEntry.one().url|default('')}{buttonAsset.url|default('')}{buttonUrl|default('')}.

@mmikkel
Copy link

mmikkel commented Jun 18, 2024

However, a new error has appeared. I am using the nocache plugin, and now this behavior is 'unexpected'

Looks like basically the same thing; the Nocache plugin only registers its Twig extension for site requests: https://github.com/ttempleton/craft-nocache/blob/52e99ca49d1e6fcc12e4f92d0b8c24ca4ae5b641/src/Plugin.php#L41

I.e., something is causing your nested CKEditor entries to render their partial templates when you save the entry. Presumably you use the {% nocache %} tag somewhere in those templates, hence that exception as that tag does not actually exist due to the rendering happening outside of a site request.

In my case it was search index jobs failing, which made sense to me as I assume Craft is rendering CKEditor content for fields that are indexable. Not sure what's going on in your case though, and I'm also pretty sure this behaviour wasn't occurring prior to CKEditor 4.1.

@thomascoppein
Copy link
Author

@mmikkel Yes, that's correct. This issue did not exist prior to CKEditor 4.1.
Do you need more information @brandonkelly ?

@thomascoppein
Copy link
Author

@i-just could you provide an update about this problem since it's breaking some logic which was working prior to version 4.1?
Why can't TwigExtensions be in this if statement?

if (Craft::$app->getRequest()->getIsSiteRequest()) {
    Craft::$app->getView()->registerTwigExtension(new TwigExtension());
}

@i-just
Copy link
Contributor

i-just commented Sep 18, 2024

Hi @thomascoppein,

Why can't TwigExtensions be in this if statement?

It’s like @mmikkel said. If you wrap registration of the twig extension in the getIsSiteRequest() check, then all the goodness from the extension will only be available for the front-end requests. When you add an entry to a CKEditor and the rendering of twig happens (because, for example, you’re using an auto-generated title for the Title of the nested entry), the request won’t be classed as a front-end one, which will lead to the error.

I hope this helps. Are you still having issues after mitigating the problem of the twig extension only being available for the front-end requests?

@thomascoppein
Copy link
Author

Hi @i-just ,
I believe I understand most of the situation. The issue seems to be resolved when removing the if statement from the plugin, so that’s great.
However, there’s still one part that’s unclear to me. I have an Entry Type named “Button” with the title enabled (so it’s not auto-generated). The code in my button.twig file is as follows:

{% nocache %} Test {% endnocache %}

Despite this, I’m still encountering the following error:

image

My question is: why is it still throwing this error? Or perhaps the better question is: why does it still renders the twig code while in the editor?

@mmikkel
Copy link

mmikkel commented Sep 19, 2024

It’s like @mmikkel said. If you wrap registration of the twig extension in the getIsSiteRequest() check, then all the goodness from the extension will only be available for the front-end requests. When you add an entry to a CKEditor and the rendering of twig happens (because, for example, you’re using an auto-generated title for the Title of the nested entry), the request won’t be classed as a front-end one, which will lead to the error.

I haven't dug into it, but I assume that what's happening here is that Craft is rendering the nested entries when the owner entry is being search indexed (explains how this occurs on entry save). Or something similar to that.

It's not clear to me why this only started happening w/ the CKEditor 4.1 update, but regardless:

Considering that partial templates are generally assumed to render on the front end only (they live in the sites template folder, after all), would it make sense if Craft basically faked a site request whenever it has to render these templates under the hood (for example, when entries are saved or re-saved from the CP or CLI)?

In @thomascoppein's case, NoCache is a publicly available plugin and I assume they have little control over how that plugin bootstraps its Twig extension. And more generally; implicitly prohibiting partial templates from containing any code that will fail for a non-site request, seems like something that's going to generate a lot of gotchas going forward, considering that partial templates is a front end concept to begin with? Most developers are probably not going to be aware that they need to make sure that their partial templates doesn't contain any code (first or third party) that will trigger an error for CP or CLI requests...

@i-just
Copy link
Contributor

i-just commented Sep 20, 2024

@mmikkel - the search index issue was fixed in 4.2.0: “Fixed a bug where CKEditor fields’ search keywords were including nested entries’ rendered partial templates rather than nested entries’ search keywords.”

@thomascoppein - I think I might be missing something here; I can’t reproduce the partials getting rendered when adding a nested entry to a CKEditor field (on 4.1.0 or 4.2.0 versions).

Can you please let me know if this is correct:

  • You are using ttempleton/craft-nocache plugin.
  • You have a CKEditor field which is configured to allow adding entries to it.
  • One of the entry types allowed inside that CKEditor field has a handle of button. Show Title, Slug and Status fields are toggled on (default).
  • You have a partial template (e.g. _partials/entry/button.twig) with contents of {% nocache %} Test {% endnocache %}
  • You edit an entry that contains this CKEditor field and proceed to add a nested button entry to it. When you save the nested button entry, the card you see in CKEditor contains the error (about an unknown {% nocache %} tag), instead of rendering the title of the entry.

Is the above correct or have I missed or got anything wrong?
If you disable all the custom modules and plugins apart from CKEditor and No-Cache, does the issue persist?
Have you tried updating the CKEditor plugin to 4.2.0, and if so, does the issue persist?

@thomascoppein
Copy link
Author

@i-just - CKEditor has been updated to version 4.2.0, but unfortunately, the issue persists.

  • You are using ttempleton/craft-nocache plugin.
    Yes, that's correct
  • You have a CKEditor field which is configured to allow adding entries to it.
    Yes, also correct
  • One of the entry types allowed inside that CKEditor field has a handle of button. Show Title, Slug and Status fields are toggled on (default).
    Yes, also correct
  • You have a partial template (e.g. _partials/entry/button.twig) with contents of {% nocache %} Test {% endnocache %}.
    Yes, also correct
  • You edit an entry that contains this CKEditor field and proceed to add a nested button entry to it. When you save the nested button entry, the card you see in CKEditor contains the error (about an unknown {% nocache %} tag), instead of rendering the title of the entry.
    Yes, also correct

After some investigation, I identified the root of the problem. I’m working with a Matrix field that contains an entry type called “Text,” which uses a CKEditor field. The Title Format for this entry type is dynamic, defined as {richText|striptags|slice(0,100)}. This setup allows the addition of a “Button” entry type within the CKEditor content.

However, when the Title Format is configured this way, the issue arises. The problem disappears when I switch to a default Title Format or a regular input field. While it’s not always necessary for users to manually enter a title, this workaround resolves the issue for now.

@i-just
Copy link
Contributor

i-just commented Sep 20, 2024

Thank you for confirming and for the additional info on your setup - that was the missing piece of the puzzle!

What happens here is - you’ve set the title of the “Text” entry type to be dynamic and to use the content of the CKEditor field. When using dynamic titles, your “pattern” is rendered to find out what the result should be (more info).

Your pattern references a CKEditor field, which triggers the rendering of the content of that field. That means your button.twig partial gets rendered (as you have an entry of that type inside the CKEditor field), and that results in the error you’re seeing.

This setup started to be problematic for you because CKEditor 4.1.0 introduced the concept of chunks.

@brandonkelly
Copy link
Member

The reason this issue cropped up in 4.2.0 is because prior to that, when your CKEditor field was getting rendered for the Title Format, in was assumed that it was being rendered for the CP field input. So nested entries were getting rendered as cards, when they should have been rendered using the partial templates. 4.2.0 fixed that, which exposed ttempleton/craft-nocache#31.

Closing this as technically everything is working as expected now (besides the No Cache issue).

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