| [ Index ] |
PHP Cross Reference of Akelos Framework |
[Summary view] [Print] [Text view]
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 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Oct 27 12:43:49 2008 | Cross-referenced by PHPXref 0.6 |