Problem/Motivation

Metatag D7 has #2143979: Allow Views meta tags to "Use replacement tokens from the first row" we should port it.

Proposed resolution

The token form and the tokenization logic can be lifted wholesale from TokenizeAreaPluginBase, one problem appears though: while Views caches results, it does cache the rendered fields necessary for tokenization. It doesn't need that because the render cache will store the tokenized, rendered results. However, page attachments are not cached because of #2478393: hook_page_(attachments|attachments_alter|top|bottom)() should specify the right cacheability metadata. Supplying our own cache plugin is not possible because we would need one for each existing cache plugin. So the patch decorates the views cache plugin manager in order to return a class wrapping the cache plugin.

Remaining tasks

User interface changes

New tokenization options in the views form.

API changes

Data model changes

Original issue summary

In a Views metatag options, you can use global tokens from the Token module. But that doesn't do you all that much good in a view; what you really need access to are Views' own replacement tokens for fields and arguments. This way you can use anything you can get at in your View in your metadata. The Metatag settings should have a "Use replacement tokens from the first row" checkbox, the same way "Global: Text area" fields do in headers and footers.

(D8 port of #2143979: Allow Views meta tags to "Use replacement tokens from the first row")

Issue fork metatag-2952229

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

DamienMcKenna created an issue. See original summary.

lmpur’s picture

Are there any plans to implement this fix soon?

damienmckenna’s picture

I do not have a client/project which currently needs this, but I'll happily review patches if someone has the time to write it.

josh.stewart’s picture

@DamienMcKenna Any chance you've had a client/project which needed this in the past few months? Asking for a friend.

damienmckenna’s picture

None yet.

menelai’s picture

Need this feature.

solide-echt’s picture

I've given this some thought but need some guidance - at least. The current class for this setting is based on a display_extender plugin. However, in views core the TokenizeAreaPluginBase class extends AreaPluginBase so we can not simply reuse that class. IMHO tokens in views would be better handled as a trait but that seems way out of scope for this - and beyond my OOP skills too ;-)
Anyway, I simply copied the functions from TokenizeAreaPluginBase to MetatagDisplayExtender but this wreaks havoc on the jQuery. The checkbox is shown but no description of field tokens is toggled and the submission returns a 500 error. Does this mean I'm on the wrong track or simply I need to put in more effort to debug what's going wrong with the jQuery - yikes ;-)

Kind regards,
Eric

dajka’s picture

This would be a very nice to have feature! One up from me ;)

dajka’s picture

Actually... This issue is the same as this one... https://www.drupal.org/project/metatag/issues/2962890 is that right? in case yes, can we merge them?

dajka’s picture

Charlie ChX Negyesi made their first commit to this issue’s fork.

ghost of drupal past’s picture

Assigned: Unassigned » ghost of drupal past

Progress report: the tokenizer form and the tokenizer replacement code has been lifted from TokenizeAreaPluginBase, that would work but metatags runs from hook_page_attachment() completely disjoint from the main controller workflow and $this->view in MetatagDisplayExtender::getMetatags is the $view loaded by metatag_views_metatag_route_entity and it is not built/executed/rendered and so the token replacement does not work. Researching ways to preserve the first row.

ghost of drupal past’s picture

Status: Active » Needs review

I pushed a solution which preserves the rendered metatags to git not sure whether it'll show up here as a patch, this workflow is still very new to me.

ghost of drupal past’s picture

The patch has been moved to the 2952229-allow-views-meta branch so the top link works.

To describe the changes: most of it is a copy of TokenizeAreaPluginBase::tokenForm and an inlined copy of TokenizeAreaPluginBase::tokenizeValue. The interesting part is where the tokenized metatags are saved in a static class property in order to avoid executing the Views twice, this is implemented by metatag_views_views_post_render calling MetatagDisplayExtender::getMetatags which now tokenizes and saves the results. The real changes are best seen in the whitespace change suppressing diff.

damienmckenna’s picture

Excellent, thanks for sorting that out.

Could you please create a merge request with these changes so we can run them through the testbot? Thank you.

ghost of drupal past’s picture

Uncached flow:

  1. Views runs query against database
  2. Views renders the rows
  3. Metatags are tokenized and static cached in post render
  4. Metatags are pulled from static cache in attachment phase

Cached flow

  1. Views does not query because it finds the rendered HTML
  2. Metatags are pulled from static cache in attachment phase. Which is empty.

Solution: permanent the static cache. Done. Just run. What do we say to #2478393: hook_page_(attachments|attachments_alter|top|bottom)() should specify the right cacheability metadata? Not today.

damienmckenna’s picture

Status: Needs review » Needs work

Thank you for that.

Now back to needing test coverage to make sure the functionality works as intended.

ghost of drupal past’s picture

Status: Needs work » Needs review

D'oh. Had a stupid moment with the push last night but it's fixed.

ghost of drupal past’s picture

Note for reviewers: updateView starts with $title_prefix = $this->randomMachineName(); changing to this to $title_prefix = $this->randomString(); fails the test indicating foo & bar {{ title }} will get escaped in so it reads foo & bar $node_title. I think this will stay so.

