I have a block view which if i enable block caching with, the slideshow no longer works - i just see a normal ul / li type listing.

is there a way to get caching working with block views and views_slideshow?

thanks

Comments

Version:6.x-1.0-beta1» 6.x-2.x-dev

Bumping to 2.x

This happened to a site of mine.
The views slideshow javascript files are not being included when a block is cached.

[oops, double post]

Status:Active» Closed (won't fix)

Block caching and js seems to be a known problem in drupal. Look at this from fivestar. http://drupal.org/node/364764

marking as won't fix as this seems to be a drupal problem and not a views_slideshow issue.

Title:Block cachingBlock caching prevents the javascript from always being loaded
Status:Closed (won't fix)» Active

I would disagree with the decision to mark this as won't fix, as it severely limits the ability for a person to use the module. While many of us recognize why we might not want to load the slideshow JS on every page, on a high-performance site, disabling the cache for a slideshow block can be worse.

Do you have any specific opposition to using a solution that is similar to the fivestar solution?

I'm not sure that loading js on every page is the best approach for this module. In 2.x you can have x number of plugins. For each plugin you enable we would have to load a js file on every page no matter if views slideshow is used or not. Another option for a user is to add the js files to their theme. This will load it on every page load to accomplish the same effect.

There is a patch that needs work in the drupal queue to fix this situation. #235673: Changes to block caching mode not caught

Do you think this is a reasonable solution?

Status:Active» Closed (won't fix)

Although a quick glance at the linked to issue queue didn't directly tell me how these issues were related, I guess adding it to the theme layer is a reasonable solution. My biggest concern here is adding the settings to the jQuery drupal options array. As it is right now, it isn't as simple as copy and pasting the module code into the theme/hook_init() of a custom module.

Status:Closed (won't fix)» Needs review
StatusFileSize
new1.29 KB

It turns out that it is a lot of work to hardcode the javascript into a custom module via hook_init. You'll notice that I had to hardcode a lot of data for performance reasons (I did not want to unnecessarily load the views object every time a user loaded the homepage).

This is my hook_init code

function MODULE_init() {
  // code for views_slideshow to prevent the caching bug
  // Most of this is copied from template_preprocess_views_slideshow_singleframe
  // in contrib/views_slideshow_singleframe/views_slideshow_singleframe.theme.inc
  // only display on the front page
  if ($_GET['q'] == 'front') {
    // Add support for the jQuery Cycle plugin.
    // If we have the jQ module installed, use that to add the Cycle plugin if possible.
    // That allows for version control.
    if (module_exists('jq')) {
      $js = jq_add('cycle');
    }
    // Otherwise, we'll add the version included with this module.
    if (!$js) {
      // Disabled JS aggregation for this module's JS since it is only loaded
      // on one page, we don't want to force users to have to load TWO
      // aggregated JS files across the site
      drupal_add_js(drupal_get_path('module', 'views_slideshow') .'/js/jquery.cycle.all.min.js', 'module', 'header', FALSE, TRUE, FALSE);
    }
    $base = drupal_get_path('module', 'views_slideshow_singleframe');
    drupal_add_js($base .'/views_slideshow.js', 'module', 'header', FALSE, TRUE, FALSE);
    drupal_add_css($base .'/views_slideshow.css', 'module');
    // Unfortunately we have to hardcode the number of divs (number of items)
    $num_divs = 3;
    // hardcoding the view id
    $view_id = 1;
    // these are the hardcoded $options from views.  If we change the settings,
    // they must be changed here.  This is the best way (for performance)
    $options = array (
      'timeout' => '4000',
      'sort' => '1',
      'effect' => 'scrollRight',
      'speed' => '700',
      'random' => '0',
      'pause' => '1',
      'controls' => '0',
      'pager' => '1',
      'sync' => '1',
    );
    $settings = array_merge(
      array(
        'num_divs' => $num_divs,
        'hoverFunction' => NULL,
        'id_prefix' => '#views_slideshow_main_',
        'div_prefix' => '#views_slideshow_div_',
        'id' => $view_id,
      ),
      $options
    );
    // load the static $javascript var
    $javascript = drupal_add_js();
    $loaded = FALSE;
    // this should never return true
    foreach ($javascript['setting'] as $setting_array) {
      if(array_key_exists('viewsSlideshowSingleFrame', $setting_array)) {
        $loaded = TRUE;
      }
    }
    unset($setting_array);
    if ($loaded == FALSE) {
      drupal_add_js(array('viewsSlideshowSingleFrame' => array('#views_slideshow_main_'. $view_id => $settings)), 'setting');
    }
  }
}

I also noticed a bug with this approach. Basically, if the block cache was cleared, it would load the settings twice, which broke the jQuery from working properly. For this reason, I had to create this (attached) patch for views_slideshow_singleframe.theme.inc

I am not necessarily saying this is the best solution, but if we want to avoid loading the JS on *every* page by default (which is reasonable), this seems to be the only way. The patch does not seem to be that burdensome and allows people to use the module on high-performance sites.

Hmm I thought drupal_add_js() handled duplicate loading.

It does, but you can also load the contents from the static $javascript variable by the method I used (so we can ensure the settings have not already been added to the array).

more info: http://us2.php.net/manual/en/language.variables.scope.php

@kmonty: What happens if you don't do it this way?

I'm using hook_init to add the js and it seems to be working fine so far. What problems might I run into?

function lntw_tweaks_init() {
  // Add JS from Views Slideshow to every page.
  // This is necessary so Views Slideshow blocks will work correctly for anonymous users when
  // page caching is turned on.
  drupal_add_js(drupal_get_path('module', 'views_slideshow') .'/js/jquery.cycle.all.min.js', 'module');
  drupal_add_js(drupal_get_path('module', 'views_slideshow') .'/js/views_slideshow.js', 'module');
}

@adamo #11

That solution does not work because it does not load the settings in the Drupal settings array. Without the settings loaded, the slideshow will not work (it does not know what divs to target).

@redndahead #9

I misread your comment. It handles duplicate loading of .js files, but not settings. You can look at the function on API.d.o and see this.

Any thoughts about committing this patch?

@kmonty I'm on the fence about it mainly because I don't quite grasp it yet. I am planning on release a beta 2 today and I don't think this one will get in, but it is one of my priorities before a full release. If someone else can corroborate that this works for them that would go a long way.

Also at a quick glance it seems it wouldn't load the second set of settings if there were 2 singleframe slideshows on one page. I haven't tested do you that might be the case?

Priority:Normal» Critical

This is the case:
blockcaching enabled and views caching (both query and output caching) turned on.

Under this situation, the module will absolutely not work. Even with the code from comment #11, the views_slideshow settings will not be loaded into the Drupal.settings array unless you manually do it via a hook_init() (as I demonstrated by my code in #8).

The module as-is is fine with one exception: when the cache is cleared (by saving a node, for example), drupal will add the views_slideshow settings to Drupal.settings twice, once from the custom hook_init() and once from the template_preprocess_views_slide() function provided by the module. The first page view (that generates the cache), the slideshow will not function due to the duplicate settings. Subsequent page loads will function correctly, as the template_preprocess_views_slide() function is not called.

Re: #15
I have not tested this yet because it has not been a part of my use-case. One possible way to get around this error would be to tag the settings array not just by the standard "viewsSlideshowSingleFrame" but "viewsSlideshowSingleFrame". $view_id. This way, when we get to array_key_exists(), the function will not return TRUE for additional slideshows.

I am marking this as critical because it should be a release blocker as the issue prevents users from using this module on a performance-minded production site.

Please let me know if I am not clear.

I've done some testing and this only seems to be a problem if you have Block Cache set to 'global'. If you set it to 'per page' it seems to work just fine. This is with full page cache, page compression, block cache, css/js optimization turned on. View level cache is not enabled because even the Views CSS wasn't getting loaded with that turned on. Please test on your end and confirm if you get the same results.

I've verified this works for me. Is this a viable solution?

Also if using Authcache you need to set block caching to "Per role, per page" for each slideshow block.

This works but it is really not ideal for performance especially when you have the same slideshow block appearing on every page for every user.

Loading the JS and settings for every views slideshow on every page is really not ideal either.

Would it be possible to embed the settings in the in a script tag in the view content? That way the settings would get cached along with the rest of the content?

Another way might be for Views Slideshow to determine what slideshow blocks appeared on a given page and load the JS and settings in hook_init(). Perhaps there could be a setting in the view... Could be a simple Load via hook_init() checkbox or could be something like the visibility settings in block configuration. Maybe the settings could be serialized and stored with variable_set() (or in your own table). The hook_init() code could check for the views_slideshow_init variable, and if present deserialize and load the settings. If the variable is empty nothing else would be done, so this shouldn't really have any impact on sites that don't need it. Deserializing and loading the settings on each page view would be better than rendering the entire block on each page view.

I tested out my idea of embedding the settings JS in the view content and it works fine. I have 3 Views Slideshow blocks cached globally and full page cache, block cache, CSS aggregation, and JS aggregation turned on. The settings will get cached with the rest of the view content. You still need to make sure the Views Slideshow CSS and JS files are loaded on each page where the slideshows appear, but this is much easier than having to hardcode all of the settings in hook_init(). If you are using Views Slideshow in blocks, you know where all of those blocks are going to be loaded and can easily implement your own hook_init() to only load the necessary CSS and JS files on the appropriate pages.

I've only done this for the singleframe slideshow at this point. The changes are relatively minor...

In views_slideshow_singleframe.theme.inc, at the bottom of function template_preprocess_views_slideshow_singleframe() replace this:

<?php
  drupal_add_js
(array('viewsSlideshowSingleFrame' => array('#views_slideshow_singleframe_main_'. $vars['id'] => $settings)), 'setting');
?>

with this:
<?php
 
/**
   * Modified to embed settings in view content so they will get cached with the view content when
   * block cache is turned on.
   */
  //drupal_add_js(array('viewsSlideshowSingleFrame' => array('#views_slideshow_singleframe_main_'. $vars['id'] => $settings)), 'setting');
  // For inline Javascript to validate as XHTML, all Javascript containing
  // XHTML needs to be wrapped in CDATA. To make that backwards compatible
  // with HTML 4, we need to comment out the CDATA-tag.
 
$embed_prefix = "\n<!--//--><![CDATA[//><!--\n";
 
$embed_suffix = "\n//--><!]]>\n";
 
$vars['js'] = '<script type="text/javascript">' . $embed_prefix
   
.'if (Drupal.settings.viewsSlideshowSingleFrame == null) {Drupal.settings.viewsSlideshowSingleFrame = {};}'
   
.'Drupal.settings.viewsSlideshowSingleFrame["#views_slideshow_singleframe_main_'. $vars['id'] .'"] = '. drupal_to_js($settings) .';'
   
. $embed_suffix . "</script>\n";
?>

Then in views-slideshow-singleframe.tpl.php, put this at the beginning:

<?php
 
print $js;
?>

I can do up a patch if you want.

Adamo - Tried your fix and it didn't work for me. :(. Any other ideas?

I think this is a different issue. I've never seen the block just disappear. When the JS isn't loaded you end up with no animation and no prev/next/pause/resume buttons. When the CSS isn't loaded you end seeing all slides in a list. Neither of these conditions results in the block completely disappearing.

Oh boy! With /everything/ disabled (caching/optimization, etc.), the slideshow STILL only displays immediately after i clear the cache manually. Thoughts? i'm not using Authcache, though i am using BlockCache Alter which i believe has the same functionality along these lines.

The plot thickens!

When i use the default Garland theme, the slideshow block works!

It is only with my custom theme that i am experiencing this issue! Should i stop posting here due to the fact that it's probably some sort of theming error (even though the theme is very minimalistic and i can find none)?

K guys - i have a possible fix for you (Anyhow, it worked for me!) Simply bypass all you've done here, and in your view, under the caching option, enable caching, for say, 1hour by 1 hour, and bam, everything works. Not joking. Maybe my problem was different from yours - but it's worth a shot, eh?

@chalja: My guess is you still had some left over cache from moving around your caching options.

Oye caching is becoming a pain and my lack of knowledge isn't helping. So far I have about enough time to answer posts. I'll try to get to some of these patches soon.

For what it's worth I don't think chalja's issues have anything at all to do with this issue.

I'll try to explain the situation and hopefully it'll be a little more clear.

Block caching is a problem because the code that adds the JS and CSS is called in the code that renders the block. When block caching is turned on, that code only gets executed when the block is initially rendered. The output of that block gets put in a blob in the cache_block table. The next time that block is loaded in a page Drupal just takes cached output from the cache_block table, and prints that in the page. Hence, drupal_add_js and drupal_add_css are not called when a block is loaded from cache, and the JS/CSS will not get added to the page.

Getting Drupal to load a couple of JS and CSS files in hook_init() on every page or just the pages where the slideshow blocks are displayed is pretty trivial. Having to hard code all of the JS settings for each slideshow block in hook_init() is not trivial.

The code I posted does pretty much the same thing that drupal_add_js does to load the JS settings into the page. The only difference is instead of it getting put in a script tag in the header of the page, it's getting put in a script tag in the block content. This gets cached along with the rest of the block content, so when Drupal loads the block content from the blob in the cache_block table, the script tag containing the code to add the appropriate settings to the Drupal.settings JS object is included in that blob. It gets executed inline when the page is loaded by the web browser, and the appropriate settings are all in the appropriate place when the code in views_slideshow.js is executed (after the entire page has been loaded).

On more thing... If you do it this way the patch that kmonty posted above isn't necessary. The settings are always loaded with the block content, so there is no chance of them getting duplicated whether or not the block has been cached.

Let me know if there's anything I should try to clarify further.

Thanks!

@chalja:
I have the view cached 1 hour/1 hour on my site, but that didn't do any good until I modified the module to put the settings JS in the view content. Those settings have to get loaded somewhere. Either it gets done when the view content is generated (which will not occur when it gets loaded from cache), or it can be hardcoded in hook_init (major pain), or it can be embedded in the view content and then it will be stored and loaded from cache with the rest of the view content. Have you tried viewing your block on a different page after it has been cached? If you have full page cache enabled the the JS will be loaded when the view content is initially generated and since it is there in the page it will get cached in the page. Next time you load that page the full page including all the JS/CSS will be loaded from cache. Then when you go to another page on the site it will load the cached view content, the code to add the JS and CSS to the page will never get called, so it will not be added to the page, and you'll end up with a broken slideshow on that page.

@adamo thank you that really helps me understand, even at 12:30 am. :)

So let me see if I get this.

1) apply patch above
2) If you cache your blocks then you need to make sure that you are adding the js files on each page through your template.

Sound about right?

Yep. Whether you are caching at the block level or view level or both, the code I posted above makes sure the settings for a slideshow are always loaded with that slideshow. You can add the JS/CSS files to your template or you can add them in your own module's implementation of hook_init().

Seems I may be able to create an option to load the needed js files on hook_init(). Is it ok to load the files there with drupal_add_js()?

Yep.

The only reason it's a problem using drupal_add_js within the view code is that it won't get called if the content is loaded from cache (it puts the JS in the page header, not in the view content). hook_init() runs on every page. It doesn't get run if the page is loaded from cache, but that's OK since the JS is included in the page header and the full page is being cached.

StatusFileSize
new13.03 KB

Here is my first stab at it. It creates settings under admin/settings/views_slideshow. In those settings you'll be able to set if the javascript files should be loaded on every page.

Testing would be great. If more is needed it would be great to know.

StatusFileSize
new14.34 KB

Forgot a file.

I tried to test this out today but I wasn't able to apply the patch successfully. If you can post a zip or tar.gz of the source tree you created the patch from I can test it out right away. Otherwise I will make the changes manually and test when I have time to dig into it. Thanks!

hi,

thanks for the awesome work on this module!

i applied the patch in #34 and it did not fix the issue. it's still loading the slideshow as a list in my block (when it's viewed from the cache).

although, i applied the patch to the beta 2 version, not the dev version (which it appears it was intended for?)

NOTE: it seems to work fine if the view is cached (i'm using 6 days/6 days), but block cache is set to "do not cache". this seems to be effective at significantly reducing the block load time without causing the slideshow to display as a list.

I think adding js and css shouldn't be in preprocess hook. This function caches and files don't add in further.

function template_preprocess_views_slideshow_singleframe(&$vars) {
  $options = $vars['options'];
  $base = drupal_get_path('module', 'views_slideshow_singleframe');
  drupal_add_js($base . '/views_slideshow.js', 'module');
  drupal_add_css($base . '/views_slideshow.css', 'module');
  $num_divs = sizeof($vars['rows']);
  $settings = array_merge(
    array(
      'num_divs' => $num_divs,
      'hoverFunction' => $hover,
      'id_prefix' => '#views_slideshow_singleframe_main_',
      'div_prefix' => '#views_slideshow_singleframe_div_',
      'id' => $vars['id'],
    ),
    $options['singleframe']
  );
  drupal_add_js(array('viewsSlideshowSingleFrame' => array('#views_slideshow_singleframe_main_'. $vars['id'] => $settings)), 'setting');
}

I've just inserted this code in views-slideshow-singleframe.tpl.php, which is in my theme folder, so I avoid all core patches:

    $base = drupal_get_path('module', 'views_slideshow_singleframe');
    drupal_add_js($base . '/views_slideshow.js', 'module');
    drupal_add_css($base . '/views_slideshow.css', 'module');
    $settings = array_merge(
      array(
        'num_divs' => sizeof($rows),
        'hoverFunction' => $hover,
        'id_prefix' => '#views_slideshow_singleframe_main_',
        'div_prefix' => '#views_slideshow_singleframe_div_',
        'id' => 1,
      ),
      $options['singleframe']
    );
    drupal_add_js(array('viewsSlideshowSingleFrame' => array('#views_slideshow_singleframe_main_1' => $settings)), 'setting');

#views_slideshow_singleframe_main_1 id is static and will work only in my case (probably :) ).

Status:Needs review» Postponed (maintainer needs more info)

Ok I committed today a patch that moves the js loading and css loading to hook_init(). Can someone who has ran into this issue before check to see if they still have the same issue? If so, can you give me the exact settings to reproduce it and I'll take a harder look now that I have a small window of available time.

Status:Postponed (maintainer needs more info)» Fixed

Marking as fixed from lack of response.

Status:Fixed» Closed (fixed)

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

On a similar note, I've used the JQuery Cycle plugin to slideshow-ize my views using list view (apart form this module) and ran into the same problem. I was not able to merely add the javascript to the views template and take advantage of normal views output for a cycle slideshow... while caching (core caching, boost and block cache alter) was on.

If anyone else decided to just use this module for inspiration, but do it themselves manually. Here is an example of the preprocess function you can use to get the js on the page in spite of block caching as blobs into the cache table.

<?php
function MYTHEME_preprocess_block(&$vars, $hook) {
  if(
$vars['block']->delta=='VIEW_NAME-block_#'):
     
jquery_plugin_add('cycle');
     
drupal_add_js(drupal_get_path('theme', 'MYTHEME') . '/scripts.js');
  endif;
}
?>

Use the blockcache_alter module and set the block to no caching and then js works fine.

Priority:Critical» Normal
Status:Closed (fixed)» Active

It appears that this issue is not fixed in 6.x-2.3, assuming that I'm having the same problem as this issue. (I assume from the dates that the patch in this issue was included in 6.x-2.3.)

I have a slideshow block on the home page. SingleFrame, with no caching for either block or base display. For the site, block caching is on, page caching is on/normal, and CSS and JS optimization are on. If I turn off JS optimization, then it works on FireFox (but IE breaks because of file number limit being exceeded). If I turn JS optimization back on and turn off block caching, then it works. The user was authenticated.

The failure happens in Drupal.behaviors.viewSlideshowSingleFrame. The fullId is not found in Drupal.settings. I assume this is because of missing JS, but I haven't looking into how this module works much.

The work-around is to disable block caching, so I changed the priority to normal.

I'm happy to provide any further information.

Status:Active» Postponed (maintainer needs more info)

It's not loading the settings. Not sure why adding settings to the page wouldn't be cached, or why a template_preprocess wouldn't be cached either. Not sure what else to try. Any caching experts out there?

Subscribing.

subscribing

Status:Postponed (maintainer needs more info)» Closed (fixed)

This probably won't get fixed in 2.x more than likely this is a views bug and views has moved on. I have definitely tested this in 3.x and it does work.

Here is how i managed it (i use context and context_block_cache_alter)

hook_context_plugins => use a new class that will extends context_block_cache_alter

In block_list function, replace $context_blocks[$r] = _block_render_blocks($blocks); by $context_blocks[$r] = YOUR_FUNCTION_render_blocks($blocks);

Now you have access to the cache_set workflow. The idea is to replace cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY); by

$js = drupal_add_js();
$array['block_js_settings'] = $js['settings']['data'];
cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);

Fianlly,
After if (isset($block->content) && $block->content) {
add

                if(isset($block->block_js_settings) && $block->block_js_settings){
                    Foreach($block->block_js_settings as $module=>$settings){
                        if(is_array($settings)){
                            Foreach($settings as $module=>$datas){
                                Foreach($datas as $key=>$value){
                                    if(!isset($js[$module][$key])){
                                        drupal_add_js(array($module => array($key => $value)), 'setting');
                                        $js[$module][$key] = true;
                                    }
                                }
                            }
                        }
                    }
                }

with $js = &drupal_static(__FUNCTION__, array()); at the top of the function.

Here is, you can now cache your block with its javascript ;-)

Arthur

Shouldn't this be moved to project Context? I came accross the same issue and had to disable block caching with project "block cache alter" for that specific block.

https://drupal.org/project/blockcache_alter

Or maybe using bloackcache_alter module and disable caching for all views slideshows is a good workaround.