1: <?php
2: /*****************************************************************************************
3: * X2Engine Open Source Edition is a customer relationship management program developed by
4: * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
5: *
6: * This program is free software; you can redistribute it and/or modify it under
7: * the terms of the GNU Affero General Public License version 3 as published by the
8: * Free Software Foundation with the addition of the following permission added
9: * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10: * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
11: * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12: *
13: * This program is distributed in the hope that it will be useful, but WITHOUT
14: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15: * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16: * details.
17: *
18: * You should have received a copy of the GNU Affero General Public License along with
19: * this program; if not, see http://www.gnu.org/licenses or write to the Free
20: * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21: * 02110-1301 USA.
22: *
23: * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
24: * California 95067, USA. or at email address [email protected].
25: *
26: * The interactive user interfaces in modified source and object code versions
27: * of this program must display Appropriate Legal Notices, as required under
28: * Section 5 of the GNU Affero General Public License version 3.
29: *
30: * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31: * these Appropriate Legal Notices must retain the display of the "Powered by
32: * X2Engine" logo. If the display of the logo is not reasonably feasible for
33: * technical reasons, the Appropriate Legal Notices must display the words
34: * "Powered by X2Engine".
35: *****************************************************************************************/
36:
37: /**
38: * Base generic controller class
39: *
40: * @package application.controllers
41: */
42: abstract class X2Controller extends CController {
43:
44: /**
45: * Renders a view file.
46: * Overrides {@link CBaseController::renderFile} to check if the requested view
47: * has a version in /custom, and uses that if it exists.
48: *
49: * @param string $viewFile view file path
50: * @param array $data data to be extracted and made available to the view
51: * @param boolean $return whether the rendering result should be returned instead of being echoed
52: * @return string the rendering result. Null if the rendering result is not required.
53: * @throws CException if the view file does not exist
54: */
55: // public function renderFile($viewFile,$data=null,$return=false) {
56: // return parent::renderFile(Yii::getCustomPath($viewFile),$data,$return);
57: // }
58:
59:
60: public function actions () {
61: return $this->getBehaviorActions ();
62: }
63:
64:
65: /**
66: * Finds a view file based on its name.
67: * Overrides {@link CBaseController::resolveViewFile} to check if the requested view
68: * has a version in /custom, and uses that if it exists.
69: *
70: * @param string $viewName the view name
71: * @param string $viewPath the directory that is used to search for a relative view name
72: * @param string $basePath the directory that is used to search for an absolute view name under the application
73: * @param string $moduleViewPath the directory that is used to search for an absolute view name under the current module.
74: * If this is not set, the application base view path will be used.
75: * @return mixed the view file path. False if the view file does not exist.
76: */
77: public function resolveViewFile($viewName,$viewPath,$basePath,$moduleViewPath=null) {
78: if(empty($viewName))
79: return false;
80:
81: if($moduleViewPath===null)
82: $moduleViewPath=$basePath;
83:
84: if(($renderer=Yii::app()->getViewRenderer())!==null)
85: $extension=$renderer->fileExtension;
86: else
87: $extension='.php';
88: if($viewName[0]==='/')
89: {
90: if(strncmp($viewName,'//',2)===0)
91: $viewFile=$basePath.$viewName;
92: else
93: $viewFile=$moduleViewPath.$viewName;
94: }
95: else if(strpos($viewName,'.'))
96: $viewFile=Yii::getPathOfAlias($viewName);
97: else
98: $viewFile=$viewPath.DIRECTORY_SEPARATOR.$viewName;
99:
100: // custom part
101: $fileName = Yii::getCustomPath($viewFile.$extension);
102: if(is_file($fileName)) {
103: return Yii::app()->findLocalizedFile($fileName);
104: } else if($extension!=='.php') {
105: $fileName = Yii::getCustomPath($viewFile.'.php');
106: if(is_file($fileName))
107: return Yii::app()->findLocalizedFile($fileName);
108: }
109: return false;
110: }
111:
112: public function badRequest ($message=null) {
113: throw $this->badRequestException ($message);
114: }
115:
116: public function redirectToLogin () {
117: if (Yii::app()->params->isMobileApp) {
118: $this->redirect($this->createUrl('/mobile/login'));
119: } else {
120: $this->redirect($this->createUrl('/site/login'));
121: }
122: }
123:
124: /**
125: * Set fields of model using uploaded files in super global
126: * @param bool $merge if true, files will be merged with existing values
127: */
128: public function setFileFields ($model, $merge=false) {
129: if (isset ($_FILES[get_class ($model)])) {
130: $files = $_FILES[get_class ($model)];
131: $attributes = array_keys ($files['name']);
132: foreach ($attributes as $attr) {
133: if ($merge) {
134: $model->$attr = array_merge (
135: is_array ($model->$attr) ? $model->$attr : array (),
136: CUploadedFile::getInstances ($model, $attr));
137: } else {
138: $model->$attr = CUploadedFile::getInstance ($model, $attr);
139: }
140: }
141: }
142: }
143:
144: /**
145: * @return CHttpException
146: */
147: protected function badRequestException ($message=null) {
148: if ($message === null) $message = Yii::t('app', 'Bad request.');
149: return new CHttpException (400, $message);
150: }
151:
152:
153: /**
154: * More reliable alternative to CHttpRequest::getIsAjaxRequest in cases where 'x2ajax' or
155: * 'ajax' parameters are being used.
156: * See http://www.yiiframework.com/forum/index.php?/topic/4945-yiiapp-request-isajaxrequest/
157: */
158: public function isAjaxRequest () {
159: return
160: Yii::app()->request->getIsAjaxRequest () ||
161: isset ($_POST['x2ajax']) && $_POST['x2ajax'] ||
162: isset ($_POST['ajax']) && $_POST['ajax'] ||
163: isset ($_GET['x2ajax']) && $_GET['x2ajax'] ||
164: isset ($_GET['ajax']) && $_GET['ajax'];
165: }
166:
167: /**
168: * Rejects ajax requests to non-mobile actions from X2Touch.
169: */
170: protected function validateMobileRequest ($action) {
171: if (!Yii::app()->isMobileApp () || !$this->isAjaxRequest ()) return;
172:
173: $whitelist = array ('getItems', 'error');
174: if (!in_array ($action->getId (), $whitelist) &&
175: !($this instanceof MobileController) &&
176: (!$this->asa ('X2MobileControllerBehavior') ||
177: !$this->asa ('X2MobileControllerBehavior')->hasMobileAction ($action->getId ()))) {
178:
179: throw new CHttpException (400, Yii::t('app', 'Bad request.'));
180: }
181: }
182:
183: protected function beforeAction ($action) {
184: $this->validateMobileRequest ($action);
185: $run = $this->runBehaviorBeforeActionHandlers ($action);
186: return $run;
187: }
188:
189: protected function runBehaviorBeforeActionHandlers ($action) {
190: $run = true;
191: foreach ($this->behaviors () as $name => $config) {
192: if ($this->asa ($name) && $this->asa ($name)->getEnabled () &&
193: $this->asa ($name) instanceof X2ControllerBehavior) {
194:
195: $run &= $this->asa ($name)->beforeAction ($action);
196: }
197: if (!$run) break;
198: }
199: return $run;
200: }
201:
202: protected function getBehaviorActions () {
203: $actions = array ();
204: foreach ($this->behaviors () as $name => $config) {
205: if ($this->asa ($name) && $this->asa ($name)->getEnabled () &&
206: $this->asa ($name) instanceof X2ControllerBehavior) {
207:
208: $actions = array_merge ($this->asa ($name)->actions (), $actions);
209: }
210: }
211: return $actions;
212: }
213:
214:
215:
216: }
217: