Currently panes in a panelizer entity view mode are not supported by authcache personalization. This means that they cannot be lazy-loaded.

To make this work we would need an PER_ENTITY const in AuthcacheP13nCacheGranularity and pass the entity id to the url in AuthcacheP13nDefaultRequestUrlGenerator::url(). Further changes needed are:
1. A new AuthcachePanelizerDisplayLoader class extending AuthcachePanelsAbstractDisplayLoader.
2. To collect panelizer fragments we need to modify authcache_panels_page_manager_authcache_p13n_fragment() or add a new, authcache_panelizer module.

In case someone created a patch, would this be included in this module or should we consider creating a new contrib for this?

Comments

czigor created an issue. See original summary.

znerol’s picture

Related issue where panelizer support has been discussed: #2231701: Support Mini Panels

czigor’s picture

One big difference with Mini Panels though (if I understand correctly) is that Panelizer will need an additional query in the authcache AJAX request url for the entity_id. The use case is a view showing entities that are displayed using Panelizer.

znerol’s picture

I don't think so. Rather I guess we'd need one authcache route for every panelized node (ie., the entity id would be part of the route). This is what I'm talking about in #4 of the other thread.

However, all of this will only work if Panelizer is used as a site-building tool. It will not work well if used extensively on a growing amount of content. That is one of the reasons I'm not enthusiastic at all to support this.

czigor’s picture

Thanks for the feedback, @znerol!

Under "authcache route" you mean the things stored in the authcache_p13n_key_value table name column? What I'm thinking of is adding the entity_id to the authcache-ajax-frag span as a separate data argument instead.

That is, right now the span replaced by AJAX looks like this:
<span class="authcache-ajax-frag" data-p13n-frag="panels/my-lazy-loaded-panelized-entity-images"></span>
which could be changed to
<span class="authcache-ajax-frag" data-p13n-frag="panels/my-lazy-loaded-panelized-entity-images" data-p13n-entityid="123"></span>

This would not be a performance issue even in case of many entities on the site. What I'm not sure about is if the module is prepared for such changes or this would be hard to implement in the current framework of authcache.

znerol’s picture

@czigor I'd like to discuss this issue the other way around. What is the use case which requires an infinite number of authcache fragments on a site?

czigor’s picture

@znerol: Our use case is a taxonomy page listing nodes in panelized teaser view mode. A pane in this teaser view mode is a view displaying images and they are quite slow to load so we would like to lazy-load this pane. Other panes (like node title and description) are sent over with the initial page request.

czigor’s picture

So this is what the page looks like:

node1
node2
node3
...

where node[i] is the panelized teaser view mode:

title_pane
description_pane
images_pane_lazy_loaded

znerol’s picture

A pane in this teaser view mode is a view displaying images and they are quite slow to load

Is there personalization going on inside this view? I.e., do the images or the presentation of them depend on a per-user basis?

czigor’s picture

No, there is no personalization inside the view.

czigor’s picture

But it's quite easy to come up with a use case with personalized panelized panes. E.g a page view listing taxonomy terms listing in turn nodes tagged by that user with that term:

My tagged items

My favorites
node1
node2
node3

My non-favorites
node4
node5
node6

where "My favorites" is a teaser view mode of the "favorite" taxonomy term using panelizer to list nodes tagged by the current user with "favorite".

medinasod’s picture

I'm wondering if this the root problem of an issue I'm having right now. We use Open Atrium with Authcache for an intranet site and we have a mini panel (oa_toolbar) with a block called oa_user_badge in it, which displays the username and a menu of personalized links. When I apply Authcache to the block, it has no effect, though the route is detected:

frag/block/oa_toolbar-oa_user_badge-> AuthcacheP13nDefaultRequestHandler-> AuthcacheP13nDefaultRequestUrlGenerator-> Route Exists-> Yes

If I apply Authcache via panelizer to the oa_user_badge in the mini panel, Authcache attempts to substitute the markup, but we get this error:

"Failed to find url generator for route frag/panels/open-atrium-user-badge"

Does my issue sound related to this thread?

Thank you.

znerol’s picture

I strongly advice against trying to implement Authcache with OpenAtrium, I can only repeat what I've explained in #2474327-1: Organic Groups / Open Atrium:

At the moment I cannot give any advice on Authcache in combination with Organic Groups and/or Open Atrium. If there is a strong demand for that combination, then I'm willing to explore the options we have. However, because all this projects are rather complex, I will do this only if I get the opportunity to work together with experienced developers in that area (either maintainers or active contributors for one of the aforementioned projects).

Same goes for Panelizer.

czigor’s picture

Just for the record: We ended up using a different approach to improve performance. Anyway, after 2 days of research it seemed to me that adding Panelizer support could be done in a separate contrib module as well but it would be quite a bit of work and would need a throughout understanding of both authcache and panelizer.

