Problem/Motivation

This issue was initially opened due to a regression introduced in #1760384: Update node_view.inc to execute the normal Drupal hooks (in 7.x-1.6). The commit from #2437773: Attached CSS and JS files are not loaded (now in 7.x-1.8/9) largely reverses the changes from #1760384, but at the same time essentially re-opens #1760384. This also means that the initial problem from the OP of this issue is no longer present.

As a result, we need to determine if any components from #1760384 still need attention and also ensure that the behavior of node_view.inc, term_view.inc and user_view.inc (in terms of *_view hook invoking) is consistent.

Proposed resolution

It's currently proposed (#28) that the changes in #1760384 (which force entity view hooks to always be invoked for node_view pages) was essentially a mistake, and that it's the sole responsibility of plugins to invoke, or not invoke, entity view hooks. The most recent patch (#29) therefore ensures this rule is consistent for node_view.inc, term_view.inc and user_view.inc, along with some other cleanup.

Remaining tasks

We need to definitely answer the base question "should entity view hooks be triggered unconditionally in node_view, term_view and user_view pages or not"? In 7.x-1.6 the answer was "yes".

User interface changes

n/a

API changes

Other modules would no longer be able to depend on *_view hooks (e.g., hook_node_view() and hook_entity_view()) being invoked unconditionally for node_view, term_view and user_view pages.

Data model changes

n/a

Original report by rjacobs (2015-02-07)

This is a follow-up to #1760384: Update node_view.inc to execute the normal Drupal hooks. That (now closed and committed) issue made some changes to ensure that node-related hooks are triggered whenever page manager is taking over the display of a node path (node/%) even if the node itself is not rendered in the final output. Unfortunately the changes made to accommodate this introduce cases where these node-related hooks may get triggered twice for the same node, during a single request. I don't think it's safe to assume that all node hook implementations can be run redundantly on the same node, especially those that might make alterations. In our case this has led to numerous php errors popping up immediatly after updating to ctools 1.6.

To replicate:

  1. Install ctools and panels.
  2. Enable the system provided node_view page and configure it with some arbitrary layout and no selection criteria (active for all nodes).
  3. Add a variant to this node_view page that displays some panels content. Within one of the pane regions add a "rendered node" pane. This pane could show any view mode, but for testing "Full Content" is probable easiest.
  4. Set a breakpoint or dpm() call within node_build_content(), or implement either hook_node_view() or hook_entity_view() and set a breakpoint or dpm() in there.

With the above in place, you'll see that hook_node_view() and hook_entity_view() are fired twice for the same $node object variable when viewing any site node. If reverting back to ctools 1.5, or simply commenting out the first $default_output = node_page_view($node) line in page_manager_node_view_page() these invocations are only triggered once as expected.

I think the ramifications of having these hooks fired twice on the same node could range from none to dangerous. Either way my impression is that it's probably not safe to do this. We discovered this issue because we have some bootstrap (theme) related preprocessors that are triggered on book nodes within theme calls triggered from book_node_view(). These theme calls did some menu proecessing that is apparently not robust enough to happen redundantly on the same node (triggering a barrage of php notices). I don't think the specifics of our case are all that important though, as the main point is that any weird things could happen if the same hook implementation logic runs redundantly on the same variables.

There is also a performance concern here, as it seems quite wasteful to be building and rendering the same entity twice (even if the static cache ensures they it's only loaded once) when a node is only displayed once during a page request.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

rjacobs’s picture

The updated logic inside page_manager_node_view_page() calls node_page_view() at the start, which builds/renders the node and invokes all related hooks no matter what output is produced. This makes sense as a way to ensure those hooks are fired, but does not accommodate for cases where the page's configured output (via panels or some other means) also includes the content of the node who's ID is called in the node/[id] request. This use case of using page_manager to "take over" a node page, but still render the node from the path to that page, is probably quite common as many people simple want to "wrap" a normal node display with other layout logic. So it seems like this could be a common error case.

Perhaps we need a way to detect if the configurable output of the page already triggered hook_node_view() and/or hook_entity_view() and then only call node_page_view() (transparently) if that detection fails? I suppose ctools could implement it's own version of these hooks and set a static variables or something as a flag? Maybe there are more elegant ways?

rjacobs’s picture

Title: Node-related hooks can be redundantly invoked for same node in certain situations causing errors. » Entity-related hooks can be redundantly invoked for same entity in certain situations causing errors.
Related issues: +#1760578: Update user_view.inc to execute all normal Drupal hooks, +#1760584: Update term_view.inc to execute all normal Drupal hooks

It also occurs to me that this pattern of invoking entity hooks via [entity-type]_view() independent of the page output produced by page_manager may repeat as part of #1760578: Update user_view.inc to execute all normal Drupal hooks and #1760584: Update term_view.inc to execute all normal Drupal hooks. So I think those are also cases where this same problem could play out for term and user content (in addition to nodes). I'm marking those are related just in case so it can be confirmed whether-or-not they pose the same problem.

DamienMcKenna’s picture

Maybe each entity_view() call should be controllable via a variable? Of course we're also four years into the live of D7 and fixing regressions that have persisted for years, other modules may have adjusted (like Metatag did), but now that Panels has fixed the bug they have to readjust.

rjacobs’s picture

Maybe each entity_view() call should be controllable via a variable?

Do you mean a configuration option within page_manager directly (like a toggle within the system-provided node_view, term_view and user_view pages)? I guess that would be a pretty low-impact way to do things and might make more sense that trying to add logic (that would surely end up pretty convoluted) to auto-detect when to trigger these extra *_view() calls.

Before getting too far down that road though I do wonder if you agree that there is a fundamental issue here? I'm not too sure if it really is considered a bug to fire entity hooks redundantly on the same entity object. I'm just sort-of assuming that's the case given our local case-specific experience with this. My best judgement tells me it's a dangerous thing to, but I can think of soooo many different ways that could happen in the contrib space (given the static entity cache) that I wonder how preventable it really is.

Bug or no bug, I suppose there is still a performance concern here that is ctools specific.

hefox’s picture

This also causes node_tag_new being called at least two times, one early so that panes have the wrong (updated) timestamp.

redndahead’s picture

I ran into an issue where this strips off the css file added by a display suite layout. After git bisecting my way through it seems related to the issue referenced here. $default_output looks good, but ctools_context_handler_render returns an array minus the #attached value. Maybe this is a display suite issue in that they need to hook into some place else so ctools_context_handler_render works correctly or maybe it's this issue, but I thought I would bring it up.

rjacobs’s picture

Status: Active » Needs review
FileSize
3.28 KB

The way this issue manifests itself is rather tricky and case-specific. It seems like we need a patch that can at least evade the multi-invoking hook behavior so people can verify if this issue is related to their own observed problems... even if the correct way to fix things is not 100% clear yet.

I'm attaching a patch that adds a ctools_entity_view() implementation to set a static variable that tracks which entities have been "viewed" (hook_entity_view() invoked and hook_[type]_view() by inference). This variable can later be checked in page_manager_node_view_page() (or page_manager_[type]_view_page(), etc.) to see if the related entity "view" hooks were invoked. The patch only handles things for nodes, but the idea could easily be extended to terms and users as per those other related issues.

I really have no idea how close this is to a "real" fix. It may be an over-engineered fix (I'm not sure that using drupal_static() across scopes like this is a good idea), but it does fix the issue locally while retaining the requirements established in #1760384: Update node_view.inc to execute the normal Drupal hooks.

tstoeckler’s picture

When using the Webform module, which renders a form inside of node_page_view() #1760384: Update node_view.inc to execute the normal Drupal hooks causes the form to be rendered twice, which besides being wasteful results in the HTML IDs of all form elements having the nasty --2 suffix. This is not only ugly, but can lead to very hard to track JavaScript bugs.

See #2453385: Drupal.webform_conditional.getComponentsByName() fails if the webform form ID appears twice on the page for one example.

tstoeckler’s picture

The latest patch does fix my issue as well, by the way.

Mattias Holmqvist’s picture

You may want to use this patch instead of #7.

ctools_node_page_view_called_twice_2422123-10.patch

diff --git a/sites/all/modules/contrib/ctools/page_manager/plugins/tasks/node_view.inc b/sites/all/modules/contrib/ctools/page_manager/plugins/tasks/node_view.inc
index ad754e0..e2f3910 100644
--- a/sites/all/modules/contrib/ctools/page_manager/plugins/tasks/node_view.inc
+++ b/sites/all/modules/contrib/ctools/page_manager/plugins/tasks/node_view.inc
@@ -78,9 +78,6 @@ function page_manager_node_view_menu_alter(&$items, $task) {
  * node view, which is node_page_view().
  */
 function page_manager_node_view_page($node) {
-  // Prep the node to be displayed so all of the regular hooks are triggered.
-  // Also save the output for later, in case it is needed.
-  $default_output = node_page_view($node);
 
   // Load my task plugin
   $task = page_manager_get_task('node_view');
@@ -108,7 +105,7 @@ function page_manager_node_view_page($node) {
   }
 
   // Otherwise, fall back to the default output generated by node_page_view().
-  return $default_output;
+  return node_page_view($node);
 }
 
 /**
rjacobs’s picture

You may want to use this patch instead of #7.

All the moving parts are not fresh in my head right now, so forgive me if I'm missing something. Though the idea from #10 would remove this bug I think it would also introduce a regression. Namely it would re-open #1760384: Update node_view.inc to execute the normal Drupal hooks because the node/entity view hooks would only get called in the "fallback" case where no plugin is selected to render the output (which is rare). However, the patch does highlight another way to test if observed bugs are linked to this issue.

Anyway, at this point I think it's pretty safe to say that we've isolated the root cause of a fairly significant bug here. It would be interesting to learn more about the best-practice ways to address it. I think #7 is a candidate, I'm just not sure if there is a more elegant way.

michielnugter’s picture

Priority: Normal » Major
Status: Needs review » Reviewed & tested by the community

I had the same problem. I'm using the revisioning module which implements the page_manager_override hook. The nodes are rendered twice causing some very confusing bugs and a very slow website. With the patch in #7 all problems are fixed.

I tested the patch by editing / viewing various nodes (which also rely on various node_view hooks) and all seems well.

I highly recommend giving this patch priority as the effect is huge if you're site is affected..

jonraedeke’s picture

Patch #7 works for me as well. Thank you!

This was breaking a slideshow view attached to a node as an EVA field on the Default view mode and displayed in a Panels pane with any view mode. The gallery worked fine as long as that field was not set to display on the Default or Full content view modes.

rjacobs’s picture

Status: Reviewed & tested by the community » Needs work

Actually, it looks like this needs a bit more work as the current patch will not generate a "default" page title. So if you are using, say, Panels to set your layout, and have the panel configured to fall-back on the generic page title from the node referenced in the URL, no title will be generated.

Anyway, it looks like prior to #1760384: Update node_view.inc to execute the normal Drupal hooks there was an explicit call to drupal_set_title() in page_manager_node_view_page() that took care of this. After #1760384 that line was removed, most likely because the change from that issue forced node_page_view() to be called no matter what, which it seems took care of setting the default title. The whole point of this issue's patch is to not call node_page_view() unless we can confirm that it's hooks were not invoked via other means (to ensure they don't fire twice), so if we do that I guess we have to also set a fallback title manually.

Anyway, I'll try to get my patch updated to address this, though it would be useful to get some technical feedback on all this from a maintainer.

rjacobs’s picture

Status: Needs work » Needs review
FileSize
4.07 KB

Ok, here's an new version that addresses the page title issue by essentially reverting a bit more of #1760384: Update node_view.inc to execute the normal Drupal hooks. This updated patch ensures that the few lines that must always be triggered from node_page_view() are run (e.g., drupal_set_title(), etc.) while still ensuring that the entity_view hooks are only invoked once.

The fundamental strategy for checking that the entity_view hooks only fire once is the same as it was in #7.

AlexKirienko’s picture

@rjacobs, thank you for this issue and patches. I have faced problem with pager in comments #2433923: Comment Pager not working when using node_comments or node_comment_wrapper plugins.

Patch #15 works for me.

mpotter’s picture

Status: Needs review » Reviewed & tested by the community

We are using #15 in Open Atrium and it fixes a number of issues for us, including the comment pager not working as well as the node new/updated history hefox mentioned in #5. So going to mark this RTBC to get it moving forward.

sergeypavlenko’s picture

Hi all

Patch #15 works for me. Path solved problem with pager. I'm use panelizer & entity modules for display "Full content" on node page.

japerry’s picture

Status: Reviewed & tested by the community » Needs work

This needs to be re-rolled against the latest dev.

joelpittet’s picture

Status: Needs work » Needs review
FileSize
3.29 KB

Re-rolled, @mpotter and @rjacobs could you check that it's done correctly.

DamienMcKenna’s picture

Status: Needs review » Reviewed & tested by the community

Looks good.

rjacobs’s picture

Yep, the re-roll applies fine and looks consistent with my patch from #15. Thanks.

yannickoo’s picture

After debugging of hours l could finally figure out that the Ctools update breaked the node view panel when you tried to load attached CSS properly.

I didn't review the patch because I'm totally I§%$ZU§($%UITH$§IGH§W but it works :)

DamienMcKenna’s picture

dsnopek’s picture

Issue tags: +panopoly

RTBC +1

We're using this patch in Panopoly now too!

dillix’s picture

RTBC +1

andypost’s picture

Status: Reviewed & tested by the community » Needs work

Also other handlers affected (user, taxonomy)
Any reason to have this static in ctools, when used in PM only?

  1. +++ b/ctools.module
    @@ -617,6 +617,20 @@ function ctools_registry_files_alter(&$files, $indexed_modules) {
    +function ctools_entity_view($entity, $type) {
    
    +++ b/page_manager/plugins/tasks/node_view.inc
    @@ -85,29 +85,43 @@ function page_manager_node_view_page($node) {
    +  $viewed_entities = drupal_static('ctools_entity_view');
    +  if (!isset($viewed_entities['node'][$node->nid])) {
    

    that's wrong - this static is for page_manager and is not related to ctools

  2. +++ b/page_manager/plugins/tasks/node_view.inc
    @@ -85,29 +85,43 @@ function page_manager_node_view_page($node) {
    +    node_show($node); // Quick way to invoke ALL view hooks.
    

    it's too late to invoke hooks, because output already populated in task or page_view
    so that node_show() is executed second time, but could be expensive

andypost’s picture

Status: Needs work » Needs review
FileSize
3.69 KB
1.65 KB

Patch just a clean-up:

1) removed cache
- if we use ctools/plugins/content_types/node/node.inc that uses "node view" then we need to be able to render same node many times
- then ctools/plugins/content_types/node_context/node_content.inc should be able execute hooks defending on "no_extras" setting

2) clean-up implementation - if we use custom (panels/panelizer) code to view entity that's a job of task handler to setup title and add "head links", but the title is set by variant so should not be overriden dumbly

andypost’s picture

Fix for taxonomy and user as well.

if any content type plugin require to call "entity_view" it should be able

rjacobs’s picture

Status: Needs review » Needs work

Unfortunately the new ideas proposed in #27-onward would appear to also introduce a regression. Namely they would re-open #1760384: Update node_view.inc to execute the normal Drupal hooks because the node/entity view hooks would only get called in the "fallback" case where no plugin is selected to render the output.

I think this is largely going back to where we were before #1760384. In fact there are other issues that seem to be, in one way or another, trying to undo the changes from #1760384 (most recently #2437773: Attached CSS and JS files are not loaded, which btw contains a commit that sorta complicates the review process here).

So the pending problem, and complication, seems to be that we also need to address the problem that #1760384 originally set out to solve, which is to ensure entity view hooks are always fired when page manager is displaying a node page. The reason the original patches in this issue (e.g. #15) may seem slightly "heavy" (with the new static, etc.) is really because of the problem opened in #1760384.

rjacobs’s picture

Status: Needs work » Needs review

Hummmm. I'm now digesting your comment here:

then ctools/plugins/content_types/node_context/node_content.inc should be able execute hooks defending on "no_extras" setting

That's very interesting. I didn't realize that setting toggled those exact 2 hooks that were missing (node_view and entity_view), and were presumably the whole basis of the issues in #1760384: Update node_view.inc to execute the normal Drupal hooks. That might be the key to creating harmony with #1760384.

I'm wondering however if all the limitations that came up in #1760384 could be addressed with this one toggle? Are there cases where the "no extras" toggle would need to be on, but those hooks would still need to be invoked for some tracking purposes? If so, I'm guessing that would be an issue with the implementing modules, and not ctools/page-manager.

Putting back to "needs review" in light of that insight.

andypost’s picture

Are there cases where the "no extras" toggle would need to be on, but those hooks would still need to be invoked for some tracking purposes?

Another example "node_links" content type - that required to invoke hooks to populate links array, so once you put them on panel more then 2 times you get "hooks executed" twice (but links could be taken from different view modes so that's right way)

I also thinking about

with the implementing modules

metatag and others that "could" have issues with logic flow (suppose doing too much assumptions about "view" hook) must be fixed by their own (great to link them here)

rjacobs’s picture

metatag and others that "could" have issues with logic flow (suppose doing too much assumptions about "view" hook) must be fixed by their own (great to link them here)

True. It sounds like this issue could be "decoupled" from the points discussed in #1760384: Update node_view.inc to execute the normal Drupal hooks and moved forward more efficiently. However, it's worth pointing out that as it stands right now (based on this issue and the existing commit from #2437773: Attached CSS and JS files are not loaded) anyone depending on the changes previously implemented in #1760384 (ctools 7.x-1.6) is going to perceive a regression when they upgrade to ctools 7.x-1.8. They may be able to fix it with the "no extras" toggle, or via changes to the modules that depend on those *_view hooks (I think metatag already did this), but some disruption will likely result. I'm not sure what the best process is to mitigate that, but figured it was worth noting.

I posted a comment in #1760384 about this to point people here, though it may go relatively unseen without re-opening that issue.

rjacobs’s picture

Ok, finally had a chance to look at the term_view.inc and user_view.inc changes that were added to the patch in #29. From what I can see everything is consistent such that all "fallback" cases trigger default core behavior (*_view hooks are invoked, etc.) and otherwise everything is left up to whatever plugins are active with most all other assumptions eliminated. It also looks like quite a bit of baggage was stripped away from term_view.inc, which I can only assume was some legacy bloat (redundancy with taxonomy_term_page() removed).

I'm all-for moving forward with this approach, but I think others will need to comment on this (new) premise that *_view hooks are the sole responsibility of plugins (and may be conditional... see #33).

DamienMcKenna’s picture

Moving this to the v7.x-1.10 release plan.

rjacobs’s picture

Title: Entity-related hooks can be redundantly invoked for same entity in certain situations causing errors. » should entity view hooks be triggered unconditionally in node_view, term_view and user_view pages?
Issue summary: View changes

I think we are potentially playing regression tag with this issue. In order to clarify things I've created an issue summary. I hope it accurately reflects the components of this issue that remain along with the rather complex history.

samuel.mortenson’s picture

Worth noting that the patch from #20 fixed a bug reported in #2557495: Workbench information block disappeared after update to 7.39 and ctools 7.x-1.9, but the newer patches reintroduce the bug (hook_node_view is not being invoked).

mariagwyn’s picture

We attempted the patch in #20, it did not resolve the issue which is a disappearing workbench_moderation block on node and node-edit panel views (https://www.drupal.org/node/2557495)

bserem’s picture

Patch from #29 successfully solved a problem I was having, where duplicate runs made the 'for' attribute incosistent with the id of file forms.

I do not know if it introduces any other problems at the moment. I will provide more information soon, but for now I'd say it is RTBC.

bserem’s picture

Status: Needs review » Needs work

After applying this patch all term pages with a metatag configuration for being "indexed" lost that config.
Unpatched ctools behaves properly.

niko-’s picture

Hi

Do you render the term as entity or as set of fields related to entity ?

bserem’s picture

Fields related to an entity (we use the "term being viewed").

Update regarding patch #29: nodes are also affected regarding their metatags.

niko-’s picture

We have some ctools special things here

In short:
1. If any page shows using page manager + panels my debug told me that call of core hooks is nonsense on pagemanager level because pagemanager got already rendered entity or panes in the panel and it's too late to call node hooks.
2. You can use pane named "Rendered Content" from group "Entity" so all core hooks will be called as usual

Please check #28 for details.

niko-’s picture

And also If you need cover node or any other entity with panels please use panelizerso you can play with entity layout in panel way

DamienMcKenna’s picture

This didn't get added to 7.x-1.10.

ron_s’s picture

I'm guessing this one has fallen through the cracks?

ron_s’s picture

Patch #20 still applies cleanly to 7.x-1.15.

ron_s’s picture

Patch #20 still applies cleanly to 7.x-1.17.

q11q11’s picture

@ron_s
> Patch #20 still applies cleanly to 7.x-1.17.
There are offsets in "ctools.module" and "page_manager/plugins/tasks/node_view.inc" while patching. But since it not failing completely, it's ok.
And I agree, nobody to care about D7 modules when D8 core support will be dropped in about 10 month.

Summit’s picture

Hi, I thought the drop of D7 was postponed to November 2022, so I think maintaining is still relevant: https://www.drupal.org/psa-2019-02-25
greetings,

ThomasDik’s picture

Altered patch for latest dev.

q11q11’s picture

Just combination of #20 and #51 against 7.x-1.19.