ghost of drupal past’s picture

Issue summary: View changes
ghost of drupal past’s picture

Issue summary: View changes
ghost of drupal past’s picture

Issue summary: View changes

To better understand the evolution of the caching of this patch:

  1. At first I just slammed the rendered metatags into cache every time the view was rendered. The problem here is if this cache entry goes away while the view cache entry exists, things will break. Potentially storing the results in key-value would be a solution, over time, however, pollution would become a big problem.
  2. Next, which didn't address the problems in the previous points, I aligned the cache tags with the Views result object cache tags.
  3. Next I even fished out the expiration from the cache plugin despite it was not exposed in public.
  4. Finally, by doing some decoration and wrapping magic, I have taken over the cacheGet and cacheSet methods. If a cache plugin overrides these then but I simply hope this doesn't happen -- it is certainly not supposed to happen. THere are a very small number of cache plugins in use anyways.
dgwebcreative’s picture

Trying to test this but I'm not sure how it works. I've installed the forked module and have metatag_views enabled but I don't find the "Use replacement tokens from the first row" option in metatag settings or the views metatag settings. Views field tokens pasted in the metatag title are printed in raw token form {{ title }}.

ghost of drupal past’s picture

I am not sure how to help, maybe try to follow what the automated test does? We know that one works: creates a view with a page display row style titles, and then it sets the tokenize option to 1... it's weird you don't have that one. Maybe find me on slack some time between 2PM and 2AM Pacific Time and let's do a screenshare? My nick is chx.

joseph.olstad’s picture

@Charlie ChX Negyesi,

Testing this patch now, I apply the patch then rebuild cache, then I see the new option "Use replacement tokens from the first row" that shows up at the bottom of the views metatag override dialog box

As soon as I checked this box my contextual filter value showed up as I expected.

I will run more tests, so far so good though, thanks very much!

joseph.olstad’s picture

Status: Needs review » Reviewed & tested by the community

The merge request includes tests and everything works as I would expect.

Thanks so much for adding this functionality!

damienmckenna’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs tests
Parent issue: » #3203686: Plan for Metatag 8.x-1.17

Committed. Thanks everyone for your work! I added an update script to clear the caches so the functionality would be available.

damienmckenna’s picture

Assigned: ghost of drupal past » Unassigned
nikathone’s picture

I did apply the patch at https://git.drupalcode.org/project/metatag/-/merge_requests/5.diff and started to get

Notice: Trying to get property 'build_info' of non-object in Drupal\views\Plugin\views\cache\CachePluginBase->generateResultsKey() (line 198 of /var/www/app/web/core/modules/views/src/Plugin/views/cache/CachePluginBase.php)
#0 /var/www/app/web/core/includes/bootstrap.inc(305): _drupal_error_handler_real(8, 'Trying to get p...', '/var/www/app/we...', 198)
#1 /var/www/app/web/core/modules/views/src/Plugin/views/cache/CachePluginBase.php(198): _drupal_error_handler(8, 'Trying to get p...', '/var/www/app/we...', 198, Array)
#2 /var/www/app/web/modules/contrib/metatag/metatag_views/src/MetatagViewsCacheWrapper.php(92): Drupal\views\Plugin\views\cache\CachePluginBase->generateResultsKey()
#3 /var/www/app/web/core/modules/views/src/ViewExecutable.php(1419): Drupal\metatag_views\MetatagViewsCacheWrapper->cacheGet('results')
#4 /var/www/app/web/core/modules/views/src/ViewExecutable.php(1454): Drupal\views\ViewExecutable->execute(NULL)
#5 /var/www/app/web/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php(2454): Drupal\views\ViewExecutable->render()
#6 /var/www/app/web/core/modules/views/src/ViewExecutable.php(1662): Drupal\views\Plugin\views\display\DisplayPluginBase->preview()
#7 /var/www/app/web/core/modules/views_ui/src/ViewUI.php(602): Drupal\views\ViewExecutable->preview('page_1', Array)
#8 /var/www/app/web/core/modules/views_ui/src/ViewPreviewForm.php(62): Drupal\views_ui\ViewUI->renderPreview('page_1', Array)
#9 /var/www/app/web/core/lib/Drupal/Core/Entity/EntityForm.php(106): Drupal\views_ui\ViewPreviewForm->form(Array, Object(Drupal\Core\Form\FormState))
#10 /var/www/app/web/core/modules/views_ui/src/ViewFormBase.php(41): Drupal\Core\Entity\EntityForm->buildForm(Array, Object(Drupal\Core\Form\FormState))
#11 [internal function]: Drupal\views_ui\ViewFormBase->buildForm(Array, Object(Drupal\Core\Form\FormState), 'page_1')

followed by