medinasod’s picture

@czigor would you mind sharing your alternative approach?

Thanks.

medinasod’s picture

@znerol, just FYI, we've been using Authcache in production on a large OpenAtrium 2 intranet site for over a year. The site is pretty much unusable without it. We're using it mostly to load views on a personalized dashboard and it works perfectly in that regard. Thanks for all your work on this project.

czigor’s picture

@medinasod We would have used authcache to lazyload some computation-heavy parts of the page. Instead of doing this now we just try to make sure that the CDN cache is always warm. Fortunately, in our case that should be possible to do.

znerol’s picture

I am sure that an experienced team is capable of integrating Authcache into a complex application such as OA. However, I personally am not in a position where I could support people for free if I first have to familiarize with a software distribution I'm not using in any of my own projects.

@medinasod: Are there any docs/blogs/resources of your project I can link to?

medinasod’s picture

@znerol I mainly used this blog post to get started: https://ohthehugemanatee.org/blog/2014/06/14/how-to-configure-authcache-....

@czigor thank you. I haven't run into any problems using Authcache with views, just with the Open Atrium User Badge block which comes bundled inside of the Open Atrium Toolbar mini panel. I ended up recreating the badge in a custom module so I could display it in a block/region, which allowed me to use Authcache with it:
https://github.com/medinasod/auth-user-badge

nwom’s picture

As a workaround, I managed to keep page caching enabled, and set the pane's caching to Lazy Pane caching. This way the pages loads from page cache, and the personalized pane is then lazy-loaded. This replaced the need to have Authcache Ajax or Authcache ESI for personalization in panels as well.

sgdev’s picture

Status: Active » Needs review
Related issues: +#2231701: Support Mini Panels
StatusFileSize
new14.63 KB

I've spent a lot of time looking at this issue. I previously wrote the Panelizer support patch for the ESI module, so I thought it would be good to see if I could resolve this for Authcache.

I don't think there needs to be a PER_ENTITY, a new class, or even a new module. I'm fairly sure this can be resolved within the framework of the existing panels page manager code.

It's my opinion that the keys to resolving this are the use of the storage_type and storage_id values. Both of these pieces of information tell everything we need to know about Panelizer panes.

Have tested the attached patch in a variety of scenarios, and has worked as expected so far. I'm sure there is room for improvement, but it seems to be a very solid base for what is necessary.

This patch also includes support for Mini Panels, and is cross-referenced with issue #2231701 (https://www.drupal.org/project/authcache/issues/2231701). Unfortunately, the authcache_panels_page_manager_mini_panel_get_displays function is shared by both patches, so please take this into consideration. If the other patch gets committed, then will be able to revise this patch based on the updated version of 7.x-2.x-dev.

Have also made updates to the Admin interface and code to include a unique "frag/panelizer/..." fragment type. I found it a bit easier to keep track of the panes having them separate from standard Panels panes. There is certainly room for further improvement in this area too.

I look forward to your feedback and hopefully getting this to a point where it can finally be added to the module. Thanks for your time.

Status: Needs review » Needs work

The last submitted patch, 21: authcache-panelizer_support-2703789-21.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

sgdev’s picture

Status: Needs work » Needs review
StatusFileSize
new16.06 KB

Ok, looks like it is being strict about coding standards. Here is an update that adds proper comments for each function.

Status: Needs review » Needs work

The last submitted patch, 23: authcache-panelizer_support-2703789-22.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

sgdev’s picture

Status: Needs work » Needs review
StatusFileSize
new17.37 KB

Sorry about that, seems as though code in two functions somehow didn't get transferred into the first patch. Let's try this again.

znerol’s picture

Status: Needs review » Needs work

There are quite some suspectible things. But let's start with the most prominent one.

