The way event.css is added does not work when block caching is enabled (event.css is missing when the calender block is displayed for anonymous user when block caching is on)

Solution: implement hook_init and add the css files there with drupal_add_css

Comments

killes@www.drop.org’s picture

ugly solution, I need o think of something else.

IcyAndy’s picture

The drupal api states this about hook_init:

"For example, this hook is a typical place for modules to add CSS or JS that should be present on every page. This hook is not run on cached pages - though CSS or JS added this way will be present on a cached page."

If I understand it right it is not perfect as it is added to every page, even to pages where the block is not displayed. I don't know the caching system very well but I guess for a clean solution a core patch is needed? Something like drupal_add_block_css and drupal_add_block_js (or even better a parameter to drupal_add_css, drupal_add_js so the block specific css and js are then only cached with the block)? The solution definitely should support css aggregation if possible (drupal_build_css_cache)
But I guess it is to late for drupal 6 so this will be only cleanly solvable in 7 (unless I overlooked something, as said I'm not not a core code expert)

RobLoach’s picture

Title: Incompatible with block caching because add_css » CSS and JS for Cached Blocks (drupal_add_css incompatible with blocks)
Project: Event » Drupal core
Version: 6.x-2.x-dev » 7.x-dev
Component: Code » block.module
Priority: Normal » Critical

Moving this to the Drupal project as a core patch is required to allow cached blocks to have their own CSS and JS files included. Having the CSS and JS files added in hook_init doesn't seem like a clean solution.

Maybe something like this?

function mymodule_block($op = 'list', $delta = 0, $edit = array()) {
  $blocks = array();
  if ($op == 'list') {
    $blocks[0]['info'] = t("My module's block");
    $blocks[0]['cache'] = BLOCK_CACHE_PER_PAGE;
    $blocks[0]['css'] = array(drupal_get_path('module', 'mymodule') .'/mymodule.css');
    $blocks[0]['js'] = array(drupal_get_path('module', 'mymodule') .'/mymodule.js');
  }
  return $blocks;
}

$block['css'] provides an array of CSS files to be included when the block is shown. $block['js'] provides an array of JS files to be included when the block is shown.

add1sun’s picture

subscribing - this nipped me with nice menus upgrade.

jgoldberg’s picture

Wouldn't this be better with a callback rather than ['css'] or ['js']. For instance:

<?php
function mymodule_block($op = 'list', $delta = 0, $edit = array()) {
  $blocks = array();
  if ($op == 'list') {
    $blocks[0]['info'] = t("My module's block");
    $blocks[0]['cache'] = BLOCK_CACHE_PER_PAGE;
    $blocks[0]['init'] = 'mymodule_block_0_init';
  }
  return $blocks;
}

function mymodule_block_0_init() {
  drupal_add_css(drupal_get_path('module', 'mymodule') .'/mymodule.css'));
  drupal_add_js(drupal_get_path('module', 'mymodule') .'/mymodule.js'));
}
?>

There might be other things people want to do while still using cached blocks.

Good idea though. We've faced a simiiar question over at the Tagadelic module.

Bèr Kessels’s picture

I'd like top see one more positive test before releasing a 1.0. If no-one finds time to test before end oàf the week, i'll release a beta version by then and a stable after the beta is tested.

dmitrig01’s picture

@Bèr - Umm....

moshe weitzman’s picture

I don't find this necessary at all. A module should just do this in hook_init if the block is at all enabled. The cost is nearly free now that we aggregate css and send the right headers so the css is cached on client. This is overengineering.

Bèr Kessels’s picture

@dmitry: yea, I was being stupid, commenting in the wrong thread. Sorry 'bout that.

catch’s picture

Priority: Critical » Normal

This isn't critical.

RobLoach’s picture

moshe at #8: If the block is disabled, and the module is enabled, the CSS and the Javascript will still be included even though it isn't needed. Although the performance hit on the server side is negotiable, it would be better if it didn't even shoot it out to the user at all.

moshe weitzman’s picture

@robloach - true. yet the the proposed patch is uglier than the buglet it wants to fix.

RobLoach’s picture

Which one? $blocks[0]['css'] and $blocks[0]['js'], or $blocks[0]['init']?

eaton’s picture

If we're already returning keyed data in the block definition itself, wouldn't it make more sense to jam any CSS/JS data in there so that Drupal can keep the styles/scripts with it?

Crell’s picture

This is probably related to and better solved at: #257032: Split block $ops into separate functions

sun’s picture

This issue is not necessarily limited to blocks, it is a generic issue with caching of page elements. We had the same issue with Panels SimpleCache. There, we performed a diff between drupal_add_js()/drupal_add_css() before the block/panel is built and after it has been built. Any added JS/CSS files are stored separately, and automatically added for cached versions. Since we cannot force a block being completely generated in hook_block() (other functions as well as module_invoke_all() may be involved), this seems to be the only valid solution for this issue.

casey’s picture

$block['#attached']['css'] can be used now, right?

sun’s picture

Status: Active » Fixed

Yup! This is entirely and completely solved now. :)

RobLoach’s picture

We don't use #attached in some blocks that could use it though, hence #630100: Check non-preprocessed CSS/JS is loaded only when really needed.

Status: Fixed » Closed (fixed)

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

aidanlis’s picture

Anyone finding this issue may be confused with the solution, as #attached has nothing to do with the block system. Instead, the solution leverages the fact that Drupal7 is now using renderable arrays for all content - including blocks.

So instead of:

/**
 * Implements hook_block_view().
 */
function example_block_view($delta) {
  $basepath = drupal_get_path('module', 'example');
  return array(
    'subject' => t('Example block'),
    'content' => '<p>Your blocks HTML</p>',
  );
}

Now we can write:

/**
 * Implements hook_block_view().
 */
function example_block_view($delta) {
  $basepath = drupal_get_path('module', 'example');
  return array(
    'subject' => t('Upcoming'),
    'content' => array(
      '#markup' => '<p>Your blocks HTML</p>',
      '#attached' => array(
        'css' => array($basepath . '/example.css'),
        'js'  => array($basepath . '/example.js'),
      ),
    ),
  );
}

Hope this helps.