You are here

dynamicload.module in Javascript Tools 5

Enable AJAX-based loading of selected page elements.

File

dynamicload/dynamicload.module
View source
<?php

/**
 * @file
 * Enable AJAX-based loading of selected page elements.
 */
define('DYNAMICLOAD_HTML', 1);
define('DYNAMICLOAD_APPEND', 2);
define('DYNAMICLOAD_PREPEND', 3);
define('DYNAMICLOAD_BEFORE', 4);
define('DYNAMICLOAD_AFTER', 5);

/**
 * Implementation of hook_help().
 */
function dynamicload_help($section) {
  $return = array();

  // Add marker to every page.
  if ($section == $_GET['q']) {
    $return[] = '<div id="dynamicload-content-marker"></div>';
  }
  if ($section == 'admin/settings/dynamicload') {
    $return[] = t('<p>Full dynamic loading is <em>experimental</em> and may break sites at this point. The options on this page are not recommended for use on production sites. Work is ongoing to identify and fix remaining issues.</p>
    <p>Configure dynamic loading of individual blocks on the <a href="@config">block configuration</a> pages.</p>', array(
      '@config' => url('admin/build/block'),
    ));
  }
  if (!empty($return)) {
    return implode('', $return);
  }
}

/**
 * Implementation of hook_menu().
 */
function dynamicload_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'dynamicload/js',
      'title' => t('Dynamicload page'),
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK,
      'callback' => 'dynamicload_js',
    );
    $items[] = array(
      'path' => 'dynamicload/block',
      'title' => t('Dynamicload block'),
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK,
      'callback' => 'dynamicload_fetch_block',
    );
    $items[] = array(
      'path' => 'admin/settings/dynamicload',
      'title' => t('Dynamicload'),
      'description' => t('Configuration for dynamic loading'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'dynamicload_admin_settings',
      ),
    );
  }
  else {

    /*
        if (isset($_COOKIE['has_js']) && $_COOKIE['has_js'] && variable_get('dynamicload_all', 0)) {
          // If we are on a page other than home, go home, with this page as an anchor/hash.
          if (!isset($_SESSION['dynamicload_path']) && !drupal_is_front_page() && arg(0) != 'dynamicload') {
            $query = $_GET;
            unset($query['q']);
            $querystring = array();
            foreach ($query as $key => $value) {
              $querystring[] = $key .'='. $value;
            }
            $path = drupal_get_path_alias($_GET['q']);
            $_SESSION['dynamicload_path'] = $path;
            drupal_goto('', implode('&', $querystring), $path);
          }
          elseif (isset($_SESSION['dynamicload_path']) && !(isset($_SESSION['dynamicload_path']) && $_SESSION['dynamicload_path_active'])) {
            $_SESSION['dynamicload_path_active'] = TRUE;
            $path = $_SESSION['dynamicload_path'];
            $_GET['q'] = drupal_get_normal_path($path);
            _menu_append_contextual_items();
            menu_set_active_item($path);
            unset($_SESSION['dynamicload_path_active']);
            unset($_SESSION['dynamicload_path']);
          }
        }
    */
    dynamicload_load();
  }
  return $items;
}

/**
 * Admin settings.
 */