diff --git a/modules/authcache_panels_page_manager/includes/AuthcachePanelsPageManagerDisplayLoader.inc b/modules/authcache_panels_page_manager/includes/AuthcachePanelsPageManagerDisplayLoader.inc
index 102ba40..d0dd5cd 100644
--- a/modules/authcache_panels_page_manager/includes/AuthcachePanelsPageManagerDisplayLoader.inc
+++ b/modules/authcache_panels_page_manager/includes/AuthcachePanelsPageManagerDisplayLoader.inc
@@ -8,6 +8,7 @@
  * Personalization fragment for panel pane.
  */
 class AuthcachePanelsPageManagerDisplayLoader extends AuthcachePanelsAbstractDisplayLoader {
+  protected $paneId;
   protected $taskId;
   protected $subtaskId;
   protected $handlerId;
@@ -18,6 +19,7 @@ class AuthcachePanelsPageManagerDisplayLoader extends AuthcachePanelsAbstractDis
   public function __construct($pane_id, $task_id, $subtask_id, $handler_id) {
     parent::__construct($pane_id);
 
+    $this->paneId = $pane_id;
     $this->taskId = $task_id;
     $this->subtaskId = $subtask_id;
     $this->handlerId = $handler_id;
@@ -30,7 +32,14 @@ class AuthcachePanelsPageManagerDisplayLoader extends AuthcachePanelsAbstractDis
     $task = page_manager_get_task($this->taskId);
     $handlers = page_manager_load_task_handlers($task, $this->subtaskId);
     $handler = $handlers[$this->handlerId];
+
+    if (substr($handler->handler, 0, 10) === 'panelizer_') {
+      $did = db_query("SELECT did FROM {panels_pane} WHERE pid = :pid", array('pid' => $this->paneId))->fetchField();
+      return panels_load_display($did);
+    }
+    else {
       page_manager_get_task_handler('panel_context');
       return panels_panel_context_get_display($handler);
     }
   }
+}

This should be extracted into a separate loader. By introducing the panelizer-conditional, the function is basically split into two completely disjoint code flows which do not share any piece of data. The pane_id variable is exclusively used by the new flow while the task_id, subtask_id and handler_id variable is exclusively used by the new flow.

Also, by invoking panels_load_display() directly, you end up with a dysfunctional panel since no context will be available. In order to create a working implementation one needs to inspect how panelizer is instantiating its panels and how it sets up context.

sgdev’s picture

Ah yes, good point. We don't have any contexts, so it would make sense why that approach works without issues on our setup.

Have looked at Panelizer's code. There are several ways contexts are being extracted, and none line up directly to what would be needed here. Will have to play around with it a bit.

Also it seems like it would be necessary to support both the context of the Panelizer and of the page manager page containing the Panelizer variants? See these links:

Context for this Panelizer:
https://git.drupalcode.org/project/panelizer/blob/7.x-3.x/plugins/task_h...

Extra contexts passed from page manager to Panelizer:
https://git.drupalcode.org/project/panelizer/blob/7.x-3.x/plugins/task_h...

sgdev’s picture

Status: Needs work » Needs review
StatusFileSize
new4.37 KB
new17.8 KB

New version addressing your feedback in #26. Leaving the mini panels code in this until we get more of the panelizer issues cleaned up.

Tested including contexts in the panelizer and within the node template. Worked correctly in each case. Below are the 4 separate tests. Let me know what else needs work, would like to move forward on this. Thanks.

====================

*** TEST 1 ***
1) Added %user context to Panelizer page.
2) Added "Powered by Drupal" pane and set the title to be %user:name.
3) Cleared all Varnish cache.
4) Ran test with user with authcache-enabled role.
5) Confirmed proper user name loaded and page fully cached.

*** TEST 2 ***
1) Added "Authcache" caching per user, 5 minute duration, to the "Powered by Drupal" pane.
2) Cleared all Varnish cache.
3) Ran test with user with authcache-enabled role.
4) Confirmed proper user name loaded and page fully cached.

*** TEST 3 ***
1) Removed %user context from Panelizer.
2) Cleared all Varnish cache.
3) Ran test with user with authcache-enabled role.
4) Confirmed user name did not load, displayed "%user:name" instead.

*** TEST 4 ***
1) Added %user context to Node panelizer variant in Node template page.
2) Cleared all Varnish cache.
3) Ran test with user with authcache-enabled role.
4) Confirmed proper user name loaded and page fully cached.

znerol’s picture

It looks like this patch is a mere refactoring of #26.

sgdev’s picture

I'm confused what you mean. There are two items you mentioned in #26:

This should be extracted into a separate loader.

That has been done.

Also, by invoking panels_load_display() directly, you end up with a dysfunctional panel since no context will be available. In order to create a working implementation one needs to inspect how panelizer is instantiating its panels and how it sets up context.

Panelizer uses panels_load_display. And the way to make it "functional" is to load the context, and attach it to the $display object generated by panels_load_display.

sgdev’s picture

If you're saying, "I don't want Authcache to directly call panels_load_display and want you use another method," would prefer if you would be direct and say so.

znerol’s picture

And the way to make it "functional" is to load the context, and attach it to the $display object generated by panels_load_display

Oh, sorry I missed that bit.

das-peter’s picture

StatusFileSize
new7.36 KB
new20.62 KB

