context_reaction_block.inc

Go to the documentation of this file.
00001 <?php
00002 
00003 /**
00004  * Expose blocks as context reactions.
00005  */
00006 class context_reaction_block extends context_reaction {
00007   /**
00008    * Options form.
00009    */
00010   function options_form($context) {
00011     // Rebuild the block info cache if necessary.
00012     $this->get_blocks(NULL, NULL, $this->rebuild_needed());
00013     $this->rebuild_needed(FALSE);
00014 
00015     $theme_key = variable_get('theme_default', 'garland');
00016     $weight_delta = $this->max_block_weight();
00017 
00018     $form = array(
00019       '#tree' => TRUE,
00020       '#theme' => 'context_block_form',
00021       'max_block_weight' => array(
00022         '#value' => $weight_delta,
00023         '#type' => 'value',
00024       ),
00025       'state' => array(
00026         '#type' => 'hidden',
00027         '#attributes' => array('class' => 'context-blockform-state'),
00028       ),
00029     );
00030 
00031     /**
00032      * Selector.
00033      */
00034     $modules = module_list();
00035     $form['selector'] = array(
00036       '#type' => 'item',
00037       '#tree' => TRUE,
00038       '#prefix' => '<div class="context-blockform-selector">',
00039       '#suffix' => '</div>',
00040     );
00041     foreach ($this->get_blocks() as $block) {
00042       $group = isset($block->context_group) ? $block->context_group : $block->module;
00043       if (!isset($form['selector'][$group])) {
00044         $form['selector'][$group] = array(
00045           '#type' => 'checkboxes',
00046           '#title' => isset($block->context_group) ? $block->context_group : $modules[$block->module],
00047           '#options' => array(),
00048         );
00049       }
00050       $form['selector'][$group]['#options'][$block->bid] = check_plain($block->info);
00051     }
00052     ksort($form['selector']);
00053 
00054     /**
00055      * Regions.
00056      */
00057     $form['blocks'] = array(
00058       '#tree' => TRUE,
00059       '#theme' => 'context_block_regions_form',
00060     );
00061     foreach (system_region_list($theme_key, REGIONS_VISIBLE) as $region => $label) {
00062       $form['blocks'][$region] = array(
00063         '#type' => 'item',
00064         '#title' => $label,
00065         '#tree' => TRUE,
00066       );
00067       foreach ($this->get_blocks($region, $context) as $block) {
00068         if (!empty($block->context)) {
00069           $form['blocks'][$region][$block->bid] = array(
00070             '#value' => check_plain($block->info),
00071             '#weight' => $block->weight,
00072             '#type' => 'markup',
00073             '#tree' => TRUE,
00074             'weight' => array('#type' => 'weight', '#delta' => $weight_delta, '#default_value' => $block->weight),
00075           );
00076         }
00077       }
00078     }
00079     return $form;
00080   }
00081 
00082   /**
00083    * Options form submit handler.
00084    */
00085   function options_form_submit($values) {
00086     $blocks = array();
00087     $block_info = $this->get_blocks();
00088 
00089     // Retrieve blocks from submitted JSON string.
00090     if (!empty($values['state'])) {
00091       $edited = $this->json_decode($values['state']);
00092     }
00093     else {
00094       $edited = array();
00095     }
00096 
00097     foreach ($edited as $region => $block_data) {
00098       foreach ($block_data as $position => $data) {
00099         if (isset($block_info[$data->bid])) {
00100           $blocks[$data->bid] = array(
00101             'module' => $block_info[$data->bid]->module,
00102             'delta' => $block_info[$data->bid]->delta,
00103             'region' => $region,
00104             'weight' => $data->weight,
00105           );
00106         }
00107       }
00108     }
00109     return array('blocks' => $blocks);
00110   }
00111 
00112   /**
00113    * Context editor form for blocks.
00114    */
00115   function editor_form($context) {
00116     $form = array();
00117     drupal_add_library('system', 'ui.droppable');
00118     drupal_add_library('system', 'ui.sortable');
00119     drupal_add_js(drupal_get_path('module', 'context_ui') . '/json2.js');
00120     drupal_add_js(drupal_get_path('module', 'context_ui') . '/theme/filter.js');
00121     drupal_add_js(drupal_get_path('module', 'context') . '/plugins/context_reaction_block.js');
00122     drupal_add_css(drupal_get_path('module', 'context') . '/plugins/context_reaction_block.css');
00123 
00124     // We might be called multiple times so use a static to ensure this is set just once.
00125     static $once;
00126     if (!isset($once)) {
00127       $settings = array(
00128         'path' => drupal_is_front_page() ? base_path() : url($_GET['q']),
00129         'params' => (object) array_diff_key($_GET, array('q' => '')),
00130         'scriptPlaceholder' => theme('context_block_script_placeholder', array('text' => '')),
00131       );
00132       drupal_add_js(array('contextBlockEditor' => $settings), 'setting');
00133       $once = TRUE;
00134     }
00135 
00136     $form['state'] = array(
00137       '#type' => 'hidden',
00138       '#attributes' => array('class' => array('context-block-editor-state')),
00139     );
00140     $form['browser'] = array(
00141       '#markup' => theme('context_block_browser', array(
00142         'blocks' => $this->get_blocks(NULL, NULL, $this->rebuild_needed()),
00143         'context' => $context
00144       )),
00145     );
00146     $this->rebuild_needed(FALSE);
00147     return $form;
00148   }
00149 
00150   /**
00151    * Submit handler context editor form.
00152    */
00153   function editor_form_submit(&$context, $values) {
00154     $edited = !empty($values['state']) ? (array) $this->json_decode($values['state']) : array();
00155 
00156     $options = array();
00157 
00158     // Take the existing context values and remove blocks that belong affected regions.
00159     $affected_regions = array_keys($edited);
00160     if (!empty($context->reactions['block']['blocks'])) {
00161       $options = $context->reactions['block'];
00162       foreach ($options['blocks'] as $key => $block) {
00163         if (in_array($block['region'], $affected_regions)) {
00164           unset($options['blocks'][$key]);
00165         }
00166       }
00167     }
00168 
00169     // Iterate through blocks and add in the ones that belong to the context.
00170     foreach ($edited as $region => $blocks) {
00171       foreach ($blocks as $weight => $block) {
00172         if ($block->context === $context->name) {
00173           $split = explode('-', $block->bid);
00174           $options['blocks'][$block->bid] = array(
00175             'module' => array_shift($split),
00176             'delta' => implode('-', $split),
00177             'region' => $region,
00178             'weight' => $weight,
00179           );
00180         }
00181       }
00182     }
00183 
00184     return $options;
00185   }
00186 
00187   /**
00188    * Settings form for variables.
00189    */
00190   function settings_form() {
00191     $form = array();
00192     $form['context_reaction_block_all_regions'] = array(
00193       '#title' => t('Show all regions'),
00194       '#type' => 'checkbox',
00195       '#default_value' => variable_get('context_reaction_block_all_regions', FALSE),
00196       '#description' => t('Show all regions including those that are empty. Enable if you are administering your site using the inline editor.')
00197     );
00198     return $form;
00199   }
00200 
00201   /**
00202    * Execute.
00203    */
00204   function execute(&$page) {
00205     global $theme;
00206 
00207     // The theme system might not yet be initialized. We need $theme.
00208     drupal_theme_initialize();
00209 
00210     // If the context_block querystring param is set, switch to AJAX rendering.
00211     // Note that we check the output buffer for any content to ensure that we
00212     // are not in the middle of a PHP template render.
00213     if (isset($_GET['context_block']) && !ob_get_contents()) {
00214       return $this->render_ajax($_GET['context_block']);
00215     }
00216 
00217     // Populate all block regions
00218     $all_regions = system_region_list($theme);
00219 
00220     // Load all region content assigned via blocks.
00221     foreach (array_keys($all_regions) as $region) {
00222       if ($this->is_enabled_region($region)) {
00223         if ($blocks = $this->block_get_blocks_by_region($region)) {
00224 
00225           // Are the blocks already sorted.
00226           $blocks_sorted = TRUE;
00227 
00228           // If blocks have already been placed in this region (most likely by
00229           // Block module), then merge in blocks from Context.
00230           if (isset($page[$region])) {
00231             $page[$region] = array_merge($page[$region], $blocks);
00232 
00233             // Restore the weights that Block module manufactured
00234             // @see _block_get_renderable_array()
00235             foreach ($page[$region] as &$block) {
00236               if (isset($block['#block']->weight)) {
00237                 $block['#weight'] = $block['#block']->weight;
00238                 $blocks_sorted = FALSE;
00239               }
00240             }
00241           }
00242           else {
00243             $page[$region] = $blocks;
00244           }
00245 
00246           $page[$region]['#sorted'] = $blocks_sorted;
00247         }
00248       }
00249     }
00250   }
00251 
00252   /**
00253    * Return a list of enabled regions for which blocks should be built.
00254    * Split out into a separate method for easy overrides in extending classes.
00255    */
00256   protected function is_enabled_region($region) {
00257     global $theme;
00258     $regions = array_keys(system_region_list($theme));
00259     return in_array($region, $regions, TRUE);
00260   }
00261 
00262   /**
00263    * Determine whether inline editing requirements are met and that the current
00264    * user may edit.
00265    */
00266   protected function is_editable_region($region, $reset = FALSE) {
00267     // Check requirements.
00268     // Generally speaking, it does not make sense to allow anonymous users to
00269     // edit a context inline. Though it may be possible to save (and restore)
00270     // an edited context to an anonymous user's cookie or session, it's an
00271     // edge case and probably not something we want to encourage anyway.
00272     static $requirements;
00273     if (!isset($requirements) || $reset) {
00274       global $user;
00275       if ($user->uid && variable_get('context_ui_dialog_enabled', FALSE)) {
00276         $requirements = TRUE;
00277         drupal_add_library('system', 'ui.droppable');
00278         drupal_add_library('system', 'ui.sortable');
00279         drupal_add_js(drupal_get_path('module', 'context') . '/plugins/context_reaction_block.js');
00280         drupal_add_css(drupal_get_path('module', 'context') . '/plugins/context_reaction_block.css');
00281       }
00282       else {
00283         $requirements = FALSE;
00284       }
00285     }
00286     // Check that this region is not locked by the theme.
00287     global $theme;
00288     $info = system_get_info('theme', $theme);
00289     if ($info && isset($info['regions_locked']) && in_array($region, $info['regions_locked'])) {
00290       return FALSE;
00291     }
00292     // Check that this region is not hidden
00293     $visible = system_region_list($theme, REGIONS_VISIBLE);
00294     return $requirements && $this->is_enabled_region($region) && isset($visible[$region]);
00295   }
00296 
00297   /**
00298    * Add markup for making a block editable.
00299    */
00300   protected function editable_block($block) {
00301     if (!empty($block->content)) {
00302       $block->content = array(
00303         'content' => $block->content,
00304         'context' => array('#markup' => "<a id='context-block-{$block->module}-{$block->delta}' class='context-block editable edit-{$block->context}'></a>"),
00305       );
00306       //Contextual links are in the wrong spot in the render array once we've nested them
00307       if (isset($block->content['content']['#contextual_links'])) {
00308         $block->content['#contextual_links'] = $block->content['content']['#contextual_links'];
00309         unset($block->content['content']['#contextual_links']);
00310       }
00311     }
00312     else {
00313       // the block alter in context.module should ensure that blocks are never
00314       // empty if the inline editor is present but in the case that they are,
00315       // warn that editing the context is likely to cause this block to be dropped
00316       drupal_set_message(t('The block with delta @delta from module @module is not compatible with the inline editor and will be dropped from the context containing it if you edit contexts here', array('@delta' => $block->delta, '@module' => $block->module)), 'warning');
00317     }
00318     return $block;
00319   }
00320 
00321   /**
00322    * Add markup for making a region editable.
00323    */
00324   protected function editable_region($region, $build) {
00325     if ($this->is_editable_region($region) &&
00326         (!empty($build) ||
00327          variable_get('context_reaction_block_all_regions', FALSE) ||
00328          context_isset('context_ui', 'context_ui_editor_present'))
00329     ) {
00330       global $theme;
00331       $regions = system_region_list($theme);
00332       $name = isset($regions[$region]) ? $regions[$region] : $region;
00333       // The negative weight + sorted will push our region marker to the top of the region
00334       $build['context'] = array(
00335         '#prefix' => "<div class='context-block-region' id='context-block-region-{$region}'>",
00336         '#markup' => "<span class='region-name'>{$name}</span>" .
00337                      "<a class='context-ui-add-link'>" . t('Add a block here.') . '</a>',
00338         '#suffix' => '</div>',
00339         '#weight' => -100,
00340       );
00341       $build['#sorted'] = FALSE;
00342     }
00343 
00344     return $build;
00345   }
00346 
00347   /**
00348   * Get a renderable array of a region containing all enabled blocks.
00349   */
00350   function block_get_blocks_by_region($region) {
00351     module_load_include('module', 'block', 'block');
00352 
00353     $build = array();
00354     if ($list = $this->block_list($region)) {
00355       $build = _block_get_renderable_array($list);
00356     }
00357     if ($this->is_editable_region($region)) {
00358       $build = $this->editable_region($region, $build);
00359     }
00360     return $build;
00361   }
00362 
00363   /**
00364    * An alternative version of block_list() that provides any context enabled blocks.
00365    */
00366   function block_list($region) {
00367     module_load_include('module', 'block', 'block');
00368 
00369     $context_blocks = &drupal_static('context_reaction_block_list');;
00370     $contexts = context_active_contexts();
00371     if (!isset($context_blocks)) {
00372       $info = $this->get_blocks();
00373       $context_blocks = array();
00374       foreach ($contexts as $context) {
00375         $options = $this->fetch_from_context($context);
00376         if (!empty($options['blocks'])) {
00377           foreach ($options['blocks'] as $context_block) {
00378             $bid = "{$context_block['module']}-{$context_block['delta']}";
00379             if (isset($info[$bid])) {
00380               $block = (object) array_merge((array) $info[$bid], $context_block);
00381               $block->context = $context->name;
00382               $block->title = isset($info[$block->bid]->title) ? $info[$block->bid]->title : NULL;
00383               $block->cache = isset($info[$block->bid]->cache) ? $info[$block->bid]->cache : DRUPAL_NO_CACHE;
00384               $context_blocks[$block->region][$block->bid] = $block;
00385             }
00386           }
00387         }
00388       }
00389       
00390       $this->is_editable_check($context_blocks);
00391       foreach ($context_blocks as $r => $blocks) {
00392         $context_blocks[$r] = _block_render_blocks($blocks);
00393 
00394         // Make blocks editable if allowed.
00395         if ($this->is_editable_region($r)) {
00396           foreach ($context_blocks[$r] as $key => $block) {
00397             $context_blocks[$r][$key] = $this->editable_block($block);
00398           }
00399         }
00400 
00401         // Sort blocks.
00402         uasort($context_blocks[$r], array('context_reaction_block', 'block_sort'));
00403       }
00404     }
00405     return isset($context_blocks[$region]) ? $context_blocks[$region] : array();
00406   }
00407 
00408   /**
00409    * Determine if there is an active context editor block, and set a flag.  We will set a flag so 
00410    * that we can make sure that blocks with empty content have some default content.  This is
00411    * needed so the save of the context inline editor does not remove the blocks with no content.
00412    */
00413   function is_editable_check($context_blocks) {
00414     foreach ($context_blocks as $r => $blocks) {
00415       if (isset($blocks['context_ui-editor'])) {
00416         $block = $blocks['context_ui-editor'];
00417         // see if the editor is actually enabled, lifted from _block_render_blocks
00418         if (!count(module_implements('node_grants')) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && ($cid = _block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) {
00419           $array = $cache->data;
00420         }
00421         else {
00422           $array = module_invoke($block->module, 'block_view', $block->delta);
00423           drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block);
00424         }
00425         if(!empty($array['content'])) {
00426           context_set('context_ui', 'context_ui_editor_present', TRUE);
00427         }
00428         break;
00429       }
00430     }
00431   }
00432   
00433   /**
00434    * Generate the safe weight range for a block being added to a region such that
00435    * there are enough potential unique weights to support all blocks.
00436    */
00437   protected function max_block_weight() {
00438     $blocks = $this->get_blocks();
00439     $block_count = 0;
00440     foreach ($blocks as $region => $block_list) {
00441       $block_count += count($block_list);
00442     }
00443     // Add 2 to make sure there's space at either end of the block list
00444     return round(($block_count + 2) / 2);
00445   }
00446 
00447   /**
00448    * Check or set whether a rebuild of the block info cache is needed.
00449    */
00450   function rebuild_needed($set = NULL) {
00451     if (isset($set) && $set != variable_get('context_block_rebuild_needed', FALSE)) {
00452       variable_set('context_block_rebuild_needed', $set);
00453     }
00454     return (bool) variable_get('context_block_rebuild_needed', FALSE);
00455   }
00456 
00457   /**
00458    * Helper function to generate a list of blocks from a specified region. If provided a context object,
00459    * will generate a full list of blocks for that region distinguishing between system blocks and
00460    * context-provided blocks.
00461    *
00462    * @param $region
00463    *   The string identifier for a theme region. e.g. "left"
00464    * @param $context
00465    *   A context object.
00466    *
00467    * @return
00468    *   A keyed (by "module_delta" convention) array of blocks.
00469    */
00470   function get_blocks($region = NULL, $context = NULL, $reset = FALSE) {
00471     static $block_info;
00472     $theme_key = variable_get('theme_default', 'garland');
00473 
00474     if (!isset($block_info) || $reset) {
00475       $block_info = array();
00476       if (!$reset) {
00477         $block_info = context_cache_get('block_info');
00478       }
00479       if (empty($block_info)) {
00480         if (module_exists('block')) {
00481           $block_blocks = _block_rehash($theme_key);
00482           $block_info = array();
00483           // Change from numeric keys to module-delta.
00484           foreach ($block_blocks as $block) {
00485             $block = (object) $block;
00486             unset($block->theme, $block->status, $block->weight, $block->region, $block->custom, $block->visibility, $block->pages);
00487             $block->bid = "{$block->module}-{$block->delta}";
00488             $block_info[$block->bid] = $block;
00489           }
00490         }
00491         else {
00492           $block_info = array();
00493           foreach (module_implements('block_info') as $module) {
00494             $module_blocks = module_invoke($module, 'block_info');
00495             if (!empty($module_blocks)) {
00496               foreach ($module_blocks as $delta => $block) {
00497                 $block = (object) $block;
00498                 $block->module = $module;
00499                 $block->delta = $delta;
00500                 $block->bid = "{$block->module}-{$block->delta}";
00501                 $block_info[$block->bid] = $block;
00502               }
00503             }
00504           }
00505         }
00506         context_cache_set('block_info', $block_info);
00507       }
00508       // Allow other modules that may declare blocks dynamically to alter
00509       // this list.
00510       drupal_alter('context_block_info', $block_info);
00511 
00512       // Gather only region info from the database.
00513       if (module_exists('block')) {
00514         $result = db_select('block')
00515           ->fields('block')
00516           ->condition('theme', $theme_key)
00517           ->execute();
00518         foreach ($result as $row) {
00519           if (isset($block_info["{$row->module}-{$row->delta}"])) {
00520             $block_info["{$row->module}-{$row->delta}"] = (object) array_merge((array) $row, (array) $block_info["{$row->module}-{$row->delta}"]);
00521             unset($block_info["{$row->module}-{$row->delta}"]->status);
00522             unset($block_info["{$row->module}-{$row->delta}"]->visibility);
00523             unset($block_info["{$row->module}-{$row->delta}"]->cache);
00524           }
00525         }
00526       }
00527     }
00528 
00529     $blocks = array();
00530 
00531     // No region specified, provide all blocks.
00532     if (!isset($region)) {
00533       $blocks = $block_info;
00534     }
00535     // Region specified.
00536     else {
00537       foreach ($block_info as $bid => $block) {
00538         if (isset($block->region) && $block->region == $region) {
00539           $blocks[$bid] = $block;
00540         }
00541       }
00542     }
00543 
00544     // Add context blocks if provided.
00545     if (is_object($context) && $options = $this->fetch_from_context($context)) {
00546       if (!empty($options['blocks'])) {
00547         foreach ($options['blocks'] as $block) {
00548           if (
00549             isset($block_info["{$block['module']}-{$block['delta']}"]) && // Block is valid.
00550             (!isset($region) || (!empty($region) && $block['region'] == $region)) // No region specified or regions match.
00551           ) {
00552             $context_block = $block_info["{$block['module']}-{$block['delta']}"];
00553             $context_block->weight = $block['weight'];
00554             $context_block->region = $block['region'];
00555             $context_block->context = !empty($context->name) ? $context->name : 'tempname';
00556             $blocks[$context_block->bid] = $context_block;
00557           }
00558         }
00559       }
00560       uasort($blocks, array('context_reaction_block', 'block_sort'));
00561     }
00562     return $blocks;
00563   }
00564 
00565   /**
00566    * Sort callback.
00567    */
00568   static function block_sort($a, $b) {
00569     return ($a->weight - $b->weight);
00570   }
00571 
00572   /**
00573    * Compatibility wrapper around json_decode().
00574    */
00575   protected function json_decode($json, $assoc = FALSE) {
00576     // Requires PHP 5.2.
00577     if (function_exists('json_decode')) {
00578       return json_decode($json, $assoc);
00579     }
00580     return context_reaction_block::_json_decode($json);
00581   }
00582 
00583   /**
00584    * From http://www.php.net/manual/en/function.json-decode.php#91216
00585    * with modifications for consistency with output of json_decode().
00586    *
00587    * Original author: walidator.info 2009.
00588    */
00589   static function _json_decode($json) {
00590     $comment = FALSE;
00591     $out = '$x = ';
00592     for ($i=0; $i < strlen($json); $i++) {
00593       if (!$comment) {
00594         switch ($json[$i]) {
00595           case '{':
00596             $out .= ' (object) array(';
00597             break;
00598           case '}':
00599             $out .= ')';
00600             break;
00601           case '[':
00602             $out .= ' array(';
00603             break;
00604           case ']':
00605             $out .= ')';
00606             break;
00607           case ':';
00608             $out .= '=>';
00609             break;
00610           default:
00611             $out .= $json[$i];
00612             break;
00613         }
00614       }
00615       else {
00616         $out .= $json[$i];
00617       }
00618       if ($json[$i] == '"') {
00619         $comment = !$comment;
00620       }
00621     }
00622     eval($out . ';');
00623     return $x;
00624   }
00625 
00626   /**
00627    * Block renderer for AJAX requests. Triggered when $_GET['context_block']
00628    * is set. See ->execute() for how this is called.
00629    */
00630   function render_ajax($param) {
00631     // Besure the page isn't a 404 or 403.
00632     $headers = drupal_get_http_header();
00633     if (array_key_exists('status', $headers) && ($headers['status'] == "404 Not Found" || $headers['status'] == "403 Forbidden")) {
00634       return;
00635     }
00636     // Set the header right away. This will inform any players in the stack
00637     // that we are in the middle of responding to an AJAX request.
00638     drupal_add_http_header('Content-Type', 'text/javascript; charset=utf-8');
00639 
00640     if (strpos($param, ',') !== FALSE) {
00641       list($bid, $context) = explode(',', $param);
00642       list($module, $delta) = explode('-', $bid, 2);
00643       // Check token to make sure user has access to block.
00644       if (empty($_GET['context_token']) || $_GET['context_token'] != drupal_get_token($bid)) {
00645         echo drupal_json_encode(array('status' => 0));
00646         exit;
00647       }
00648 
00649       // Ensure $bid is valid.
00650       $info = $this->get_blocks();
00651       if (isset($info[$bid])) {
00652         module_load_include('module', 'block', 'block');
00653         $block = $info[$bid];
00654         $block->title = isset($block->title) ? $block->title : '';
00655         $block->context = $context;        
00656         $block->region = '';
00657         $rendered_blocks = _block_render_blocks(array($block)); // For E_STRICT warning
00658         $block = array_shift($rendered_blocks);
00659         if (empty($block->content['#markup'])) {
00660           $block->content['#markup'] = "<div class='context-block-empty'>" . t('This block appears empty when displayed on this page.') . "</div>";
00661         }
00662         $block = $this->editable_block($block);
00663         $renderable_block = _block_get_renderable_array(array($block)); // For E_STRICT warning
00664         echo drupal_json_encode(array(
00665           'status' => 1,
00666           'block' => drupal_render($renderable_block),
00667         ));
00668         drupal_exit();
00669       }
00670     }
00671     echo drupal_json_encode(array('status' => 0));
00672     drupal_exit();
00673   }
00674 }

Generated on Wed May 22 02:23:48 2013 for Context by  doxygen 1.4.7