function dynamicload_admin_settings() {
  $form = array();
  $form['jstools_history_remote'] = array(
    '#type' => 'radios',
    '#title' => t('History plugin'),
    '#description' => t('Load the history_remote plugin, which ties dynamic loading to browser history. Dynamicload will be missing some functionality if this is disabled. However, the plugin can cause errors in combination with certain other Javascript functionality. Disable to avoid such errors. Note that disabling this setting will affect other modules if you enable them: Active Search and Tabs.'),
    '#default_value' => variable_get('jstools_history_remote', 0),
    '#options' => array(
      t('disabled'),
      t('enabled'),
    ),
  );
  $form['dynamicload_all'] = array(
    '#type' => 'checkbox',
    '#title' => t('Process all links for dynamic loading'),
    '#description' => t('Check this option to make all links on the page dynamically load into the main content area. Links processed through block configuraiton will be skipped. You can also configure by paths, below.'),
    '#default_value' => variable_get('dynamicload_all', 0),
  );
  $form['dynamicload_paths_type'] = array(
    '#type' => 'radios',
    '#title' => t('Path processing'),
    '#options' => array(
      t('Apply dynamic loading to all links except those to the listed pages.'),
      t('Apply dynamic loading only on links to the listed pages.'),
    ),
    '#default_value' => variable_get('dynamicload_paths_type', 0),
  );
  $form['dynamicload_paths'] = array(
    '#type' => 'textarea',
    '#title' => t('Paths'),
    '#default_value' => variable_get('dynamicload_paths', 'admin*'),
    '#description' => t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog and %admin for all admin pages. Put the wildcard at the both ends of a path fragment to match any path containing that fragment. The front page is automatically skipped.", array(
      '%blog' => 'blog',
      '%blog-wildcard' => 'blog/*',
      '%admin' => 'admin*',
    )),
  );
  include_once drupal_get_path('module', 'dynamicload') . '/paths.inc';
  $paths = module_invoke_all('dynamicload_paths');
  if (is_array($paths) && !empty($paths)) {
    $form['dynamicload_paths']['#description'] .= t(' If Path processing is set to "Apply dynamic loading to all links except those to the listed pages" above, the following paths will be used automatically and so don\'t need to be entered here: %paths.', array(
      '%paths' => implode(', ', $paths),
    ));
  }
  $form['dynamicload_regions'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Load region data'),
    '#description' => t('If you wish to refresh block regions with page loads, select the regions here. Only selected regions will be loaded.'),
    '#options' => system_region_list(variable_get('theme_default', 'garland')),
    '#default_value' => variable_get('dynamicload_regions', array()),
  );
  $form = system_settings_form($form);
  return $form;
}

/**
 * Load needed files.
 */
function dynamicload_load() {
  static $loaded = FALSE;
  if (!$loaded) {
    jstools_modules_includes('dynamicload');
    $path = drupal_get_path('module', 'dynamicload');
    jstools_add_js(array(
      $path . '/dynamicload.js',
      'misc/progress.js',
    ));
    drupal_add_js(drupal_get_path('module', 'jstools') . '/jquery.history_remote.pack.js');

    // Insert dummy content that will be used to select the region container.
    // We need to manually set the left and right
    _dynamicload_set_region_markers(array(
      'left',
      'right',
    ));
    drupal_add_css(drupal_get_path('module', 'block') . '/block.css');
    $loaded = TRUE;
  }
}

/**
 * Send settings to the browser.
 *
 * We do this in the footer because some of the settings are theme-dependent. Typically,
 * hook_footer will be called after the theme is initiated, so we have the correct theme
 * to load data for.
 */
function dynamicload_footer() {
  global $theme;
  static $loaded = FALSE;
  if (!$loaded) {

    // Insert dummy content that will be used to select the region container.
    $regions = array_keys(system_region_list($theme));

    // The left and right regions are handled separately in dynamicload_load().
    unset($regions['left'], $regions['right']);
    _dynamicload_set_region_markers($regions);
    $settings = dynamicload_block_data();
    foreach (module_invoke_all('dynamicload') as $setting) {
      $settings = array_merge($settings, $setting);
    }

    // Allow theme-based overrides.
    $theme_data = module_invoke('jstools', 'theme_data', 'dynamicload');
    if (is_array($theme_data)) {
      foreach ($theme_data as $key => $selector) {
        if ($source = array_search($key, $settings)) {
          $settings[$source] = $selector;
        }
        else {
          unset($settings[$source]);
        }
      }
    }
    $paths_type = variable_get('dynamicload_paths_type', 0);

    // Send both admin-configured and module-produced paths.
    $paths = variable_get('dynamicload_paths', 'admin*');
    if ($paths_type == 0) {
      include_once drupal_get_path('module', 'dynamicload') . '/paths.inc';
      $paths .= "\n" . implode("\n", module_invoke_all('dynamicload_paths'));
    }
    drupal_add_js(array(
      'dynamicload' => array(
        'all' => (bool) variable_get('dynamicload_all', 0),
        'paths_type' => $paths_type,
        'paths' => $paths,
        'content' => $theme_data['content'] ? $theme_data['content'] : '#main',
        'settings' => $settings,
      ),
    ), 'setting');
    $loaded = TRUE;
  }
  return '';
}