Just stumbled over this patch and gave it a try as I need that feature too.
I came across following issues:

  1. New class wasn't registered - added files[] = includes/AuthcachePanelsPageManagerPanelizerDisplayLoader.inc
  2. The loaded display didn't contain the necessary contexts - I've a quite complex setup in which a views_pane is called using a lot of contexts & tokens.
    I've changed the approach in AuthcachePanelsPageManagerPanelizerDisplayLoader::loadDisplay() to use the panelizer objects available on the entity object. Further I use the same code to prepare the display as PanelizerEntityDefault::render_entity() does.
    Unfortunately PanelizerEntityDefault::render_entity() is a monoligh and we can't re-use it to just render a single pane. Only thing I could imagine is to manipulate the display and remove all unused contents.
sgdev’s picture

StatusFileSize
new21.3 KB
new2.04 KB

@das-peter, thank you for taking time to try the patch and creating an updated version. For the first issue, I have it locally just as you referenced, but for some reason it did not get included in the patch. Thanks for catching that.

With the second issue, I agree with the extra code you have included to add further context details. When attempting to use your patch, I found two issues in the loadDisplay() function:

  1. The $display->storage_id is exploded into the following: list ($entity_type, $bundle, $view_mode, $variant). The issue is Panelizer in Drupal 7 only has three items in storage_id: $entity_type, $bundle, and $view_mode. There is no $variant, and having this included causes a watchdog error. I have removed it in the updated patch.
  2. When $view_mode in $display->storage_id returns default, it needs to be modified. The array key used in $argument_context->data->panelizer[$view_mode] will be page_manager when $view_mode = 'default', not default. Therefore, if $view_mode in storage_id returns a value of default, it needs to be changed to page_manager before proceeding. Not making this change will cause an error for all default setups.

I also fixed a spelling typo and removed commented out code later in the function. See attached, thanks.

sgdev’s picture

StatusFileSize
new21.35 KB
new820 bytes

Found a bug in patch #33, which was carried over to #34. The code will generate EntityMalformedException: Missing bundle property errors when the entity_type of the page does not match the entity_type of the panelizer context.

For example, a page can have $entity_type = 'node' based on the value pulled from $this->paneId (line 63 in AuthcachePanelsPageManagerPanelizerDisplayLoader.inc). However, the $argument_context could very well be based on a user entity, which will cause a conflict when attempting to run entity_extract_ids.

The proper fix is to check $argument_context->type, so the value of $entity_type matches the proper context entity.

See attached, thanks.

sgdev’s picture

StatusFileSize
new21.39 KB
new957 bytes

One more update. Found that if a panelizer pane contains an AJax-based block and the user is anonymous (non-authenticated), the $argument_context->data->panelizer object may not be set. This returns the following error:

Notice: Undefined property: stdClass::$panelizer in AuthcachePanelsPageManagerPanelizerDisplayLoader->loadDisplay() (line 74 of /sites/all/modules/authcache/modules/authcache_panels_page_manager/includes/AuthcachePanelsPageManagerPanelizerDisplayLoader.inc).

The way to fix this is add an isset to check for the existence of the object. See attached patch and interdiff.

sgdev’s picture

StatusFileSize
new23.27 KB
new21.38 KB
new3.68 KB

I've modified the patch to more closely align with the newest update to the Support Mini Panels patch (https://www.drupal.org/project/authcache/issues/2231701#comment-14097099). There is a lot of overlap between the two.

Also per my comment on that thread, I'm attaching a second patch for reference which combines Panelizer (#2703789) and Mini Panels (#2231701) into one patch, so it can be more clearly seen how the two fit together.

As well, I made small changes to be consistent with the rest of the module -- removed extra line breaks between protected variables and used PHP5-style arrays rather than PHP7.

See attached, thank you.

sgdev’s picture

Related issues: +#3246850: Add alter hook to reset_entity_panelizer function
StatusFileSize
new27.27 KB
new6.77 KB

Attached is an updated patch for review. We found the previous patch does not work properly when Panelizer is used to perform page overrides and revisions.

When a page override is requested, Panelizer performs a direct copy of the existing panels display and panes. The problem with this is the Authcache machine_name value for cached panes is expected to be unique. Since Panelizer simply performs a copy/paste to a new version of the page, the Authcache machine name will be the same as the original. This causes p13n fragment paths to clash across multiple Panelizer "page" versions, sometimes leading to unexpected display results.

The interdiff shows there are now submit hooks into every link and form button used by Panelizer to generate and reset page overrides. When one of these is clicked, our new code determines the machine name to be set for each Authcache-enabled pane, and modifies the panes before they are saved to the database by Panelizer.

Also to support this updated patch, we need to add a small patch for Panelizer. When the Panelizer page reset link is clicked, it leads to inappropriate results if Authcache routers are not rebuilt at the same time. The only way to do this is with an alter hook that allows us to run the rebuild when necessary.

See the Panelizer patch for more information: https://www.drupal.org/project/panelizer/issues/3246850