[ Index ]

PHP Cross Reference of Akelos Framework

title

Body

[close]

/ -> AkActionView.php (source)

   1  <?php
   2  /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
   3  
   4  // +----------------------------------------------------------------------+
   5  // | Akelos Framework - http://www.akelos.org                             |
   6  // +----------------------------------------------------------------------+
   7  // | Copyright (c) 2002-2006, Akelos Media, S.L.  & Bermi Ferrer Martinez |
   8  // | Released under the GNU Lesser General Public License, see LICENSE.txt|
   9  // +----------------------------------------------------------------------+
  10  
  11  /**
  12   * @package ActionView
  13   * @subpackage Base
  14   * @author Bermi Ferrer <bermi a.t akelos c.om>
  15   * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
  16   * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
  17   */
  18  
  19  /**
  20  * Action View templates can be written in two ways. If the template file has a +.tpl+ extension then it uses PHP.
  21  * 
  22  * = PHP
  23  * 
  24  * You trigger PHP by using embeddings such as <? ?>, <?php ?> and <?= ?>. The difference is whether you want output or not. Consider the 
  25  * following loop for names:
  26  *
  27  *   <b>Names of all the people</b>
  28  *   <?php foreach($people as $person) : ?>
  29  *   Name: <?=$person->name ?><br/>
  30  *   <?php endforeach ?>
  31  *
  32  * == Using sub templates
  33  *
  34  * Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
  35  * classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
  36  *
  37  *   <?= $controller->render("shared/header") ?>
  38  *   Something really specific and terrific
  39  *   <?= $controller->render("shared/footer") ?>
  40  *
  41  * As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
  42  * result of the rendering. The output embedding writes it to the current template.
  43  *
  44  * But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
  45  * variables defined using the regular embedding tags. Like this:
  46  *
  47  *   <?php $shared->page_title = "A Wonderful Hello" ?>
  48  *   <?= $controller->render("shared/header") ?>
  49  *
  50  * Now the header can pick up on the $page_title variable and use it for outputting a title tag:
  51  *
  52  *   <title><?= $page_title ?></title>
  53  *
  54  * == Passing local variables to sub templates
  55  * 
  56  * You can pass local variables to sub templates by using an array with the variable names as keys and the objects as values:
  57  *
  58  *   <?= $controller->render("shared/header", array('headline'=>'Welcome','person'=> $person )) ?>
  59  *
  60  * These can now be accessed in shared/header with:
  61  *
  62  *   Headline: <?= $headline ?>
  63  *   First name: <?= $person->first_name ?>
  64  *
  65  * 
  66  * == JavaScriptGenerator ==
  67  *
  68  * @todo Fully implement Javascript Generators
  69  *
  70  * JavaScriptGenerator templates end in +.js.tpl+. Unlike conventional templates which are used to 
  71  * render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to 
  72  * modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax 
  73  * and make updates to the page where the request originated from.
  74  * 
  75  * An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, 
  76  * which is implicitly wrapped in an AkActionView/Helpers/PrototypeHelper::update_page method. 
  77  *
  78  * When an .js.tpl action is called with +linkToRemote+, the generated JavaScript is automatically evaluated.  Example:
  79  *
  80  *   linkToRemote(array('url' => array('action' => 'delete')));
  81  *
  82  * The subsequently rendered +delete.js.tpl+ might look like:
  83  *
  84  *   <% replace_html  'sidebar', :partial => 'sidebar' %>
  85  *   <% remove "person-#{person.id}" %>
  86  *   <% visual_effect :highlight, 'user-list' %>
  87  *
  88  * This refreshes the sidebar, removes a person element and highlights the user list.
  89  * 
  90  * See the AkActionView/Helpers/PrototypeHelper/JavaScriptGenerator documentation for more details.
  91  */
  92  class AkActionView extends AkObject
  93  {
  94      var $first_render, $base_path, $assigns, $template_extension, $controller,
  95      $logger, $params, $request, $response, $session, $headers, $flash;
  96      var $_template_handlers = array();
  97      var $template_args = array();
  98  
  99  
 100  
 101      function _loadHelpers($helper_dir = null)
 102      {
 103          $helper_dir = empty($helper_dir) ? AK_LIB_DIR.DS.'Helpers' : $helper_dir;
 104          if (!empty($this->helpers)){
 105              $this->helpers = is_array($this->helpers) ? $this->helpers : array($this->helpers);
 106              foreach ($this->helpers as $helper_name){
 107                  if(empty($this->_helperInstances[$helper_name])){
 108                      $helper_file_name = $helper_dir.DS.AkInflector::underscore($helper_name).'.php';
 109                      $helper_class_name = ucfirst($helper_name).'Helper';
 110                      if (file_exists($helper_file_name)){
 111                          require_once($helper_file_name);
 112                          if(class_exists($helper_class_name) === true){
 113                              $this->_helperInstances[$helper_name] =& new $helper_class_name(&$this);
 114                          }
 115                      }
 116                  }
 117              }
 118          }
 119      }
 120  
 121      /**
 122      * Register a class that knows how to handle template files with the given
 123      * extension. This can be used to implement new template types.
 124      * The constructor for the class must take the AkActionView instance
 125      * as a parameter, and the class must implement a "render" method that
 126      * takes the contents of the template to render as well as the array of
 127      * local assigns available to the template. The "render" method ought to
 128      * return the rendered template as a string.
 129      */
 130      function _registerTemplateHandler($extension, $className)
 131      {
 132          $this->_template_handlers[$extension] = $className;
 133      }
 134  
 135      function AkActionView($base_path = null, $assigns_for_first_render = array(), $controller = null)
 136      {
 137          $this->base_path = empty($base_path) ? AK_VIEWS_DIR : $base_path;
 138          $this->assigns = $assigns_for_first_render;
 139          $this->assigns_added = null;
 140          $this->controller = $controller;
 141          $this->logger = !empty($controller) && !empty($controller->Logger);
 142      }
 143  
 144      /**
 145      * Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true, 
 146      * it's relative to the template_root, otherwise it's absolute. The array in <tt>local_assigns</tt> 
 147      * is made available as local variables.
 148      */
 149      function renderFile($template_path, $use_full_path = true, $local_assigns = array())
 150      {
 151          if(empty($this->first_render)){
 152              $this->first_render = $template_path;
 153          }
 154  
 155          $template_path = substr($template_path,0,7) === 'layouts' ? AK_VIEWS_DIR.DS.$template_path.'.tpl' : $template_path;
 156  
 157          if(!$use_full_path && strstr($template_path,'.')){
 158              $template_file_name = $template_path;
 159              $template_extension = substr($template_path,strpos($template_path,'.')+1);
 160          }else{
 161              $template_extension = $this->pickTemplateExtension($template_path);
 162              $template_file_name = $this->getFullTemplatePath($template_path, $template_extension);
 163          }
 164  
 165          return $this->renderTemplate($template_extension, null, $template_file_name, $local_assigns);
 166      }
 167      /**
 168      * Renders the template present at <tt>template_path</tt> (relative to the template_root). 
 169      * The array in <tt>local_assigns</tt> is made available as local variables.
 170      */
 171      function render($options = array())
 172      {
 173          if(is_string($options)){
 174              return $this->renderFile($options, true);
 175          }elseif(is_array($options)){
 176              $options['locals'] = empty($options['locals']) ? array() : $options['locals'];
 177              $options['use_full_path'] = empty($options['use_full_path']) ? true : false;
 178  
 179              if (!empty($options['file'])){
 180                  return $this->renderFile($options['file'], $options['use_full_path'], $options['locals']);
 181              }elseif (!empty($options['partial']) && !empty($options['collection'])){
 182                  return $this->renderPartialCollection($options['partial'], $options['collection'], @$options['spacer_template'], @$options['locals']);
 183              }elseif (!empty($options['partial'])){
 184                  return $this->renderPartial($options['partial'], @$options['object'], @$options['locals']);
 185              }elseif ($options['inline']){
 186                  return $this->renderTemplate(empty($options['type']) ? 'tpl' : $options['type'], $options['inline'], null, empty($options['locals']) ? array() : $options['locals']);
 187              }
 188          }
 189      }
 190  
 191      /*
 192      * Renders the +template+ which is given as a string as tpl.php or js.tpl depending on <tt>template_extension</tt>.
 193      * The array in <tt>local_assigns</tt> is made available as local variables.
 194      */
 195      function renderTemplate($____template_extension, $____template, $____file_path = null, $____local_assigns = array(), $____save_content_in_attribute_as = 'layout')
 196      {
 197          $____result = '';
 198          $____controller_extras = isset($this->_controllerInstance) ? array('controller_name' => $this->_controllerInstance->getControllerName(), 'controller' => &$this->_controllerInstance) : array();
 199          $____local_assigns = array_merge(array_merge($this->_getGlobals(),(array)@$this->assigns,array_merge((array)@$this->_local_assigns,
 200          array_merge((array)$____local_assigns, $____controller_extras))));
 201  
 202          if(!empty($this->_template_handlers[$____template_extension])){
 203              $____handler =& $this->_template_handlers[$____template_extension];
 204              $____template = empty($____template) ? $this->_readTemplateFile($____file_path) : $____template;
 205              $____result = $this->_delegateRender(&$____handler, $____template, $____local_assigns, $____file_path);
 206              if(is_array($____result)){
 207                  $____save_content_in_attribute_as = $____result[0];
 208                  $____result = $____result[1];
 209              }
 210          }else{
 211              trigger_error(Ak::t('Could not find a template engine for delegating templates with the extension %extension',array('%extension'=>$____template_extension)), E_USER_ERROR);
 212          }
 213          $this->{'content_for_'.$____save_content_in_attribute_as} = $____result;
 214  
 215          return $____result;
 216      }
 217  
 218      function addSharedAttributes(&$local_assigns)
 219      {
 220          $this->_local_assigns =& $local_assigns;
 221      }
 222  
 223      function pickTemplateExtension($template_path)
 224      {
 225          if($match = $this->delegateTemplateExists($template_path)){
 226              return $match;
 227          }elseif($this->_templateExists($template_path,'tpl.php')){
 228              return 'tpl.php';
 229          }elseif($this->_templateExists($template_path, 'js.tpl')){
 230              return 'js.tpl';
 231          }else{
 232              trigger_error(Ak::t('No tpl.php, js.tpl or delegate template found for %template_path',array('%template_path'=>$template_path)), E_USER_ERROR);
 233              return false;
 234          }
 235      }
 236  
 237      function delegateTemplateExists($template_path)
 238      {
 239          foreach (array_keys($this->_template_handlers) as $k){
 240              if($this->_templateExists($template_path, $k)){
 241                  return $k;
 242              }
 243          }
 244          return false;
 245      }
 246  
 247      /**
 248      * Returns true is the file may be rendered implicitly.
 249      */
 250      function fileIsPublic($template_path)
 251      {
 252          return strpos(strrchr($template_path,DS),'_') !== 1;
 253      }
 254  
 255      function getFullTemplatePath($template_path, $extension)
 256      {
 257          $template_path = substr($template_path,-1*strlen($extension)) == $extension ? $template_path : $template_path.'.'.$extension;
 258          return substr($template_path,0,strlen(AK_VIEWS_DIR)) == AK_VIEWS_DIR ? $template_path : $this->base_path.DS.$template_path;
 259      }
 260  
 261      function _templateExists($template_path, $extension)
 262      {
 263          $file_path = $this->getFullTemplatePath($template_path, $extension);
 264  
 265          return !empty($this->_method_names[$file_path]) || file_exists($file_path);
 266      }
 267  
 268      /**
 269        * This method reads a template file.
 270        */
 271      function _readTemplateFile($template_path)
 272      {
 273          return Ak::file_get_contents($template_path);
 274      }
 275  
 276      function evaluateAssigns()
 277      {
 278          if(empty($this->assigns_added)){
 279              $this->_assignVariablesFromController();
 280              $this->assigns_added = true;
 281          }
 282      }
 283  
 284      function _delegateRender($handler, $template, $local_assigns, $file_path)
 285      {
 286          $HandlerInstance = new $handler($this);
 287          return $HandlerInstance->render($template, $local_assigns, $file_path);
 288      }
 289  
 290      function _assignVariablesFromController()
 291      {
 292          foreach ($this->assigns as $k=>$v){
 293              $this->$k = $v;
 294          }
 295      }
 296  
 297      function _javascriptTemplateExists($template_path)
 298      {
 299          return $this->_templateExists($template_path,'js.tpl');
 300      }
 301  
 302      /**
 303      * Partial Views
 304      * 
 305      *  There's also a convenience method for rendering sub templates within the current controller that depends on a single object 
 306      *  (we call this kind of sub templates for partials). It relies on the fact that partials should follow the naming convention of being 
 307      *  prefixed with an underscore -- as to separate them from regular templates that could be rendered on their own. 
 308      * 
 309      *  In a template for AdvertiserController::account:
 310      * 
 311      *   <?= $controller->render(array('partial' => 'account')); ?>
 312      * 
 313      *  This would render "advertiser/_account.tpl" and pass the instance variable $controller->account in as a local variable $account to 
 314      *  the template for display.
 315      * 
 316      *  In another template for Advertiser::buy, we could have:
 317      * 
 318      *    <?= $controller->render(array('partial' =>'account','locals'=>array('account'=>$buyer)));  ?>
 319      * 
 320      *    <?php foreach($advertisements as $ad) : ?>
 321      *      <?= $controller->render(array('partial'=>'ad','locals'=>array('ad'=>$ad))); ?>
 322      *    <?php endforeach; ?>
 323      * 
 324      *  This would first render "advertiser/_account.tpl" with $buyer passed in as the local variable $account, then render 
 325      *  "advertiser/_ad.tpl" and pass the local variable $ad to the template for display.
 326      * 
 327      *  == Rendering a collection of partials
 328      * 
 329      *  The example of partial use describes a familiar pattern where a template needs to iterate over an array and render a sub
 330      *  template for each of the elements. This pattern has been implemented as a single method that accepts an array and renders
 331      *  a partial by the same name as the elements contained within. So the three-lined example in "Using partials" can be rewritten
 332      *  with a single line:
 333      * 
 334      *    <?= $controller->render(array('partial'=>'ad','collection'=>(array)$advertisements)); ?>
 335      * 
 336      *  This will render "advertiser/_ad.tpl" and pass the local variable +ad+ to the template for display. An iteration counter
 337      *  will automatically be made available to the template with a name of the form +partial_name_counter+. In the case of the 
 338      *  example above, the template would be fed +ad_counter+.
 339      * 
 340      *  == Rendering shared partials
 341      * 
 342      *  Two controllers can share a set of partials and render them like this:
 343      * 
 344      *    <?= $controller->render(array('partial'=>'advertiser/ad', 'locals' => array('ad' => $advertisement ))); ?>
 345      * 
 346      *  This will render the partial "advertiser/_ad.tpl" regardless of which controller this is being called from.
 347      */
 348      function renderPartial($partial_path, $object, $local_assigns = array())
 349      {
 350          $path = $this->_partialPathPiece($partial_path);
 351          $partial_name = $this->_partialPathName($partial_path);
 352          
 353          $object =& $this->_extractingObject($partial_name, $local_assigns);
 354          $local_assigns = array_merge((array)@$this->_controllerInstance->_assigns, (array)$local_assigns);
 355          $this->_addObjectToLocalAssigns_($partial_name, $local_assigns, $object);
 356          return $this->renderFile((empty($path) ? '' : $path.DS).'_'.$partial_name, true, $local_assigns);
 357      }
 358  
 359      function renderPartialCollection($partial_name, $collection, $partial_spacer_template = null, $local_assigns = array())
 360      {
 361          Ak::profile('Rendering partial Collection'.$partial_name);
 362          $collection_of_partials = array();
 363          $counter_name = $this->_partialCounterName($partial_name);
 364          if(empty($local_assigns[$counter_name])){
 365              $local_assigns[$counter_name] = 1;
 366          }
 367  
 368          foreach ($collection as $counter=>$element){
 369              $local_assigns[$counter_name] = $counter+1;
 370              $collection_of_partials[] = $this->renderPartial($partial_name, $element, $local_assigns);
 371          }
 372  
 373          Ak::profile('Finished rendering partial Collection'.$partial_name);
 374  
 375          if (empty($collection_of_partials)) {
 376              return ' ';
 377          }
 378  
 379          if (!empty($partial_spacer_template)){
 380              $spacer_path = $this->_partialPathPiece($partial_spacer_template);
 381              $spacer_name = $this->_partialPathName($partial_spacer_template);
 382              return join((empty($spacer_path) ? '' : $spacer_path.DS).'_'.$spacer_name,$collection_of_partials);
 383          }else{
 384              return join('',$collection_of_partials);
 385          }
 386      }
 387  
 388      function renderCollectionOfPartials($partial_name, $collection, $partial_spacer_template = null, $local_assigns = array())
 389      {
 390          return $this->renderPartialCollection($partial_name, $collection, $partial_spacer_template, $local_assigns);
 391      }
 392  
 393  
 394      function _partialPathPiece($partial_path)
 395      {
 396          if(strstr($partial_path, '/')){
 397              $dir_name = dirname($partial_path);
 398              if(strstr($dir_name,'/')){
 399                  return $dir_name;
 400              }else{
 401                  return AK_VIEWS_DIR.DS.$dir_name;
 402              }
 403          }else{
 404              return '';
 405          }
 406      }
 407  
 408      function _partialPathName($partial_path)
 409      {
 410          return strstr($partial_path, '/') ? basename($partial_path) : $partial_path;
 411      }
 412  
 413      function _partialCounterName($partial_name)
 414      {
 415          return array_pop(explode('/',$partial_name)).'_counter';
 416      }
 417  
 418      function &_extractingObject($partial_name, &$deprecated_local_assigns)
 419      {
 420          if(is_array($deprecated_local_assigns)){
 421              return $this->controller->$partial_name;
 422          }else{
 423              return $deprecated_local_assigns;
 424          }
 425      }
 426  
 427      function _addObjectToLocalAssigns($partial_name, $local_assigns, &$object)
 428      {
 429          $local_assigns[$partial_name] = empty($object) ? $this->controller->$partial_name : $object;
 430      }
 431  
 432      function _addObjectToLocalAssigns_($partial_name, &$local_assigns, $object)
 433      {
 434          if(!empty($object)){
 435              $local_assigns[$partial_name] = $object;
 436          }elseif(!empty($this->controller->$partial_name)){
 437              $local_assigns[$partial_name] = $this->controller->$partial_name;
 438          }
 439  
 440      }
 441  
 442      /**
 443       * Variables assigned using this method will act on any controller or action. Use this in conjunction
 444       * with your application helpers in order to allow variable passing from inside your views.
 445       * This is used for example on the capture helper.
 446       * 
 447       * @static 
 448       */
 449      function _addGlobalVar($var_name, $value, $_retrieve = false)
 450      {
 451          static $_global_vars = array();
 452          if($_retrieve){
 453              return $_global_vars;
 454          }
 455          if($var_name[0] != '_'){
 456              $_global_vars[$var_name] =& $value;
 457          }
 458      }
 459       /**
 460       * @static 
 461       */
 462      function _getGlobals()
 463      {
 464          return AkActionView::_addGlobalVar(null,null,true);
 465      }
 466      
 467  }
 468  
 469  ?>


Generated: Mon Oct 27 12:43:49 2008 Cross-referenced by PHPXref 0.6