/**
 * Set a marker div in a given set of regions.
 *
 * These divs can be used from Javascript to update regions with dynamically
 * loaded content.
 */
function _dynamicload_set_region_markers($regions) {
  foreach ($regions as $region) {
    drupal_set_content($region, '<div id="dynamicload-region-' . $region . '" class="dynamicload-region"></div>');
  }
}

/**
 * Implementation of hook_form_alter().
 */
function dynamicload_form_alter($form_id, &$form) {
  if ($form_id == 'block_admin_configure') {
    $options = array(
      'content' => t('Main content area'),
    );
    $result = db_query('SELECT module, delta FROM {blocks} WHERE status = 1 AND NOT (module = "%s" AND delta = "%s")', $form['module']['#value'], $form['delta']['#value']);
    while ($block = db_fetch_object($result)) {
      $module_blocks = module_invoke($block->module, 'block', 'list');
      $options['#block-' . $block->module . '-' . $block->delta] = t('Block: !info', array(
        '!info' => $module_blocks[$block->delta]['info'],
      ));
    }
    $regions = system_region_list(variable_get('theme_default', 'garland'));

    // Highlight regions on page to provide visual reference.
    foreach ($regions as $key => $value) {
      drupal_set_content($key, '<div class="block-region">' . $value . '</div>');
      $options['region-' . $key] = t('Region: !name', array(
        '!name' => $value,
      ));
    }
    $settings = dynamicload_block_data($form['module']['#value'], $form['delta']['#value']);
    $form['#submit']['dynamicload_submit'] = array();
    $form['dynamicload'] = array(
      '#type' => 'fieldset',
      '#title' => t('Dynamic loading'),
      '#collapsible' => true,
      '#weight' => -5,
    );
    $form['dynamicload']['dynamicload_apply'] = array(
      '#type' => 'checkbox',
      '#title' => t('Apply dynamic loading'),
      '#description' => t('Check if you want links on this block to be loaded dynamically rather than through a regular page refresh. If you enable this functionality for a block, make sure that you thoroughly test the result, as not all pages will perform as expected when loaded dynamically.'),
      '#default_value' => $settings['apply'] ? 1 : 0,
    );
    $form['dynamicload']['dynamicload_target'] = array(
      '#type' => 'select',
      '#title' => t('Target area'),
      '#default_value' => $settings['target'] ? $settings['target'] : '#main',
      '#options' => $options,
      '#description' => t('Select the page area for this block\'s links to load into. Only enabled blocks are available.'),
    );
    $options = array(
      DYNAMICLOAD_HTML => t('replace existing content'),
      DYNAMICLOAD_APPEND => t('append: add at end of existing content'),
      DYNAMICLOAD_PREPEND => t('prepend: add at beginning of existing content'),
      DYNAMICLOAD_BEFORE => t('before: add before target element'),
      DYNAMICLOAD_AFTER => t('after: add after target element'),
    );
    $form['dynamicload']['dynamicload_method'] = array(
      '#type' => 'select',
      '#title' => t('Appending method'),
      '#default_value' => $settings['method'] ? $settings['method'] : 1,
      '#options' => $options,
      '#description' => t('Select the way you want newly loaded content to be written to the target area.'),
    );
    $form['dynamicload']['dynamicload_refresh'] = array(
      '#type' => 'checkbox',
      '#title' => t('Automatically refresh this block'),
      '#default_value' => $settings['refresh'] ? 1 : 0,
    );
    $form['dynamicload']['dynamicload_refresh_interval'] = array(
      '#type' => 'select',
      '#title' => t('Block refresh interval'),
      '#description' => t('Select the interval at which this block should refresh.'),
      '#default_value' => $settings['refresh_interval'] ? $settings['refresh_interval'] : 30000,
      '#options' => array(
        5000 => t('five seconds'),
        10000 => t('ten seconds'),
        30000 => t('thirty seconds'),
        60000 => t('one minute'),
        120000 => t('two minutes'),
        180000 => t('three minutes'),
        300000 => t('five minutes'),
        600000 => t('ten minutes'),
      ),
    );
    $form['dynamicload']['dynamicload_scroll'] = array(
      '#type' => 'checkbox',
      '#title' => t('Scroll in new items'),
      '#description' => t('Check if you want new items for this block to scroll in ticker-style as they arrive (with the oldest item scrolling out). If this option is not checked, the whole block will refresh. Note: only works for items that come in as a list (using an HTML ul element).)'),
      '#default_value' => $settings['scroll'] ? 1 : 0,
    );
  }
}

