PageRenderTime 58ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/js/yii/web/CController.js

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