PageRenderTime 56ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/framework/web/CController.php

https://bitbucket.org/diegoaraujo/alexikeda
PHP | 1254 lines | 503 code | 81 blank | 670 comment | 94 complexity | 606a4550a416c9299918ce36bb7fd656 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, BSD-2-Clause
  1. <?php
  2. /**
  3. * CController class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CController manages a set of actions which deal with the corresponding user requests.
  12. *
  13. * Through the actions, CController coordinates the data flow between models and views.
  14. *
  15. * When a user requests an action 'XYZ', CController will do one of the following:
  16. * 1. Method-based action: call method 'actionXYZ' if it exists;
  17. * 2. Class-based action: create an instance of class 'XYZ' if the class is found in the action class map
  18. * (specified via {@link actions()}, and execute the action;
  19. * 3. Call {@link missingAction()}, which by default will raise a 404 HTTP exception.
  20. *
  21. * If the user does not specify an action, CController will run the action specified by
  22. * {@link defaultAction}, instead.
  23. *
  24. * CController may be configured to execute filters before and after running actions.
  25. * Filters preprocess/postprocess the user request/response and may quit executing actions
  26. * if needed. They are executed in the order they are specified. If during the execution,
  27. * any of the filters returns true, the rest filters and the action will no longer get executed.
  28. *
  29. * Filters can be individual objects, or methods defined in the controller class.
  30. * They are specified by overriding {@link filters()} method. The following is an example
  31. * of the filter specification:
  32. * <pre>
  33. * array(
  34. * 'accessControl - login',
  35. * 'ajaxOnly + search',
  36. * array(
  37. * 'COutputCache + list',
  38. * 'duration'=>300,
  39. * ),
  40. * )
  41. * </pre>
  42. * The above example declares three filters: accessControl, ajaxOnly, COutputCache. The first two
  43. * are method-based filters (defined in CController), which refer to filtering methods in the controller class;
  44. * while the last refers to a object-based filter whose class is 'system.web.widgets.COutputCache' and
  45. * the 'duration' property is initialized as 300 (s).
  46. *
  47. * For method-based filters, a method named 'filterXYZ($filterChain)' in the controller class
  48. * will be executed, where 'XYZ' stands for the filter name as specified in {@link filters()}.
  49. * Note, inside the filter method, you must call <code>$filterChain->run()</code> if the action should
  50. * be executed. Otherwise, the filtering process would stop at this filter.
  51. *
  52. * Filters can be specified so that they are executed only when running certain actions.
  53. * For method-based filters, this is done by using '+' and '-' operators in the filter specification.
  54. * The '+' operator means the filter runs only when the specified actions are requested;
  55. * while the '-' operator means the filter runs only when the requested action is not among those actions.
  56. * For object-based filters, the '+' and '-' operators are following the class name.
  57. *
  58. * @author Qiang Xue <qiang.xue@gmail.com>
  59. * @version $Id: CController.php 3307 2011-06-23 15:58:37Z qiang.xue $
  60. * @package system.web
  61. * @since 1.0
  62. */
  63. class CController extends CBaseController
  64. {
  65. /**
  66. * Name of the hidden field storing persistent page states.
  67. */
  68. const STATE_INPUT_NAME='YII_PAGE_STATE';
  69. /**
  70. * @var mixed the name of the layout to be applied to this controller's views.
  71. * Defaults to null, meaning the {@link CWebApplication::layout application layout}
  72. * is used. If it is false, no layout will be applied.
  73. * Since version 1.0.3, the {@link CWebModule::layout module layout} will be used
  74. * if the controller belongs to a module and this layout property is null.
  75. */
  76. public $layout;
  77. /**
  78. * @var string the name of the default action. Defaults to 'index'.
  79. */
  80. public $defaultAction='index';
  81. private $_id;
  82. private $_action;
  83. private $_pageTitle;
  84. private $_cachingStack;
  85. private $_clips;
  86. private $_dynamicOutput;
  87. private $_pageStates;
  88. private $_module;
  89. /**
  90. * @param string $id id of this controller
  91. * @param CWebModule $module the module that this controller belongs to. This parameter
  92. * has been available since version 1.0.3.
  93. */
  94. public function __construct($id,$module=null)
  95. {
  96. $this->_id=$id;
  97. $this->_module=$module;
  98. $this->attachBehaviors($this->behaviors());
  99. }
  100. /**
  101. * Initializes the controller.
  102. * This method is called by the application before the controller starts to execute.
  103. * You may override this method to perform the needed initialization for the controller.
  104. * @since 1.0.1
  105. */
  106. public function init()
  107. {
  108. }
  109. /**
  110. * Returns the filter configurations.
  111. *
  112. * By overriding this method, child classes can specify filters to be applied to actions.
  113. *
  114. * This method returns an array of filter specifications. Each array element specify a single filter.
  115. *
  116. * For a method-based filter (called inline filter), it is specified as 'FilterName[ +|- Action1, Action2, ...]',
  117. * where the '+' ('-') operators describe which actions should be (should not be) applied with the filter.
  118. *
  119. * For a class-based filter, it is specified as an array like the following:
  120. * <pre>
  121. * array(
  122. * 'FilterClass[ +|- Action1, Action2, ...]',
  123. * 'name1'=>'value1',
  124. * 'name2'=>'value2',
  125. * ...
  126. * )
  127. * </pre>
  128. * where the name-value pairs will be used to initialize the properties of the filter.
  129. *
  130. * Note, in order to inherit filters defined in the parent class, a child class needs to
  131. * merge the parent filters with child filters using functions like array_merge().
  132. *
  133. * @return array a list of filter configurations.
  134. * @see CFilter
  135. */
  136. public function filters()
  137. {
  138. return array();
  139. }
  140. /**
  141. * Returns a list of external action classes.
  142. * Array keys are action IDs, and array values are the corresponding
  143. * action class in dot syntax (e.g. 'edit'=>'application.controllers.article.EditArticle')
  144. * or arrays representing the configuration of the actions, such as the following,
  145. * <pre>
  146. * return array(
  147. * 'action1'=>'path.to.Action1Class',
  148. * 'action2'=>array(
  149. * 'class'=>'path.to.Action2Class',
  150. * 'property1'=>'value1',
  151. * 'property2'=>'value2',
  152. * ),
  153. * );
  154. * </pre>
  155. * Derived classes may override this method to declare external actions.
  156. *
  157. * Note, in order to inherit actions defined in the parent class, a child class needs to
  158. * merge the parent actions with child actions using functions like array_merge().
  159. *
  160. * Since version 1.0.1, you may import actions from an action provider
  161. * (such as a widget, see {@link CWidget::actions}), like the following:
  162. * <pre>
  163. * return array(
  164. * ...other actions...
  165. * // import actions declared in ProviderClass::actions()
  166. * // the action IDs will be prefixed with 'pro.'
  167. * 'pro.'=>'path.to.ProviderClass',
  168. * // similar as above except that the imported actions are
  169. * // configured with the specified initial property values
  170. * 'pro2.'=>array(
  171. * 'class'=>'path.to.ProviderClass',
  172. * 'action1'=>array(
  173. * 'property1'=>'value1',
  174. * ),
  175. * 'action2'=>array(
  176. * 'property2'=>'value2',
  177. * ),
  178. * ),
  179. * )
  180. * </pre>
  181. *
  182. * In the above, we differentiate action providers from other action
  183. * declarations by the array keys. For action providers, the array keys
  184. * must contain a dot. As a result, an action ID 'pro2.action1' will
  185. * be resolved as the 'action1' action declared in the 'ProviderClass'.
  186. *
  187. * @return array list of external action classes
  188. * @see createAction
  189. */
  190. public function actions()
  191. {
  192. return array();
  193. }
  194. /**
  195. * Returns a list of behaviors that this controller should behave as.
  196. * The return value should be an array of behavior configurations indexed by
  197. * behavior names. Each behavior configuration can be either a string specifying
  198. * the behavior class or an array of the following structure:
  199. * <pre>
  200. * 'behaviorName'=>array(
  201. * 'class'=>'path.to.BehaviorClass',
  202. * 'property1'=>'value1',
  203. * 'property2'=>'value2',
  204. * )
  205. * </pre>
  206. *
  207. * Note, the behavior classes must implement {@link IBehavior} or extend from
  208. * {@link CBehavior}. Behaviors declared in this method will be attached
  209. * to the controller when it is instantiated.
  210. *
  211. * For more details about behaviors, see {@link CComponent}.
  212. * @return array the behavior configurations (behavior name=>behavior configuration)
  213. * @since 1.0.6
  214. */
  215. public function behaviors()
  216. {
  217. return array();
  218. }
  219. /**
  220. * Returns the access rules for this controller.
  221. * Override this method if you use the {@link filterAccessControl accessControl} filter.
  222. * @return array list of access rules. See {@link CAccessControlFilter} for details about rule specification.
  223. */
  224. public function accessRules()
  225. {
  226. return array();
  227. }
  228. /**
  229. * Runs the named action.
  230. * Filters specified via {@link filters()} will be applied.
  231. * @param string $actionID action ID
  232. * @throws CHttpException if the action does not exist or the action name is not proper.
  233. * @see filters
  234. * @see createAction
  235. * @see runAction
  236. */
  237. public function run($actionID)
  238. {
  239. if(($action=$this->createAction($actionID))!==null)
  240. {
  241. if(($parent=$this->getModule())===null)
  242. $parent=Yii::app();
  243. if($parent->beforeControllerAction($this,$action))
  244. {
  245. $this->runActionWithFilters($action,$this->filters());
  246. $parent->afterControllerAction($this,$action);
  247. }
  248. }
  249. else
  250. $this->missingAction($actionID);
  251. }
  252. /**
  253. * Runs an action with the specified filters.
  254. * A filter chain will be created based on the specified filters
  255. * and the action will be executed then.
  256. * @param CAction $action the action to be executed.
  257. * @param array $filters list of filters to be applied to the action.
  258. * @see filters
  259. * @see createAction
  260. * @see runAction
  261. */
  262. public function runActionWithFilters($action,$filters)
  263. {
  264. if(empty($filters))
  265. $this->runAction($action);
  266. else
  267. {
  268. $priorAction=$this->_action;
  269. $this->_action=$action;
  270. CFilterChain::create($this,$action,$filters)->run();
  271. $this->_action=$priorAction;
  272. }
  273. }
  274. /**
  275. * Runs the action after passing through all filters.
  276. * This method is invoked by {@link runActionWithFilters} after all possible filters have been executed
  277. * and the action starts to run.
  278. * @param CAction $action action to run
  279. */
  280. public function runAction($action)
  281. {
  282. $priorAction=$this->_action;
  283. $this->_action=$action;
  284. if($this->beforeAction($action))
  285. {
  286. if($action->runWithParams($this->getActionParams())===false)
  287. $this->invalidActionParams($action);
  288. else
  289. $this->afterAction($action);
  290. }
  291. $this->_action=$priorAction;
  292. }
  293. /**
  294. * Returns the request parameters that will be used for action parameter binding.
  295. * By default, this method will return $_GET. You may override this method if you
  296. * want to use other request parameters (e.g. $_GET+$_POST).
  297. * @return array the request parameters to be used for action parameter binding
  298. * @since 1.1.7
  299. */
  300. public function getActionParams()
  301. {
  302. return $_GET;
  303. }
  304. /**
  305. * This method is invoked when the request parameters do not satisfy the requirement of the specified action.
  306. * The default implementation will throw a 400 HTTP exception.
  307. * @param CAction $action the action being executed
  308. * @since 1.1.7
  309. */
  310. public function invalidActionParams($action)
  311. {
  312. throw new CHttpException(400,Yii::t('yii','Your request is invalid.'));
  313. }
  314. /**
  315. * Postprocesses the output generated by {@link render()}.
  316. * This method is invoked at the end of {@link render()} and {@link renderText()}.
  317. * If there are registered client scripts, this method will insert them into the output
  318. * at appropriate places. If there are dynamic contents, they will also be inserted.
  319. * This method may also save the persistent page states in hidden fields of
  320. * stateful forms in the page.
  321. * @param string $output the output generated by the current action
  322. * @return string the output that has been processed.
  323. */
  324. public function processOutput($output)
  325. {
  326. Yii::app()->getClientScript()->render($output);
  327. // if using page caching, we should delay dynamic output replacement
  328. if($this->_dynamicOutput!==null && $this->isCachingStackEmpty())
  329. {
  330. $output=$this->processDynamicOutput($output);
  331. $this->_dynamicOutput=null;
  332. }
  333. if($this->_pageStates===null)
  334. $this->_pageStates=$this->loadPageStates();
  335. if(!empty($this->_pageStates))
  336. $this->savePageStates($this->_pageStates,$output);
  337. return $output;
  338. }
  339. /**
  340. * Postprocesses the dynamic output.
  341. * This method is internally used. Do not call this method directly.
  342. * @param string $output output to be processed
  343. * @return string the processed output
  344. * @since 1.0.4
  345. */
  346. public function processDynamicOutput($output)
  347. {
  348. if($this->_dynamicOutput)
  349. {
  350. $output=preg_replace_callback('/<###dynamic-(\d+)###>/',array($this,'replaceDynamicOutput'),$output);
  351. }
  352. return $output;
  353. }
  354. /**
  355. * Replaces the dynamic content placeholders with actual content.
  356. * This is a callback function used internally.
  357. * @param array $matches matches
  358. * @return string the replacement
  359. * @see processOutput
  360. */
  361. protected function replaceDynamicOutput($matches)
  362. {
  363. $content=$matches[0];
  364. if(isset($this->_dynamicOutput[$matches[1]]))
  365. {
  366. $content=$this->_dynamicOutput[$matches[1]];
  367. $this->_dynamicOutput[$matches[1]]=null;
  368. }
  369. return $content;
  370. }
  371. /**
  372. * Creates the action instance based on the action name.
  373. * The action can be either an inline action or an object.
  374. * The latter is created by looking up the action map specified in {@link actions}.
  375. * @param string $actionID ID of the action. If empty, the {@link defaultAction default action} will be used.
  376. * @return CAction the action instance, null if the action does not exist.
  377. * @see actions
  378. */
  379. public function createAction($actionID)
  380. {
  381. if($actionID==='')
  382. $actionID=$this->defaultAction;
  383. if(method_exists($this,'action'.$actionID) && strcasecmp($actionID,'s')) // we have actions method
  384. return new CInlineAction($this,$actionID);
  385. else
  386. {
  387. $action=$this->createActionFromMap($this->actions(),$actionID,$actionID);
  388. if($action!==null && !method_exists($action,'run'))
  389. throw new CException(Yii::t('yii', 'Action class {class} must implement the "run" method.', array('{class}'=>get_class($action))));
  390. return $action;
  391. }
  392. }
  393. /**
  394. * Creates the action instance based on the action map.
  395. * This method will check to see if the action ID appears in the given
  396. * action map. If so, the corresponding configuration will be used to
  397. * create the action instance.
  398. * @param array $actionMap the action map
  399. * @param string $actionID the action ID that has its prefix stripped off
  400. * @param string $requestActionID the originally requested action ID
  401. * @param array $config the action configuration that should be applied on top of the configuration specified in the map
  402. * @return CAction the action instance, null if the action does not exist.
  403. * @since 1.0.1
  404. */
  405. protected function createActionFromMap($actionMap,$actionID,$requestActionID,$config=array())
  406. {
  407. if(($pos=strpos($actionID,'.'))===false && isset($actionMap[$actionID]))
  408. {
  409. $baseConfig=is_array($actionMap[$actionID]) ? $actionMap[$actionID] : array('class'=>$actionMap[$actionID]);
  410. return Yii::createComponent(empty($config)?$baseConfig:array_merge($baseConfig,$config),$this,$requestActionID);
  411. }
  412. else if($pos===false)
  413. return null;
  414. // the action is defined in a provider
  415. $prefix=substr($actionID,0,$pos+1);
  416. if(!isset($actionMap[$prefix]))
  417. return null;
  418. $actionID=(string)substr($actionID,$pos+1);
  419. $provider=$actionMap[$prefix];
  420. if(is_string($provider))
  421. $providerType=$provider;
  422. else if(is_array($provider) && isset($provider['class']))
  423. {
  424. $providerType=$provider['class'];
  425. if(isset($provider[$actionID]))
  426. {
  427. if(is_string($provider[$actionID]))
  428. $config=array_merge(array('class'=>$provider[$actionID]),$config);
  429. else
  430. $config=array_merge($provider[$actionID],$config);
  431. }
  432. }
  433. else
  434. throw new CException(Yii::t('yii','Object configuration must be an array containing a "class" element.'));
  435. $class=Yii::import($providerType,true);
  436. $map=call_user_func(array($class,'actions'));
  437. return $this->createActionFromMap($map,$actionID,$requestActionID,$config);
  438. }
  439. /**
  440. * Handles the request whose action is not recognized.
  441. * This method is invoked when the controller cannot find the requested action.
  442. * The default implementation simply throws an exception.
  443. * @param string $actionID the missing action name
  444. * @throws CHttpException whenever this method is invoked
  445. */
  446. public function missingAction($actionID)
  447. {
  448. throw new CHttpException(404,Yii::t('yii','The system is unable to find the requested action "{action}".',
  449. array('{action}'=>$actionID==''?$this->defaultAction:$actionID)));
  450. }
  451. /**
  452. * @return CAction the action currently being executed, null if no active action.
  453. */
  454. public function getAction()
  455. {
  456. return $this->_action;
  457. }
  458. /**
  459. * @param CAction $value the action currently being executed.
  460. */
  461. public function setAction($value)
  462. {
  463. $this->_action=$value;
  464. }
  465. /**
  466. * @return string ID of the controller
  467. */
  468. public function getId()
  469. {
  470. return $this->_id;
  471. }
  472. /**
  473. * @return string the controller ID that is prefixed with the module ID (if any).
  474. * @since 1.0.3
  475. */
  476. public function getUniqueId()
  477. {
  478. return $this->_module ? $this->_module->getId().'/'.$this->_id : $this->_id;
  479. }
  480. /**
  481. * @return string the route (module ID, controller ID and action ID) of the current request.
  482. * @since 1.1.0
  483. */
  484. public function getRoute()
  485. {
  486. if(($action=$this->getAction())!==null)
  487. return $this->getUniqueId().'/'.$action->getId();
  488. else
  489. return $this->getUniqueId();
  490. }
  491. /**
  492. * @return CWebModule the module that this controller belongs to. It returns null
  493. * if the controller does not belong to any module
  494. * @since 1.0.3
  495. */
  496. public function getModule()
  497. {
  498. return $this->_module;
  499. }
  500. /**
  501. * Returns the directory containing view files for this controller.
  502. * The default implementation returns 'protected/views/ControllerID'.
  503. * Child classes may override this method to use customized view path.
  504. * If the controller belongs to a module (since version 1.0.3), the default view path
  505. * is the {@link CWebModule::getViewPath module view path} appended with the controller ID.
  506. * @return string the directory containing the view files for this controller. Defaults to 'protected/views/ControllerID'.
  507. */
  508. public function getViewPath()
  509. {
  510. if(($module=$this->getModule())===null)
  511. $module=Yii::app();
  512. return $module->getViewPath().DIRECTORY_SEPARATOR.$this->getId();
  513. }
  514. /**
  515. * Looks for the view file according to the given view name.
  516. *
  517. * When a theme is currently active, this method will call {@link CTheme::getViewFile} to determine
  518. * which view file should be returned.
  519. *
  520. * Otherwise, this method will return the corresponding view file based on the following criteria:
  521. * <ul>
  522. * <li>absolute view within a module: the view name starts with a single slash '/'.
  523. * In this case, the view will be searched for under the currently active module's view path.
  524. * If there is no active module, the view will be searched for under the application's view path.</li>
  525. * <li>absolute view within the application: the view name starts with double slashes '//'.
  526. * In this case, the view will be searched for under the application's view path.
  527. * This syntax has been available since version 1.1.3.</li>
  528. * <li>aliased view: the view name contains dots and refers to a path alias.
  529. * The view file is determined by calling {@link YiiBase::getPathOfAlias()}. Note that aliased views
  530. * cannot be themed because they can refer to a view file located at arbitrary places.</li>
  531. * <li>relative view: otherwise. Relative views will be searched for under the currently active
  532. * controller's view path.</li>
  533. * </ul>
  534. *
  535. * After the view file is identified, this method may further call {@link CApplication::findLocalizedFile}
  536. * to find its localized version if internationalization is needed.
  537. *
  538. * @param string $viewName view name
  539. * @return string the view file path, false if the view file does not exist
  540. * @see resolveViewFile
  541. * @see CApplication::findLocalizedFile
  542. */
  543. public function getViewFile($viewName)
  544. {
  545. if(($theme=Yii::app()->getTheme())!==null && ($viewFile=$theme->getViewFile($this,$viewName))!==false)
  546. return $viewFile;
  547. $moduleViewPath=$basePath=Yii::app()->getViewPath();
  548. if(($module=$this->getModule())!==null)
  549. $moduleViewPath=$module->getViewPath();
  550. return $this->resolveViewFile($viewName,$this->getViewPath(),$basePath,$moduleViewPath);
  551. }
  552. /**
  553. * Looks for the layout view script based on the layout name.
  554. *
  555. * The layout name can be specified in one of the following ways:
  556. *
  557. * <ul>
  558. * <li>layout is false: returns false, meaning no layout.</li>
  559. * <li>layout is null: the currently active module's layout will be used. If there is no active module,
  560. * the application's layout will be used.</li>
  561. * <li>a regular view name.</li>
  562. * </ul>
  563. *
  564. * The resolution of the view file based on the layout view is similar to that in {@link getViewFile}.
  565. * In particular, the following rules are followed:
  566. *
  567. * Otherwise, this method will return the corresponding view file based on the following criteria:
  568. * <ul>
  569. * <li>When a theme is currently active, this method will call {@link CTheme::getLayoutFile} to determine
  570. * which view file should be returned.</li>
  571. * <li>absolute view within a module: the view name starts with a single slash '/'.
  572. * In this case, the view will be searched for under the currently active module's view path.
  573. * If there is no active module, the view will be searched for under the application's view path.</li>
  574. * <li>absolute view within the application: the view name starts with double slashes '//'.
  575. * In this case, the view will be searched for under the application's view path.
  576. * This syntax has been available since version 1.1.3.</li>
  577. * <li>aliased view: the view name contains dots and refers to a path alias.
  578. * The view file is determined by calling {@link YiiBase::getPathOfAlias()}. Note that aliased views
  579. * cannot be themed because they can refer to a view file located at arbitrary places.</li>
  580. * <li>relative view: otherwise. Relative views will be searched for under the currently active
  581. * module's layout path. In case when there is no active module, the view will be searched for
  582. * under the application's layout path.</li>
  583. * </ul>
  584. *
  585. * After the view file is identified, this method may further call {@link CApplication::findLocalizedFile}
  586. * to find its localized version if internationalization is needed.
  587. *
  588. * @param mixed $layoutName layout name
  589. * @return string the view file for the layout. False if the view file cannot be found
  590. */
  591. public function getLayoutFile($layoutName)
  592. {
  593. if($layoutName===false)
  594. return false;
  595. if(($theme=Yii::app()->getTheme())!==null && ($layoutFile=$theme->getLayoutFile($this,$layoutName))!==false)
  596. return $layoutFile;
  597. if(empty($layoutName))
  598. {
  599. $module=$this->getModule();
  600. while($module!==null)
  601. {
  602. if($module->layout===false)
  603. return false;
  604. if(!empty($module->layout))
  605. break;
  606. $module=$module->getParentModule();
  607. }
  608. if($module===null)
  609. $module=Yii::app();
  610. $layoutName=$module->layout;
  611. }
  612. else if(($module=$this->getModule())===null)
  613. $module=Yii::app();
  614. return $this->resolveViewFile($layoutName,$module->getLayoutPath(),Yii::app()->getViewPath(),$module->getViewPath());
  615. }
  616. /**
  617. * Finds a view file based on its name.
  618. * The view name can be in one of the following formats:
  619. * <ul>
  620. * <li>absolute view within a module: the view name starts with a single slash '/'.
  621. * In this case, the view will be searched for under the currently active module's view path.
  622. * If there is no active module, the view will be searched for under the application's view path.</li>
  623. * <li>absolute view within the application: the view name starts with double slashes '//'.
  624. * In this case, the view will be searched for under the application's view path.
  625. * This syntax has been available since version 1.1.3.</li>
  626. * <li>aliased view: the view name contains dots and refers to a path alias.
  627. * The view file is determined by calling {@link YiiBase::getPathOfAlias()}. Note that aliased views
  628. * cannot be themed because they can refer to a view file located at arbitrary places.</li>
  629. * <li>relative view: otherwise. Relative views will be searched for under the currently active
  630. * controller's view path.</li>
  631. * </ul>
  632. * For absolute view and relative view, the corresponding view file is a PHP file
  633. * whose name is the same as the view name. The file is located under a specified directory.
  634. * This method will call {@link CApplication::findLocalizedFile} to search for a localized file, if any.
  635. * @param string $viewName the view name
  636. * @param string $viewPath the directory that is used to search for a relative view name
  637. * @param string $basePath the directory that is used to search for an absolute view name under the application
  638. * @param string $moduleViewPath the directory that is used to search for an absolute view name under the current module.
  639. * If this is not set, the application base view path will be used.
  640. * @return mixed the view file path. False if the view file does not exist.
  641. * @since 1.0.3
  642. */
  643. public function resolveViewFile($viewName,$viewPath,$basePath,$moduleViewPath=null)
  644. {
  645. if(empty($viewName))
  646. return false;
  647. if($moduleViewPath===null)
  648. $moduleViewPath=$basePath;
  649. if(($renderer=Yii::app()->getViewRenderer())!==null)
  650. $extension=$renderer->fileExtension;
  651. else
  652. $extension='.php';
  653. if($viewName[0]==='/')
  654. {
  655. if(strncmp($viewName,'//',2)===0)
  656. $viewFile=$basePath.$viewName;
  657. else
  658. $viewFile=$moduleViewPath.$viewName;
  659. }
  660. else if(strpos($viewName,'.'))
  661. $viewFile=Yii::getPathOfAlias($viewName);
  662. else
  663. $viewFile=$viewPath.DIRECTORY_SEPARATOR.$viewName;
  664. if(is_file($viewFile.$extension))
  665. return Yii::app()->findLocalizedFile($viewFile.$extension);
  666. else if($extension!=='.php' && is_file($viewFile.'.php'))
  667. return Yii::app()->findLocalizedFile($viewFile.'.php');
  668. else
  669. return false;
  670. }
  671. /**
  672. * Returns the list of clips.
  673. * A clip is a named piece of rendering result that can be
  674. * inserted at different places.
  675. * @return CMap the list of clips
  676. * @see CClipWidget
  677. */
  678. public function getClips()
  679. {
  680. if($this->_clips!==null)
  681. return $this->_clips;
  682. else
  683. return $this->_clips=new CMap;
  684. }
  685. /**
  686. * Processes the request using another controller action.
  687. * This is like {@link redirect}, but the user browser's URL remains unchanged.
  688. * In most cases, you should call {@link redirect} instead of this method.
  689. * @param string $route the route of the new controller action. This can be an action ID, or a complete route
  690. * with module ID (optional in the current module), controller ID and action ID. If the former, the action is assumed
  691. * to be located within the current controller.
  692. * @param boolean $exit whether to end the application after this call. Defaults to true.
  693. * @since 1.1.0
  694. */
  695. public function forward($route,$exit=true)
  696. {
  697. if(strpos($route,'/')===false)
  698. $this->run($route);
  699. else
  700. {
  701. if($route[0]!=='/' && ($module=$this->getModule())!==null)
  702. $route=$module->getId().'/'.$route;
  703. Yii::app()->runController($route);
  704. }
  705. if($exit)
  706. Yii::app()->end();
  707. }
  708. /**
  709. * Renders a view with a layout.
  710. *
  711. * This method first calls {@link renderPartial} to render the view (called content view).
  712. * It then renders the layout view which may embed the content view at appropriate place.
  713. * In the layout view, the content view rendering result can be accessed via variable
  714. * <code>$content</code>. At the end, it calls {@link processOutput} to insert scripts
  715. * and dynamic contents if they are available.
  716. *
  717. * By default, the layout view script is "protected/views/layouts/main.php".
  718. * This may be customized by changing {@link layout}.
  719. *
  720. * @param string $view name of the view to be rendered. See {@link getViewFile} for details
  721. * about how the view script is resolved.
  722. * @param array $data data to be extracted into PHP variables and made available to the view script
  723. * @param boolean $return whether the rendering result should be returned instead of being displayed to end users.
  724. * @return string the rendering result. Null if the rendering result is not required.
  725. * @see renderPartial
  726. * @see getLayoutFile
  727. */
  728. public function render($view,$data=null,$return=false)
  729. {
  730. if($this->beforeRender($view))
  731. {
  732. $output=$this->renderPartial($view,$data,true);
  733. if(($layoutFile=$this->getLayoutFile($this->layout))!==false)
  734. $output=$this->renderFile($layoutFile,array('content'=>$output),true);
  735. $this->afterRender($view,$output);
  736. $output=$this->processOutput($output);
  737. if($return)
  738. return $output;
  739. else
  740. echo $output;
  741. }
  742. }
  743. /**
  744. * This method is invoked at the beginning of {@link render()}.
  745. * You may override this method to do some preprocessing when rendering a view.
  746. * @param string $view the view to be rendered
  747. * @return boolean whether the view should be rendered.
  748. * @since 1.1.5
  749. */
  750. protected function beforeRender($view)
  751. {
  752. return true;
  753. }
  754. /**
  755. * This method is invoked after the specified is rendered by calling {@link render()}.
  756. * Note that this method is invoked BEFORE {@link processOutput()}.
  757. * You may override this method to do some postprocessing for the view rendering.
  758. * @param string $view the view that has been rendered
  759. * @param string $output the rendering result of the view. Note that this parameter is passed
  760. * as a reference. That means you can modify it within this method.
  761. * @since 1.1.5
  762. */
  763. protected function afterRender($view, &$output)
  764. {
  765. }
  766. /**
  767. * Renders a static text string.
  768. * The string will be inserted in the current controller layout and returned back.
  769. * @param string $text the static text string
  770. * @param boolean $return whether the rendering result should be returned instead of being displayed to end users.
  771. * @return string the rendering result. Null if the rendering result is not required.
  772. * @see getLayoutFile
  773. */
  774. public function renderText($text,$return=false)
  775. {
  776. if(($layoutFile=$this->getLayoutFile($this->layout))!==false)
  777. $text=$this->renderFile($layoutFile,array('content'=>$text),true);
  778. $text=$this->processOutput($text);
  779. if($return)
  780. return $text;
  781. else
  782. echo $text;
  783. }
  784. /**
  785. * Renders a view.
  786. *
  787. * The named view refers to a PHP script (resolved via {@link getViewFile})
  788. * that is included by this method. If $data is an associative array,
  789. * it will be extracted as PHP variables and made available to the script.
  790. *
  791. * This method differs from {@link render()} in that it does not
  792. * apply a layout to the rendered result. It is thus mostly used
  793. * in rendering a partial view, or an AJAX response.
  794. *
  795. * @param string $view name of the view to be rendered. See {@link getViewFile} for details
  796. * about how the view script is resolved.
  797. * @param array $data data to be extracted into PHP variables and made available to the view script
  798. * @param boolean $return whether the rendering result should be returned instead of being displayed to end users
  799. * @param boolean $processOutput whether the rendering result should be postprocessed using {@link processOutput}.
  800. * @return string the rendering result. Null if the rendering result is not required.
  801. * @throws CException if the view does not exist
  802. * @see getViewFile
  803. * @see processOutput
  804. * @see render
  805. */
  806. public function renderPartial($view,$data=null,$return=false,$processOutput=false)
  807. {
  808. if(($viewFile=$this->getViewFile($view))!==false)
  809. {
  810. $output=$this->renderFile($viewFile,$data,true);
  811. if($processOutput)
  812. $output=$this->processOutput($output);
  813. if($return)
  814. return $output;
  815. else
  816. echo $output;
  817. }
  818. else
  819. throw new CException(Yii::t('yii','{controller} cannot find the requested view "{view}".',
  820. array('{controller}'=>get_class($this), '{view}'=>$view)));
  821. }
  822. /**
  823. * Renders a named clip with the supplied parameters.
  824. * This is similar to directly accessing the {@link clips} property.
  825. * The main difference is that it can take an array of named parameters
  826. * which will replace the corresponding placeholders in the clip.
  827. * @param string $name the name of the clip
  828. * @param array $params an array of named parameters (name=>value) that should replace
  829. * their corresponding placeholders in the clip
  830. * @param boolean $return whether to return the clip content or echo it.
  831. * @return mixed either the clip content or null
  832. * @since 1.1.8
  833. */
  834. public function renderClip($name,$params=array(),$return=false)
  835. {
  836. $text=isset($this->clips[$name]) ? strtr($this->clips[$name], $params) : '';
  837. if($return)
  838. return $text;
  839. else
  840. echo $text;
  841. }
  842. /**
  843. * Renders dynamic content returned by the specified callback.
  844. * This method is used together with {@link COutputCache}. Dynamic contents
  845. * will always show as their latest state even if the content surrounding them is being cached.
  846. * This is especially useful when caching pages that are mostly static but contain some small
  847. * dynamic regions, such as username or current time.
  848. * We can use this method to render these dynamic regions to ensure they are always up-to-date.
  849. *
  850. * The first parameter to this method should be a valid PHP callback, while the rest parameters
  851. * will be passed to the callback.
  852. *
  853. * Note, the callback and its parameter values will be serialized and saved in cache.
  854. * Make sure they are serializable.
  855. *
  856. * @param callback $callback a PHP callback which returns the needed dynamic content.
  857. * When the callback is specified as a string, it will be first assumed to be a method of the current
  858. * controller class. If the method does not exist, it is assumed to be a global PHP function.
  859. * Note, the callback should return the dynamic content instead of echoing it.
  860. */
  861. public function renderDynamic($callback)
  862. {
  863. $n=count($this->_dynamicOutput);
  864. echo "<###dynamic-$n###>";
  865. $params=func_get_args();
  866. array_shift($params);
  867. $this->renderDynamicInternal($callback,$params);
  868. }
  869. /**
  870. * This method is internally used.
  871. * @param callback $callback a PHP callback which returns the needed dynamic content.
  872. * @param array $params parameters passed to the PHP callback
  873. * @see renderDynamic
  874. */
  875. public function renderDynamicInternal($callback,$params)
  876. {
  877. $this->recordCachingAction('','renderDynamicInternal',array($callback,$params));
  878. if(is_string($callback) && method_exists($this,$callback))
  879. $callback=array($this,$callback);
  880. $this->_dynamicOutput[]=call_user_func_array($callback,$params);
  881. }
  882. /**
  883. * Creates a relative URL for the specified action defined in this controller.
  884. * @param string $route the URL route. This should be in the format of 'ControllerID/ActionID'.
  885. * If the ControllerID is not present, the current controller ID will be prefixed to the route.
  886. * If the route is empty, it is assumed to be the current action.
  887. * Since version 1.0.3, if the controller belongs to a module, the {@link CWebModule::getId module ID}
  888. * will be prefixed to the route. (If you do not want the module ID prefix, the route should start with a slash '/'.)
  889. * @param array $params additional GET parameters (name=>value). Both the name and value will be URL-encoded.
  890. * If the name is '#', the corresponding value will be treated as an anchor
  891. * and will be appended at the end of the URL. This anchor feature has been available since version 1.0.1.
  892. * @param string $ampersand the token separating name-value pairs in the URL.
  893. * @return string the constructed URL
  894. */
  895. public function createUrl($route,$params=array(),$ampersand='&')
  896. {
  897. if($route==='')
  898. $route=$this->getId().'/'.$this->getAction()->getId();
  899. else if(strpos($route,'/')===false)
  900. $route=$this->getId().'/'.$route;
  901. if($route[0]!=='/' && ($module=$this->getModule())!==null)
  902. $route=$module->getId().'/'.$route;
  903. return Yii::app()->createUrl(trim($route,'/'),$params,$ampersand);
  904. }
  905. /**
  906. * Creates an absolute URL for the specified action defined in this controller.
  907. * @param string $route the URL route. This should be in the format of 'ControllerID/ActionID'.
  908. * If the ControllerPath is not present, the current controller ID will be prefixed to the route.
  909. * If the route is empty, it is assumed to be the current action.
  910. * @param array $params additional GET parameters (name=>value). Both the name and value will be URL-encoded.
  911. * @param string $schema schema to use (e.g. http, https). If empty, the schema used for the current request will be used.
  912. * @param string $ampersand the token separating name-value pairs in the URL.
  913. * @return string the constructed URL
  914. */
  915. public function createAbsoluteUrl($route,$params=array(),$schema='',$ampersand='&')
  916. {
  917. $url=$this->createUrl($route,$params,$ampersand);
  918. if(strpos($url,'http')===0)
  919. return $url;
  920. else
  921. return Yii::app()->getRequest()->getHostInfo($schema).$url;
  922. }
  923. /**
  924. * @return string the page title. Defaults to the controller name and the action name.
  925. */
  926. public function getPageTitle()
  927. {
  928. if($this->_pageTitle!==null)
  929. return $this->_pageTitle;
  930. else
  931. {
  932. $name=ucfirst(basename($this->getId()));
  933. if($this->getAction()!==null && strcasecmp($this->getAction()->getId(),$this->defaultAction))
  934. return $this->_pageTitle=Yii::app()->name.' - '.ucfirst($this->getAction()->getId()).' '.$name;
  935. else
  936. return $this->_pageTitle=Yii::app()->name.' - '.$name;
  937. }
  938. }
  939. /**
  940. * @param string $value the page title.
  941. */
  942. public function setPageTitle($value)
  943. {
  944. $this->_pageTitle=$value;
  945. }
  946. /**
  947. * Redirects the browser to the specified URL or route (controller/action).
  948. * @param mixed $url the URL to be redirected to. If the parameter is an array,
  949. * the first element must be a route to a controller action and the rest
  950. * are GET parameters in name-value pairs.
  951. * @param boolean $terminate whether to terminate the current application after calling this method
  952. * @param integer $statusCode the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html}
  953. * for details about HTTP status code. This parameter has been available since version 1.0.4.
  954. */
  955. public function redirect($url,$terminate=true,$statusCode=302)
  956. {
  957. if(is_array($url))
  958. {
  959. $route=isset($url[0]) ? $url[0] : '';
  960. $url=$this->createUrl($route,array_splice($url,1));
  961. }
  962. Yii::app()->getRequest()->redirect($url,$terminate,$statusCode);
  963. }
  964. /**
  965. * Refreshes the current page.
  966. * The effect of this method call is the same as user pressing the
  967. * refresh button on the browser (without post data).
  968. * @param boolean $terminate whether to terminate the current application after calling this method
  969. * @param string $anchor the anchor that should be appended to the redirection URL.
  970. * Defaults to empty. Make sure the anchor starts with '#' if you want to specify it.
  971. * The parameter has been available since version 1.0.7.
  972. **/
  973. public function refresh($terminate=true,$anchor='')
  974. {
  975. $this->redirect(Yii::app()->getRequest()->getUrl().$anchor,$terminate);
  976. }
  977. /**
  978. * Records a method call when an output cache is in effect.
  979. * When the content is served from the output cache, the recorded
  980. * method will be re-invoked.
  981. * @param string $context a property name of the controller. It refers to an object
  982. * whose method is being called. If empty it means the controller itself.
  983. * @param string $method the method name
  984. * @param array $params parameters passed to the method
  985. * @see COutputCache
  986. */
  987. public function recordCachingAction($context,$method,$params)
  988. {
  989. if($this->_cachingStack) // record only when there is an active output cache
  990. {
  991. foreach($this->_cachingStack as $cache)
  992. $cache->recordAction($context,$method,$params);
  993. }
  994. }
  995. /**
  996. * @param boolean $createIfNull whether to create a stack if it does not exist yet. Defaults to true.
  997. * @return CStack stack of {@link COutputCache} objects
  998. */
  999. public function getCachingStack($createIfNull=true)
  1000. {
  1001. if(!$this->_cachingStack)
  1002. $this->_cachingStack=new CStack;
  1003. return $this->_cachingStack;
  1004. }
  1005. /**
  1006. * Returns whether the caching stack is empty.
  1007. * @return boolean whether the caching stack is empty. If not empty, it means currently there are
  1008. * some output cache in effect. Note, the return result of this method may change when it is
  1009. * called in different output regions, depending on the partition of output caches.
  1010. * @since 1.0.5
  1011. */
  1012. public function isCachingStackEmpty()
  1013. {
  1014. return $this->_cachingStack===null || !$this->_cachingStack->getCount();
  1015. }
  1016. /**
  1017. * This method is invoked right before an action is to be executed (after all possible filters.)
  1018. * You may override this method to do last-minute preparation for the action.
  1019. * @param CAction $action the action to be executed.
  1020. * @return boolean whether the action should be executed.
  1021. */
  1022. protected function beforeAction($action)
  1023. {
  1024. return true;
  1025. }
  1026. /**
  1027. * This method is invoked right after an action is executed.
  1028. * You may override this method to do some postprocessing for the action.
  1029. * @param CAction $action the action just executed.
  1030. */
  1031. protected function afterAction($action)
  1032. {
  1033. }
  1034. /**
  1035. * The filter method for 'postOnly' filter.
  1036. * This filter reports an error if the applied action is receiving a non-POST request.
  1037. * @param CFilterChain $filterChain the filter chain that the filter is on.
  1038. * @throws CHttpException if the current request is not a POST request
  1039. */
  1040. public function filterPostOnly($filterChain)
  1041. {
  1042. if(Yii::app()->getRequest()->getIsPostRequest())
  1043. $filterChain->run();
  1044. else
  1045. throw new CHttpException(400,Yii::t('yii','Your request is not valid.'));
  1046. }
  1047. /**
  1048. * The filter method for 'ajaxOnly' filter.
  1049. * This filter reports an error if the applied action is receiving a non-AJAX request.
  1050. * @param CFilterChain $filterChain the filter chain that the filter is on.
  1051. * @throws CHttpException if the current request is not an AJAX request.
  1052. */
  1053. public function filterAjaxOnly($filterChain)
  1054. {
  1055. if(Yii::app()->getRequest()->getIsAjaxRequest())
  1056. $filterChain->run();
  1057. else
  1058. throw new CHttpException(400,Yii::t('yii','Your request is not valid.'));
  1059. }
  1060. /**
  1061. * The filter method for 'accessControl' filter.
  1062. * This filter is a wrapper of {@link CAccessControlFilter}.
  1063. * To use this filter, you must override {@link accessRules} method.
  1064. * @param CFilterChain $filterChain the filter chain that the filter is on.
  1065. */
  1066. public function filterAccessControl($filterChain)
  1067. {
  1068. $filter=new CAccessControlFilter;
  1069. $filter->setRules($this->accessRules());
  1070. $filter->filter($filterChain);
  1071. }
  1072. /**
  1073. * Generates pagination information.
  1074. * This method can be used to generate pagination information given item count
  1075. * and page size. The pagination information can then be passed to {@link CBasePager pagers}
  1076. * for corresponding rendering.
  1077. *
  1078. * Note: this method has been deprecated since version 1.0.1.
  1079. * You should directly use "new CPagination" to create a pagination object.
  1080. *
  1081. * @param integer $itemCount the total item count
  1082. * @param integer $pageSize the page size. See {@link CPagination} for default value.
  1083. * @param string $pageVar the name of the GET variable storing the current page index. See {@link CPagination} for default value.
  1084. * @return CPagination the pagination information
  1085. */
  1086. public function paginate($itemCount,$pageSize=null,$pageVar=null)
  1087. {
  1088. $pages=new CPagination($itemCount);
  1089. if($pageSize!==null)
  1090. $pages->pageSize=$pageSize;
  1091. if($pageVar!==null)
  1092. $pages->pageVar=$pageVar;
  1093. return $pages;
  1094. }
  1095. /**
  1096. * Returns a persistent page state value.
  1097. * A page state is a variable that is persistent across POST requests of the same page.
  1098. * In order to use persistent page states, the form(s) must be stateful
  1099. * which are generated using {@link CHtml::statefulForm}.
  1100. * @param string $name the state name
  1101. * @param mixed $defaultValue the value to be returned if the named state is not found
  1102. * @return mixed the page state value
  1103. * @see setPageState
  1104. * @see CHtml::statefulForm
  1105. */
  1106. public function getPageState($name,$defaultValue=null)
  1107. {
  1108. if($this->_pageStates===null)
  1109. $this->_pageStates=$this->loadPageStates();
  1110. return isset($this->_pageStates[$name])?$this->_pageStates[$name]:$defaultValue;
  1111. }
  1112. /**
  1113. * Saves a persistent page state value.
  1114. * A page state is a variable that is persistent across POST requests of the same page.
  1115. * In order to use persistent page states, the form(s) must be stateful
  1116. * which are generated using {@link CHtml::statefulForm}.
  1117. * @param string $name the state name
  1118. * @param mixed $value the page state value
  1119. * @param mixed $defaultValue the default page state value. If this is the same as
  1120. * the given value, the state will be removed from persistent storage.
  1121. * @see getPageState
  1122. * @see CHtml::statefulForm
  1123. */
  1124. public function setPageState($name,$value,$defaultValue=null)
  1125. {
  1126. if($this->_pageStates===null)
  1127. $this->_pageStates=$this->loadPageStates();
  1128. if($value===$defaultValue)
  1129. unset($this->_pageStates[$name]);
  1130. else
  1131. $this->_pageStates[$name]=$value;
  1132. $params=func_get_args();
  1133. $this->recordCachingAction('','setPageState',$params);
  1134. }
  1135. /**
  1136. * Removes all page states.
  1137. */
  1138. public function clearPageStates()
  1139. {
  1140. $this->_pageStates=array();
  1141. }
  1142. /**
  1143. * Loads page states from a hidden input.
  1144. * @return array the loaded page states
  1145. */
  1146. protected function loadPageStates()
  1147. {
  1148. if(!empty($_POST[self::STATE_INPUT_NAME]))
  1149. {
  1150. if(($data=base64_decode($_POST[self::STATE_INPUT_NAME]))!==false)
  1151. {
  1152. if(extension_loaded('zlib'))
  1153. $data=@gzuncompress($data);
  1154. if(($data=Yii::app()->getSecurityManager()->validateData($data))!==false)
  1155. return unserialize($data);
  1156. }
  1157. }
  1158. return array();
  1159. }
  1160. /**
  1161. * Saves page states as a base64 string.
  1162. * @param array $states the states to be saved.
  1163. * @param string $output the output to be modified. Note, this is passed by reference.
  1164. */
  1165. protected function savePageStates($states,&$output)
  1166. {
  1167. $data=Yii::app()->getSecurityManager()->hashData(serialize($states));
  1168. if(extension_loaded('zlib'))
  1169. $data=gzcompress($data);
  1170. $value=base64_encode($data);
  1171. $output=str_replace(CHtml::pageStateField(''),CHtml::pageStateField($value),$output);
  1172. }
  1173. }