/**
 * Register settings.
 */
function dynamicload_submit($form_id, $form_values) {
  if (db_num_rows(db_query("SELECT * FROM {dynamicload_blocks} WHERE module = '%s' AND delta = '%s'", $form_values['module'], $form_values['delta']))) {
    db_query("UPDATE {dynamicload_blocks} SET refresh = %d, refresh_interval = %d, scroll = %d, apply = %d, target = '%s', method = %d WHERE module = '%s' AND delta = '%s'", $form_values['dynamicload_refresh'], $form_values['dynamicload_refresh_interval'], $form_values['dynamicload_scroll'], $form_values['dynamicload_apply'], $form_values['dynamicload_target'], $form_values['dynamicload_method'], $form_values['module'], $form_values['delta']);
    drupal_set_message(t('Dynamic loading block data updated.'));
  }
  else {
    db_query("INSERT INTO {dynamicload_blocks} (module, delta, refresh, refresh_interval, scroll, apply, target, method) VALUES ('%s', '%s', %d, %d, %d, %d, '%s', %d)", $form_values['module'], $form_values['delta'], $form_values['dynamicload_refresh'], $form_values['dynamicload_refresh_interval'], $form_values['dynamicload_scroll'], $form_values['dynamicload_apply'], $form_values['dynamicload_target'], $form_values['dynamicload_method']);
    drupal_set_message(t('Dynamic loading block data saved.'));
  }
}

/**
 * Load dynamicload settings for a block or all blocks.
 */
function dynamicload_block_data($module = NULL, $delta = NULL) {
  static $blocks = NULL;
  if ($blocks == NULL) {
    $blocks = array();
    $result = db_query("SELECT * FROM {dynamicload_blocks}");
    while ($block = db_fetch_array($result)) {
      foreach (array(
        'refresh',
        'apply',
      ) as $key) {
        $block[$key] = (bool) $block[$key];
      }
      $block['type'] = 'block';
      $block['selector'] = '#block-' . $block['module'] . '-' . $block['delta'];
      $blocks[$block['module'] . '_' . $block['delta']] = $block;
    }
  }
  if ($module !== NULL && $delta !== NULL) {
    return $blocks[$module . '_' . $delta] ? $blocks[$module . '_' . $delta] : array();
  }
  else {
    return $blocks;
  }
}

/**
 * List blocks available to the current user on the current page.
 */
