sfView.class.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
  5. * (c) 2004-2006 Sean Kerr <sean@code-box.org>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. /**
  11. * A view represents the presentation layer of an action. Output can be
  12. * customized by supplying attributes, which a template can manipulate and
  13. * display.
  14. *
  15. * @package symfony
  16. * @subpackage view
  17. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  18. * @author Sean Kerr <sean@code-box.org>
  19. * @version SVN: $Id: sfView.class.php 13572 2008-12-01 02:10:36Z dwhittle $
  20. */
  21. abstract class sfView
  22. {
  23. /**
  24. * Show an alert view.
  25. */
  26. const ALERT = 'Alert';
  27. /**
  28. * Show an error view.
  29. */
  30. const ERROR = 'Error';
  31. /**
  32. * Show a form input view.
  33. */
  34. const INPUT = 'Input';
  35. /**
  36. * Skip view execution.
  37. */
  38. const NONE = 'None';
  39. /**
  40. * Show a success view.
  41. */
  42. const SUCCESS = 'Success';
  43. /**
  44. * Do not render the presentation.
  45. */
  46. const RENDER_NONE = 1;
  47. /**
  48. * Render the presentation to the client.
  49. */
  50. const RENDER_CLIENT = 2;
  51. /**
  52. * Render the presentation to a variable.
  53. */
  54. const RENDER_VAR = 4;
  55. /**
  56. * Skip view rendering but output http headers
  57. */
  58. const HEADER_ONLY = 8;
  59. protected
  60. $context = null,
  61. $dispatcher = null,
  62. $decorator = false,
  63. $decoratorDirectory = null,
  64. $decoratorTemplate = null,
  65. $directory = null,
  66. $componentSlots = array(),
  67. $template = null,
  68. $attributeHolder = null,
  69. $parameterHolder = null,
  70. $moduleName = '',
  71. $actionName = '',
  72. $viewName = '',
  73. $extension = '.php';
  74. /**
  75. * Class constructor.
  76. *
  77. * @see initialize()
  78. */
  79. public function __construct($context, $moduleName, $actionName, $viewName)
  80. {
  81. $this->initialize($context, $moduleName, $actionName, $viewName);
  82. }
  83. /**
  84. * Initializes this view.
  85. *
  86. * @param sfContext $context The current application context
  87. * @param string $moduleName The module name for this view
  88. * @param string $actionName The action name for this view
  89. * @param string $viewName The view name
  90. *
  91. * @return bool true, if initialization completes successfully, otherwise false
  92. */
  93. public function initialize($context, $moduleName, $actionName, $viewName)
  94. {
  95. $this->moduleName = $moduleName;
  96. $this->actionName = $actionName;
  97. $this->viewName = $viewName;
  98. $this->context = $context;
  99. $this->dispatcher = $context->getEventDispatcher();
  100. sfOutputEscaper::markClassesAsSafe(array('sfForm', 'sfModelGeneratorHelper'));
  101. $this->attributeHolder = $this->initializeAttributeHolder();
  102. $this->parameterHolder = new sfParameterHolder();
  103. $this->parameterHolder->add(sfConfig::get('mod_'.strtolower($moduleName).'_view_param', array()));
  104. $request = $context->getRequest();
  105. if (!is_null($format = $request->getRequestFormat()))
  106. {
  107. if ('html' != $format)
  108. {
  109. $this->setExtension('.'.$format.$this->getExtension());
  110. }
  111. if ($mimeType = $request->getMimeType($format))
  112. {
  113. $this->context->getResponse()->setContentType($mimeType);
  114. if ('html' != $format)
  115. {
  116. $this->setDecorator(false);
  117. }
  118. }
  119. $this->dispatcher->notify(new sfEvent($this, 'view.configure_format', array('format' => $format, 'response' => $context->getResponse(), 'request' => $context->getRequest())));
  120. }
  121. // include view configuration
  122. $this->configure();
  123. return true;
  124. }
  125. protected function initializeAttributeHolder($attributes = array())
  126. {
  127. if ('both' === sfConfig::get('sf_escaping_strategy'))
  128. {
  129. $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Escaping strategy "both" is deprecated, please use "on".', 'priority' => sfLogger::ERR)));
  130. sfConfig::set('sf_escaping_strategy', 'on');
  131. }
  132. else if ('bc' === sfConfig::get('sf_escaping_strategy'))
  133. {
  134. $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Escaping strategy "bc" is deprecated, please use "off".', 'priority' => sfLogger::ERR)));
  135. sfConfig::set('sf_escaping_strategy', 'off');
  136. }
  137. $attributeHolder = new sfViewParameterHolder($this->dispatcher, $attributes, array(
  138. 'escaping_method' => sfConfig::get('sf_escaping_method'),
  139. 'escaping_strategy' => sfConfig::get('sf_escaping_strategy'),
  140. ));
  141. return $attributeHolder;
  142. }
  143. /**
  144. * Executes any presentation logic and set template attributes.
  145. */
  146. abstract function execute();
  147. /**
  148. * Configures template.
  149. */
  150. abstract function configure();
  151. /**
  152. * Retrieves this views decorator template directory.
  153. *
  154. * @return string An absolute filesystem path to this views decorator template directory
  155. */
  156. public function getDecoratorDirectory()
  157. {
  158. return $this->decoratorDirectory;
  159. }
  160. /**
  161. * Retrieves this views decorator template.
  162. *
  163. * @return string A template filename, if a template has been set, otherwise null
  164. */
  165. public function getDecoratorTemplate()
  166. {
  167. return $this->decoratorTemplate;
  168. }
  169. /**
  170. * Retrieves this view template directory.
  171. *
  172. * @return string An absolute filesystem path to this views template directory
  173. */
  174. public function getDirectory()
  175. {
  176. return $this->directory;
  177. }
  178. /**
  179. * Retrieves the template engine associated with this view.
  180. *
  181. * Note: This will return null for PHPView instances.
  182. *
  183. * @return mixed A template engine instance
  184. */
  185. abstract function getEngine();
  186. /**
  187. * Retrieves this views template.
  188. *
  189. * @return string A template filename, if a template has been set, otherwise null
  190. */
  191. public function getTemplate()
  192. {
  193. return $this->template;
  194. }
  195. /**
  196. * Retrieves attributes for the current view.
  197. *
  198. * @return sfParameterHolder The attribute parameter holder
  199. */
  200. public function getAttributeHolder()
  201. {
  202. return $this->attributeHolder;
  203. }
  204. /**
  205. * Retrieves an attribute for the current view.
  206. *
  207. * @param string $name Name of the attribute
  208. * @param string $default Value of the attribute
  209. *
  210. * @return mixed Attribute
  211. */
  212. public function getAttribute($name, $default = null)
  213. {
  214. return $this->attributeHolder->get($name, $default);
  215. }
  216. /**
  217. * Returns true if the view have attributes.
  218. *
  219. * @param string $name Name of the attribute
  220. *
  221. * @return mixed Attribute of the view
  222. */
  223. public function hasAttribute($name)
  224. {
  225. return $this->attributeHolder->has($name);
  226. }
  227. /**
  228. * Sets an attribute of the view.
  229. *
  230. * @param string $name Attribute name
  231. * @param string $value Value for the attribute
  232. */
  233. public function setAttribute($name, $value)
  234. {
  235. $this->attributeHolder->set($name, $value);
  236. }
  237. /**
  238. * Retrieves the parameters for the current view.
  239. *
  240. * @return sfParameterHolder The parameter holder
  241. */
  242. public function getParameterHolder()
  243. {
  244. return $this->parameterHolder;
  245. }
  246. /**
  247. * Retrieves a parameter from the current view.
  248. *
  249. * @param string $name Parameter name
  250. * @param string $default Default parameter value
  251. *
  252. * @return mixed A parameter value
  253. */
  254. public function getParameter($name, $default = null)
  255. {
  256. return $this->parameterHolder->get($name, $default);
  257. }
  258. /**
  259. * Indicates whether or not a parameter exist for the current view.
  260. *
  261. * @param string $name Name of the paramater
  262. *
  263. * @return bool true, if the parameter exists otherwise false
  264. */
  265. public function hasParameter($name)
  266. {
  267. return $this->parameterHolder->has($name);
  268. }
  269. /**
  270. * Sets a parameter for the view.
  271. *
  272. * @param string $name Name of the parameter
  273. * @param string $value The parameter value
  274. */
  275. public function setParameter($name, $value)
  276. {
  277. $this->parameterHolder->set($name, $value);
  278. }
  279. /**
  280. * Indicates that this view is a decorating view.
  281. *
  282. * @return bool true, if this view is a decorating view, otherwise false
  283. */
  284. public function isDecorator()
  285. {
  286. return $this->decorator;
  287. }
  288. /**
  289. * Sets the decorating mode for the current view.
  290. *
  291. * @param bool $boolean Set the decorating mode for the view
  292. */
  293. public function setDecorator($boolean)
  294. {
  295. $this->decorator = (boolean) $boolean;
  296. if (false === $boolean)
  297. {
  298. $this->decoratorTemplate = false;
  299. }
  300. }
  301. /**
  302. * Executes a basic pre-render check to verify all required variables exist
  303. * and that the template is readable.
  304. *
  305. * @throws sfRenderException If the pre-render check fails
  306. */
  307. protected function preRenderCheck()
  308. {
  309. if (is_null($this->template))
  310. {
  311. // a template has not been set
  312. throw new sfRenderException('A template has not been set.');
  313. }
  314. if (!is_readable($this->directory.'/'.$this->template))
  315. {
  316. // 404?
  317. if ('404' == $this->context->getResponse()->getStatusCode())
  318. {
  319. // use default exception templates
  320. $this->template = sfException::getTemplatePathForError($this->context->getRequest()->getRequestFormat(), false);
  321. $this->directory = dirname($this->template);
  322. $this->template = basename($this->template);
  323. $this->setAttribute('code', '404');
  324. $this->setAttribute('text', 'Not Found');
  325. }
  326. else
  327. {
  328. throw new sfRenderException(sprintf('The template "%s" does not exist or is unreadable in "%s".', $this->template, $this->directory));
  329. }
  330. }
  331. // check to see if this is a decorator template
  332. if ($this->decorator && !is_readable($this->decoratorDirectory.'/'.$this->decoratorTemplate))
  333. {
  334. throw new sfRenderException(sprintf('The decorator template "%s" does not exist or is unreadable in "%s".', $this->decoratorTemplate, $this->decoratorDirectory));
  335. }
  336. }
  337. /**
  338. * Renders the presentation.
  339. *
  340. * @return string A string representing the rendered presentation
  341. */
  342. abstract function render();
  343. /**
  344. * Sets the decorator template directory for this view.
  345. *
  346. * @param string $directory An absolute filesystem path to a template directory
  347. */
  348. public function setDecoratorDirectory($directory)
  349. {
  350. $this->decoratorDirectory = $directory;
  351. }
  352. /**
  353. * Sets the decorator template for this view.
  354. *
  355. * If the template path is relative, it will be based on the currently
  356. * executing module's template sub-directory.
  357. *
  358. * @param string $template An absolute or relative filesystem path to a template
  359. */
  360. public function setDecoratorTemplate($template)
  361. {
  362. if (false === $template)
  363. {
  364. $this->setDecorator(false);
  365. return;
  366. }
  367. else if (is_null($template))
  368. {
  369. return;
  370. }
  371. if (!strpos($template, '.'))
  372. {
  373. $template .= $this->getExtension();
  374. }
  375. if (sfToolkit::isPathAbsolute($template))
  376. {
  377. $this->decoratorDirectory = dirname($template);
  378. $this->decoratorTemplate = basename($template);
  379. }
  380. else
  381. {
  382. $this->decoratorDirectory = $this->context->getConfiguration()->getDecoratorDir($template);
  383. $this->decoratorTemplate = $template;
  384. }
  385. // set decorator status
  386. $this->decorator = true;
  387. }
  388. /**
  389. * Sets the template directory for this view.
  390. *
  391. * @param string $directory An absolute filesystem path to a template directory
  392. */
  393. public function setDirectory($directory)
  394. {
  395. $this->directory = $directory;
  396. }
  397. /**
  398. * Sets the module and action to be executed in place of a particular template attribute.
  399. *
  400. * @param string $attributeName A template attribute name
  401. * @param string $moduleName A module name
  402. * @param string $componentName A component name
  403. */
  404. public function setComponentSlot($attributeName, $moduleName, $componentName)
  405. {
  406. $this->componentSlots[$attributeName] = array();
  407. $this->componentSlots[$attributeName]['module_name'] = $moduleName;
  408. $this->componentSlots[$attributeName]['component_name'] = $componentName;
  409. }
  410. /**
  411. * Indicates whether or not a component slot exists.
  412. *
  413. * @param string $name The component slot name
  414. *
  415. * @return bool true, if the component slot exists, otherwise false
  416. */
  417. public function hasComponentSlot($name)
  418. {
  419. return isset($this->componentSlots[$name]);
  420. }
  421. /**
  422. * Gets a component slot
  423. *
  424. * @param string $name The component slot name
  425. *
  426. * @return array The component slot
  427. */
  428. public function getComponentSlot($name)
  429. {
  430. if (isset($this->componentSlots[$name]) && $this->componentSlots[$name]['module_name'] && $this->componentSlots[$name]['component_name'])
  431. {
  432. return array($this->componentSlots[$name]['module_name'], $this->componentSlots[$name]['component_name']);
  433. }
  434. return null;
  435. }
  436. /**
  437. * Sets the template for this view.
  438. *
  439. * If the template path is relative, it will be based on the currently
  440. * executing module's template sub-directory.
  441. *
  442. * @param string $template An absolute or relative filesystem path to a template
  443. */
  444. public function setTemplate($template)
  445. {
  446. if (sfToolkit::isPathAbsolute($template))
  447. {
  448. $this->directory = dirname($template);
  449. $this->template = basename($template);
  450. }
  451. else
  452. {
  453. $this->directory = $this->context->getConfiguration()->getTemplateDir($this->moduleName, $template);
  454. $this->template = $template;
  455. }
  456. }
  457. /**
  458. * Retrieves the current view extension.
  459. *
  460. * @return string The extension for current view.
  461. */
  462. public function getExtension()
  463. {
  464. return $this->extension;
  465. }
  466. /**
  467. * Sets an extension for the current view.
  468. *
  469. * @param string $extension The extension name.
  470. */
  471. public function setExtension($extension)
  472. {
  473. $this->extension = $extension;
  474. }
  475. /**
  476. * Gets the module name associated with this view.
  477. *
  478. * @return string A module name
  479. */
  480. public function getModuleName()
  481. {
  482. return $this->moduleName;
  483. }
  484. /**
  485. * Gets the action name associated with this view.
  486. *
  487. * @return string An action name
  488. */
  489. public function getActionName()
  490. {
  491. return $this->actionName;
  492. }
  493. /**
  494. * Gets the view name associated with this view.
  495. *
  496. * @return string An action name
  497. */
  498. public function getViewName()
  499. {
  500. return $this->viewName;
  501. }
  502. /**
  503. * Calls methods defined via sfEventDispatcher.
  504. *
  505. * @param string $method The method name
  506. * @param array $arguments The method arguments
  507. *
  508. * @return mixed The returned value of the called method
  509. *
  510. * @throws sfException< If the calls fails
  511. */
  512. public function __call($method, $arguments)
  513. {
  514. $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'view.method_not_found', array('method' => $method, 'arguments' => $arguments)));
  515. if (!$event->isProcessed())
  516. {
  517. throw new sfException(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
  518. }
  519. return $event->getReturnValue();
  520. }
  521. }