root/branches/0.11/src/view/AgaviView.class.php

Revision 2675, 14.7 KB (checked in by david, 3 months ago)

Fixed #813: AgaviView::initialize() incorrectly assigns container's response to a property

  • Property keywords set to Id
  • Property svn:keywords set to Id
Line 
1<?php
2
3// +---------------------------------------------------------------------------+
4// | This file is part of the Agavi package.                                   |
5// | Copyright (c) 2005-2008 the Agavi Project.                                |
6// | Based on the Mojavi3 MVC Framework, Copyright (c) 2003-2005 Sean Kerr.    |
7// |                                                                           |
8// | For the full copyright and license information, please view the LICENSE   |
9// | file that was distributed with this source code. You can also view the    |
10// | LICENSE file online at http://www.agavi.org/LICENSE.txt                   |
11// |   vi: set noexpandtab:                                                    |
12// |   Local Variables:                                                        |
13// |   indent-tabs-mode: t                                                     |
14// |   End:                                                                    |
15// +---------------------------------------------------------------------------+
16
17/**
18 * A view represents the presentation layer of an action. Output can be
19 * customized by supplying attributes, which a template can manipulate and
20 * display.
21 *
22 * @package    agavi
23 * @subpackage view
24 *
25 * @author     Sean Kerr <skerr@mojavi.org>
26 * @author     David Zülke <dz@bitxtender.com>
27 * @copyright  Authors
28 * @copyright  The Agavi Project
29 *
30 * @since      0.9.0
31 *
32 * @version    $Id$
33 */
34abstract class AgaviView
35{
36  /**
37   * @since      0.9.0
38   */
39  const NONE = null;
40
41  /**
42   * @var        AgaviExecutionContainer This view's execution container.
43   */
44  protected $container = null;
45
46  /**
47   * @var        AgaviContext The AgaviContext instance this View belongs to.
48   */
49  protected $context = null;
50
51  /**
52   * @var        array An array of defined layers.
53   */
54  protected $layers = array();
55
56  /**
57   * Execute any presentation logic and set template attributes.
58   *
59   * @param      AgaviRequestDataHolder The action's request data holder.
60   *
61   * @return     AgaviExecutionContainer An array of forwarding information in
62   *                                     case a forward should occur, or null.
63   *
64   * @author     Sean Kerr <skerr@mojavi.org>
65   * @author     David Zülke <dz@bitxtender.com>
66   * @since      0.9.0
67   */
68  abstract function execute(AgaviRequestDataHolder $rd);
69
70  /**
71   * Retrieve the current application context.
72   *
73   * @return     AgaviContext The current AgaviContext instance.
74   *
75   * @author     Sean Kerr <skerr@mojavi.org>
76   * @since      0.9.0
77   */
78  public final function getContext()
79  {
80    return $this->context;
81  }
82
83  /**
84   * Retrieve the execution container for this action.
85   *
86   * @return     AgaviExecutionContainer This action's execution container.
87   *
88   * @author     David Zülke <dz@bitxtender.com>
89   * @since      0.11.0
90   */
91  public final function getContainer()
92  {
93    return $this->container;
94  }
95
96  /**
97   * Retrieve the Response instance for this View.
98   *
99   * @return     AgaviResponse The Response instance.
100   *
101   * @author     David Zülke <dz@bitxtender.com>
102   * @since      0.11.0
103   */
104  public final function getResponse()
105  {
106    return $this->container->getResponse();
107  }
108
109  /**
110   * Initialize this view.
111   *
112   * @param      AgaviExecutionContainer This View's execution container.
113   *
114   * @author     David Zülke <dz@bitxtender.com>
115   * @since      0.9.0
116   */
117  public function initialize(AgaviExecutionContainer $container)
118  {
119    $this->container = $container;
120
121    $this->context = $container->getContext();
122  }
123
124  /**
125   * Create a new template layer object.
126   *
127   * This will automatically set the name of the layer, the current module, the
128   * current view name as the template, and the output type name.
129   *
130   * @param      string The class name of the AgaviTemplateLayer implementation.
131   * @param      string The name of the layer.
132   * @param      mixed  An optional name of the non-default renderer to use, or
133   *                    an AgaviRenderer instance to use.
134   *
135   * @return     AgaviTemplateLayer A template layer instance.
136   *
137   * @author     David Zülke <dz@bitxtender.com>
138   * @since      0.11.0
139   */
140  public function createLayer($class, $name, $renderer = null)
141  {
142    $layer = new $class();
143    if(!is_subclass_of($layer, 'AgaviTemplateLayer')) {
144      throw new AgaviViewException('Class "$class" is not a subclass of AgaviTemplateLayer');
145    }
146    $layer->initialize($this->context, array('name' => $name, 'module' => $this->container->getViewModuleName(), 'template' => $this->container->getViewName(), 'output_type' => $this->container->getOutputType()->getName()));
147    if($renderer instanceof AgaviRenderer) {
148      $layer->setRenderer($renderer);
149    } else {
150      $layer->setRenderer($this->container->getOutputType()->getRenderer($renderer));
151    }
152    return $layer;
153  }
154
155  /**
156   * Append a layer to the list of layers.
157   *
158   * If no reference layer is given, the layer will be added to the end of the
159   * list.
160   *
161   * @param      AgaviTemplateLayer The layer to insert.
162   * @param      AgaviTemplateLayer An optional other layer to insert after.
163   *
164   * @return     AgaviTemplateLayer The template layer that was inserted.
165   *
166   * @author     David Zülke <dz@bitxtender.com>
167   * @since      0.11.0
168   */
169  public function appendLayer(AgaviTemplateLayer $layer, AgaviTemplateLayer $otherLayer = null)
170  {
171    if($otherLayer !== null && !in_array($otherLayer, $this->layers, true)) {
172      throw new AgaviViewException('Layer "' . $otherLayer->getName() . '" not in list');
173    }
174
175    if(($pos = array_search($layer, $this->layers, true)) !== false) {
176      // given layer is already in the list, so we remove it first
177      array_splice($this->layers, $pos, 1);
178    }
179
180    if($otherLayer === null) {
181      $dest = count($this->layers);
182    } elseif($otherLayer === $layer) {
183      $dest = $pos;
184    } else {
185      $dest = array_search($otherLayer, $this->layers, true) + 1;
186    }
187    array_splice($this->layers, $dest, 0, array($layer));
188
189    return $layer;
190  }
191
192  /**
193   * Prepend a layer to the list of layers.
194   *
195   * If no reference layer is given, the layer will be added to the beginning of
196   * the list.
197   *
198   * @param      AgaviTemplateLayer The layer to insert.
199   * @param      AgaviTemplateLayer An optional other layer to insert before.
200   *
201   * @return     AgaviTemplateLayer The template layer that was inserted.
202   *
203   * @author     David Zülke <dz@bitxtender.com>
204   * @since      0.11.0
205   */
206  public function prependLayer(AgaviTemplateLayer $layer, AgaviTemplateLayer $otherLayer = null)
207  {
208    if($otherLayer !== null && !in_array($otherLayer, $this->layers, true)) {
209      throw new AgaviViewException('Layer "' . $otherLayer->getName() . '" not in list');
210    }
211
212    if(($pos = array_search($layer, $this->layers, true)) !== false) {
213      // given layer is already in the list, so we remove it first
214      array_splice($this->layers, $pos, 1);
215    }
216
217    if($otherLayer === null) {
218      $dest = 0;
219    } elseif($otherLayer === $layer) {
220      $dest = $pos;
221    } else {
222      $dest = array_search($otherLayer, $this->layers, true);
223    }
224    array_splice($this->layers, $dest, 0, array($layer));
225
226    return $layer;
227  }
228
229  /**
230   * Remove a layer from the list.
231   *
232   * @param      AgaviTemplateLayer The layer to remove.
233   *
234   * @author     David Zülke <dz@bitxtender.com>
235   * @since      0.11.0
236   */
237  public function removeLayer(AgaviTemplateLayer $layer)
238  {
239    if(($pos = array_search($layer, $this->layers, true)) === false) {
240      throw new AgaviViewException('Layer "' . $otherLayer->getName() . '" not in list');
241    }
242    array_splice($this->layers, $pos, 1);
243  }
244
245  /**
246   * Remove all layers from the list.
247   *
248   * @author     David Zülke <dz@bitxtender.com>
249   * @since      0.11.0
250   */
251  public function clearLayers()
252  {
253    $this->layers = array();
254  }
255
256  /**
257   * Retrieve a layer from the list.
258   *
259   * @param      string The name of the layer.
260   *
261   * @return     AgaviTemplateLayer The layer instance, or null if not found.
262   *
263   * @author     David Zülke <dz@bitxtender.com>
264   * @since      0.11.0
265   */
266  public function getLayer($name)
267  {
268    foreach($this->layers as $layer) {
269      if($name == $layer->getName()) {
270        return $layer;
271      }
272    }
273  }
274
275  /**
276   * Get all layers from the list.
277   *
278   * @return     array An array of template layer instances.
279   *
280   * @author     David Zülke <dz@bitxtender.com>
281   * @since      0.11.0
282   */
283  public function getLayers()
284  {
285    return $this->layers;
286  }
287
288  /**
289   * Load a pre-configured layout.
290   *
291   * If no layout name is given, the default layout will be used.
292   *
293   * @param      string The (optional) name of the layout.
294   *
295   * @return     array An array of parameters set for the layout.
296   *
297   * @throws     AgaviException If the layout doesn't exist.
298   *
299   * @author     David Zülke <dz@bitxtender.com>
300   * @since      0.11.0
301   */
302  public function loadLayout($layoutName = null)
303  {
304    $layout = $this->container->getOutputType()->getLayout($layoutName);
305
306    $this->clearLayers();
307
308    foreach($layout['layers'] as $name => $layer) {
309      $l = $this->createLayer($layer['class'], $name, $layer['renderer']);
310      $l->setParameters($layer['parameters']);
311      foreach($layer['slots'] as $slotName => $slot) {
312        $l->setSlot($slotName, $this->createSlotContainer($slot['module'], $slot['action'], $slot['parameters'], $slot['output_type']));
313      }
314      $this->appendLayer($l);
315    }
316   
317    return $layout['parameters'];
318  }
319
320  /**
321   * Creates a new container with the same output type as this view's container.
322   *
323   * This container will have a parameter called 'is_slot' set to true.
324   *
325   * @param      string The name of the module.
326   * @param      string The name of the action.
327   * @param      mixed  An AgaviRequestDataHolder instance with additional
328   *                    request arguments or an array of request parameters.
329   * @param      string Optional name of an initial output type to set.
330   *
331   * @return     AgaviExecutionContainer A new execution container instance,
332   *                                     fully initialized.
333   *
334   * @see        AgaviExecutionContainer::createExecutionContainer()
335   *
336   * @author     David Zülke <dz@bitxtender.com>
337   * @since      0.11.0
338   */
339  public function createSlotContainer($moduleName, $actionName, $arguments = null, $outputType = null)
340  {
341    if($arguments !== null && !($arguments instanceof AgaviRequestDataHolder)) {
342      $rdhc = $this->context->getRequest()->getParameter('request_data_holder_class');
343      $arguments = new $rdhc(array(AgaviRequestDataHolder::SOURCE_PARAMETERS => $arguments));
344    }
345    $container = $this->container->createExecutionContainer($moduleName, $actionName, $arguments, $outputType);
346    $container->setParameter('is_slot', true);
347    return $container;
348  }
349
350  /**
351   * Creates a new container with the same output type as this view's container.
352   *
353   * This container will have a parameter called 'is_forward' set to true.
354   *
355   * @param      string The name of the module.
356   * @param      string The name of the action.
357   * @param      mixed  An AgaviRequestDataHolder instance with additional
358   *                    request arguments or an array of request parameters.
359   * @param      string Optional name of an initial output type to set.
360   *
361   * @return     AgaviExecutionContainer A new execution container instance,
362   *                                     fully initialized.
363   *
364   * @see        AgaviExecutionContainer::createExecutionContainer()
365   *
366   * @author     David Zülke <dz@bitxtender.com>
367   * @since      0.11.0
368   */
369  public function createForwardContainer($moduleName, $actionName, $arguments = null, $outputType = null)
370  {
371    if($arguments !== null) {
372      if(!($arguments instanceof AgaviRequestDataHolder)) {
373        $rdhc = $this->context->getRequest()->getParameter('request_data_holder_class');
374        $arguments = new $rdhc(array(AgaviRequestDataHolder::SOURCE_PARAMETERS => $arguments));
375      }
376    } else {
377      // we carry over our container's arguments
378      $arguments = $this->container->getArguments();
379    }
380    $container = $this->container->createExecutionContainer($moduleName, $actionName, $arguments, $outputType);
381    $container->setParameter('is_forward', true);
382    return $container;
383  }
384
385  /**
386   * @see        AgaviAttributeHolder::setAttributesByRef()
387   *
388   * @author     David Zülke <dz@bitxtender.com>
389   * @since      0.9.0
390   */
391  public function clearAttributes()
392  {
393    $this->container->clearAttributes();
394  }
395
396  /**
397   * @see        AgaviAttributeHolder::setAttributesByRef()
398   *
399   * @author     David Zülke <dz@bitxtender.com>
400   * @since      0.9.0
401   */
402  public function &getAttribute($name, $default = null)
403  {
404    return $this->container->getAttribute($name, null, $default);
405  }
406
407  /**
408   * @see        AgaviAttributeHolder::setAttributesByRef()
409   *
410   * @author     David Zülke <dz@bitxtender.com>
411   * @since      0.9.0
412   */
413  public function getAttributeNames()
414  {
415    return $this->container->getAttributeNames();
416  }
417
418  /**
419   * @see        AgaviAttributeHolder::setAttributesByRef()
420   *
421   * @author     David Zülke <dz@bitxtender.com>
422   * @since      0.11.0
423   */
424  public function &getAttributes()
425  {
426    return $this->container->getAttributes();
427  }
428
429  /**
430   * @see        AgaviAttributeHolder::setAttributesByRef()
431   *
432   * @author     David Zülke <dz@bitxtender.com>
433   * @since      0.9.0
434   */
435  public function hasAttribute($name)
436  {
437    return $this->container->hasAttribute($name);
438  }
439
440  /**
441   * @see        AgaviAttributeHolder::setAttributesByRef()
442   *
443   * @author     David Zülke <dz@bitxtender.com>
444   * @since      0.9.0
445   */
446  public function &removeAttribute($name)
447  {
448    return $this->container->removeAttribute($name);
449  }
450
451  /**
452   * @see        AgaviAttributeHolder::setAttributesByRef()
453   *
454   * @author     David Zülke <dz@bitxtender.com>
455   * @since      0.9.0
456   */
457  public function setAttribute($name, $value)
458  {
459    $this->container->setAttribute($name, $value);
460  }
461
462  /**
463   * @see        AgaviAttributeHolder::setAttributesByRef()
464   *
465   * @author     David Zülke <dz@bitxtender.com>
466   * @since      0.10.0
467   */
468  public function appendAttribute($name, $value)
469  {
470    $this->container->appendAttribute($name, $value);
471  }
472
473  /**
474   * @see        AgaviAttributeHolder::setAttributesByRef()
475   *
476   * @author     David Zülke <dz@bitxtender.com>
477   * @since      0.9.0
478   */
479  public function setAttributeByRef($name, &$value)
480  {
481    $this->container->setAttributeByRef($name, $value);
482  }
483
484  /**
485   * @see        AgaviAttributeHolder::setAttributesByRef()
486   *
487   * @author     David Zülke <dz@bitxtender.com>
488   * @since      0.10.0
489   */
490  public function appendAttributeByRef($name, &$value)
491  {
492    $this->container->appendAttributeByRef($name, $value);
493  }
494
495  /**
496   * @see        AgaviAttributeHolder::setAttributesByRef()
497   *
498   * @author     David Zülke <dz@bitxtender.com>
499   * @since      0.9.0
500   */
501  public function setAttributes(array $attributes)
502  {
503    $this->container->setAttributes($attributes);
504  }
505
506  /**
507   * @see        AgaviAttributeHolder::setAttributesByRef()
508   *
509   * @author     David Zülke <dz@bitxtender.com>
510   * @since      0.9.0
511   */
512  public function setAttributesByRef(array &$attributes)
513  {
514    $this->container->setAttributesByRef($attributes);
515  }
516}
517
518?>
Note: See TracBrowser for help on using the browser.