function dynamicload_block_list() {
  global $theme_key;
  $blocks = array();
  if ($theme_key) {
    $regions = system_region_list($theme_key);
    foreach (array_keys($regions) as $region) {
      $block_list = block_list($region);
      $blocks = array_merge($blocks, $block_list);
    }
  }
  return $blocks;
}

/**
 * Menu callback to return a rendered block in JSON format.
 */
function dynamicload_fetch_block($module, $delta) {
  global $theme_key;
  if (!isset($theme_key)) {
    init_theme();
  }
  $region = db_result(db_query("SELECT region FROM {blocks} WHERE module = '%s' AND delta = %d AND theme = '%s'", $module, $delta, $theme_key));
  $blocks = block_list($region);
  if (isset($blocks[$module . '_' . $delta])) {
    print drupal_to_js(array(
      'result' => TRUE,
      'content' => theme('block', $blocks[$module . '_' . $delta]),
    ));
  }
  else {
    print drupal_to_js(array(
      'result' => FALSE,
      'content' => NULL,
    ));
  }
  exit;
}

/**
 * Menu callback to return a rendered page in JSON format.
 */
function dynamicload_js() {

  // The path requested is the current q arg minus the 'dynamicload/js/'.
  if ($path = substr($_GET['q'], 15)) {
    $return = module_invoke('pagearray', 'page', $path);
    if ($return['status'] === FALSE) {
      switch ($return['value']) {
        case MENU_NOT_FOUND:
        case NULL:
          $result = array(
            'result' => FALSE,
            'message' => t('Item was not found.'),
          );
          break;
        case MENU_ACCESS_DENIED:
          $result = array(
            'result' => FALSE,
            'message' => t('Access denied.'),
          );
          break;
        case MENU_SITE_OFFLINE:
          $result = array(
            'result' => FALSE,
            'message' => t('Site offline.'),
          );
          break;
      }
    }
    else {

      // Generate 'extra' for outputting in main content area.
      $extra = '<h1>' . $return['page']['title'] . '</h1>';
      $extra = $return['page']['breadcrumb'] . $extra;
      if ($tabs = theme('menu_local_tasks')) {
        $extra .= $tabs;
      }
      $extra .= $return['page']['help'];
      $extra .= theme('status_messages');

      // Load scripts
      $result = array(
        'result' => TRUE,
        'extra' => $extra,
        'content' => $return['page']['content'],
        'title' => $return['page']['head_title'],
        'scripts' => $return['page']['raw_scripts'],
        'css' => $return['page']['css'],
      );
      $regions = array_filter(variable_get('dynamicload_regions', array()));
      if (!empty($regions)) {
        $result['regions'] = array();
        foreach ($regions as $region) {
          if (isset($return['page']['regions'][$region])) {
            $result['regions'][$region] = $return['page']['regions'][$region];
          }
        }
      }
    }
  }
  else {
    $result = array(
      'result' => FALSE,
      'message' => t('No request found.'),
    );
  }
  print drupal_to_js($result);
  exit;
}

Functions

Namesort descending Description
dynamicload_admin_settings Admin settings.
dynamicload_block_data Load dynamicload settings for a block or all blocks.
dynamicload_block_list List blocks available to the current user on the current page.
dynamicload_fetch_block Menu callback to return a rendered block in JSON format.
dynamicload_footer Send settings to the browser.
dynamicload_form_alter Implementation of hook_form_alter().
dynamicload_help Implementation of hook_help().
dynamicload_js Menu callback to return a rendered page in JSON format.
dynamicload_load Load needed files.
dynamicload_menu Implementation of hook_menu().
dynamicload_submit Register settings.
_dynamicload_set_region_markers Set a marker div in a given set of regions.

Constants

Namesort descending Description
DYNAMICLOAD_AFTER
DYNAMICLOAD_APPEND
DYNAMICLOAD_BEFORE
DYNAMICLOAD_HTML @file Enable AJAX-based loading of selected page elements.
DYNAMICLOAD_PREPEND