Error: Call to a member function getCurrentPage() on null in Drupal\views\Plugin\views\cache\CachePluginBase->generateResultsKey() (line 219 of /var/www/app/web/core/modules/views/src/Plugin/views/cache/CachePluginBase.php)
#0 /var/www/app/web/modules/contrib/metatag/metatag_views/src/MetatagViewsCacheWrapper.php(92): Drupal\views\Plugin\views\cache\CachePluginBase->generateResultsKey()
#1 /var/www/app/web/core/modules/views/src/ViewExecutable.php(1419): Drupal\metatag_views\MetatagViewsCacheWrapper->cacheGet('results')
#2 /var/www/app/web/core/modules/views/src/ViewExecutable.php(1454): Drupal\views\ViewExecutable->execute(NULL)
#3 /var/www/app/web/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php(2454): Drupal\views\ViewExecutable->render()
#4 /var/www/app/web/core/modules/views/src/ViewExecutable.php(1662): Drupal\views\Plugin\views\display\DisplayPluginBase->preview()
#5 /var/www/app/web/core/modules/views_ui/src/ViewUI.php(602): Drupal\views\ViewExecutable->preview('page_1', Array)
#6 /var/www/app/web/core/modules/views_ui/src/ViewPreviewForm.php(62): Drupal\views_ui\ViewUI->renderPreview('page_1', Array)

If I removed the patch and re-import my views these two errors are gone.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

ac’s picture

I got similar issues to #32 when using this merge request. When used on a view with facets, it had the effect of hiding all the facets, even when they should be visible.

stephen ollman’s picture

On D9.3 and have recently updated to Metatag 8.x-1.18 and have just started to get this same error.

Notice: Trying to get property 'build_info' of non-object in Drupal\views\Plugin\views\cache\CachePluginBase->generateResultsKey() (line 199 of /home/XXXXX/public_html/core/modules/views/src/Plugin/views/cache/CachePluginBase.php)

Followed by:

Error: Call to a member function getCurrentPage() on null in Drupal\views\Plugin\views\cache\CachePluginBase->generateResultsKey() (line 220 of /home/XXXXX/public_html/core/modules/views/src/Plugin/views/cache/CachePluginBase.php)

No idea what has caused this but started happening post Metatag module upgrade.

Result is that Views do not display any preview result output.

oxy86’s picture

Same problem here as Stephen Ollman. Two D9.3 websites (both migrated from D7) with Metatag 8.x-1.18. When the metatag_views submodule is enabled, the preview function in all views does not work. This is the error I get:

2021/12/23 16:48:03 [error] 1494#1494: *20076595 FastCGI sent in stderr: "PHP message: Error: Call to a member function getCurrentPage() on null in /var/www/supersyntages.gr/web/core/modules/views/src/Plugin/views/cache/CachePluginBase.php on line 220 #0 /var/www/supersyntages.gr/web/modules/contrib/metatag/metatag_views/src/MetatagViewsCacheWrapper.php(92): Drupal\views\Plugin\views\cache\CachePluginBase->generateResultsKey()
#1 /var/www/supersyntages.gr/web/core/modules/views/src/ViewExecutable.php(1419): Drupal\metatag_views\MetatagViewsCacheWrapper->cacheGet('results')
#2 /var/www/supersyntages.gr/web/core/modules/views/src/ViewExecutable.php(1454): Drupal\views\ViewExecutable->execute(NULL)
#3 /var/www/supersyntages.gr/web/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php(2460): Drupal\views\ViewExecutable->render()
#4 /var/www/supersyntages.gr/web/core/modules/views/src/ViewExecutable.php(1662): Drupal\views\Plugin\views\display\DisplayPluginBase->preview()
#5 /var/www/supersyntages.gr/web/core/modules/views_ui/src/ViewU...PHP message: PHP Fatal error:  Uncaught Error: Call to a member function get() on null in /var/www/supersyntages.gr/web/core/lib/Drupal/Core/Session/SessionHandler.php:79
Stack trace:
#0 /var/www/supersyntages.gr/web/core/lib/Drupal/Core/Session/WriteSafeSessionHandler.php(95): Drupal\Core\Session\SessionHandler->write('gpAZowrZD4dgvtd...', '_sf2_attributes...')
#1 /var/www/supersyntages.gr/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php(71): Drupal\Core\Session\WriteSafeSessionHandler->write('gpAZowrZD4dgvtd...', '_sf2_attributes...')
#2 [internal function]: Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy->write('gpAZowrZD4dgvtd...', '_sf2_attributes...')
#3 [internal function]: session_write_close()
#4 {main}
  thrown in /var/www/supersyntages.gr/web/core/lib/Drupal/Core/Session/SessionHandler.php on line 79" while reading response header from upstream, client: 79.130.XXX
oxy86’s picture

Please note, that by downgrading to 1.6 solved the problem for me:

composer require 'drupal/metatag:1.16'
drush cr

Now the preview in views works as before.

eugene bocharov’s picture

Wrapping 'none' cache plugin, which is used by views in preview, cause errors.
I suggested patch at https://www.drupal.org/project/metatag/issues/3255547

baltazarz3’s picture

#37 worked for me. My watchdog logs table was not working due to an error on its ajax callback.
Thank you