PageRenderTime 70ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 0ms

/demo/yii/web/CController.php

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