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 contact@x2engine.com.
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: * A behavior for controllers; contains methods common to controllers whether or
39: * not they are children of x2base.
40: *
41: * All controllers that use this behavior must declare the "modelClass" property.
42: *
43: * @property X2Model $model (read-only); in the context of viewing or updating a
44: * record, this contains the active record object corresponding to that record.
45: * Its value is set by calling {@link getModel()} with the ID of the desired record.
46: * @property string $resolvedModelClass (read-only) The class name of the model to use
47: * for {@link model}. In some cases (i.e. some older custom modules) the class
48: * name will not be specified in the controller, and thus it is useful to guess
49: * its corresponding model's name based on its own name.
50: * @package application.components
51: */
52: class CommonControllerBehavior extends CBehavior {
53:
54: /**
55: * Stores the value of {@link $model}
56: */
57: private $_model;
58: public $redirectOnNullModel = true;
59: public $throwOnNullModel = true;
60:
61: /**
62: * Model class specified by the property {@link x2base.modelClass} or
63: * determined automatically based on controller ID, if possible.
64: * @var type
65: */
66: private $_resolvedModelClass;
67:
68: public function attach($owner) {
69: if (!property_exists($owner, 'modelClass'))
70: throw new CException('Property "modelClass" must be declared in all controllers that use CommonControllerBehavior, but it is not declared in ' . get_class($owner));
71: parent::attach($owner);
72: }
73:
74: /**
75: * Returns the data model based on the primary key given.
76: *
77: * If the data model is not found, an HTTP exception will be raised.
78: * @param integer $id the ID of the model to be loaded. Note, it is assumed that
79: * when this value is null, {@link _model} is set already; otherwise there's
80: * nothing that can be done to correctly resolve the model.
81: * @param bool $throw Whether to throw a 404 upon not finding the model
82: */
83: public function getModel($id = null) {
84: $throw = $this->throwOnNullModel;
85: $redirect = $this->redirectOnNullModel;
86: if (!isset($this->_model)) {
87: // Special case for Admin: let the ID be the one and only record if unspecified
88: if ($this->resolvedModelClass == 'Admin' && empty($id))
89: $id = 1;
90:
91: // Last-ditch effort:
92: if (empty($id) && isset($_GET['id'])) {
93: $id = $_GET['id'];
94: }
95:
96: // ID was never specified, so there's no way to tell which record to
97: // load. Redirect or throw an exception based on function args.
98: if ($id === null) {
99: if ($redirect) {
100: $this->owner->redirect(array('index'));
101: } elseif ($throw) {
102: throw new CHttpException(401, Yii::t('app', 'Invalid request; no record ID specified.'));
103: }
104: }
105:
106: // Look up model; ID specified
107: $this->_model = CActiveRecord::model($this->resolvedModelClass)->findByPk((int) $id);
108:
109: // Model record couldn't be retrieved, so throw a 404:
110: if ($this->_model === null && $throw)
111: throw new CHttpException(404, Yii::t('app', 'The requested page does not exist.'));
112: } else if ($this->_model->id != $id && !empty($id)) { // A different record is being requested
113: // Change the ID and load the different record.
114: // Note that setting the attribute to null will cause isset to return false.
115: $this->_model = null;
116: return $this->getModel($id);
117: }
118: return $this->_model;
119: }
120:
121: public function lookUpModel ($id, $modelClass) {
122: $throw = $this->throwOnNullModel;
123: $model = null;
124:
125: // Look up model; ID specified
126: $model = CActiveRecord::model($modelClass)->findByPk((int) $id);
127:
128: // Model record couldn't be retrieved, so throw a 404:
129: if ($model === null && $throw)
130: throw new CHttpException(404, Yii::t('app', 'The requested page does not exist.'));
131: return $model;
132: }
133:
134: /**
135: * Obtain the IP address of the current web client.
136: * @return string
137: */
138: public function getRealIp() {
139: foreach (array(
140: 'HTTP_CLIENT_IP',
141: 'HTTP_X_FORWARDED_FOR',
142: 'HTTP_X_FORWARDED',
143: 'HTTP_X_CLUSTER_CLIENT_IP',
144: 'HTTP_FORWARDED_FOR',
145: 'HTTP_FORWARDED',
146: 'REMOTE_ADDR'
147: ) as $var) {
148: if (array_key_exists($var, $_SERVER)) {
149: foreach (explode(',', $_SERVER[$var]) as $ip) {
150: $ip = trim($ip);
151: $filters = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE;
152: if (!YII_DEBUG)
153: $filters = $filters | FILTER_FLAG_NO_PRIV_RANGE;
154: if (filter_var($ip, FILTER_VALIDATE_IP, $filters) !== false)
155: return $ip;
156: }
157: }
158: }
159: return false;
160: }
161:
162: /**
163: * Resolve and return the model class, if specified, or a guess.
164: * @return string
165: */
166: public function getResolvedModelClass() {
167: if (!isset($this->_resolvedModelClass)) {
168: if (isset($this->owner->modelClass)) {
169: // Model class has been specified in the controller:
170: $modelClass = $this->owner->modelClass;
171: } else {
172: // Attempt to find an active record model named after the
173: // controller. Typically the case in custom modules.
174: $modelClass = ucfirst($this->owner->id);
175: if (!class_exists($modelClass)) {
176: $modelClass = 'Admin'; // Fall-back default
177: }
178: }
179: $this->_resolvedModelClass = $modelClass;
180: }
181: return $this->_resolvedModelClass;
182: }
183:
184: /**
185: * Kept for backwards compatibility with controllers (including custom ones)
186: * that use loadModel.
187: *
188: * @return type
189: */
190: public function loadModel($id) {
191: return $this->getModel($id);
192: }
193:
194: /**
195: * Renders Google Analytics tracking code, if enabled.
196: *
197: * @param type $location Named location in the app
198: */
199: public function renderGaCode($location) {
200: $propertyId = Yii::app()->settings->{"gaTracking_" . $location};
201: if (!empty($propertyId))
202: $this->owner->renderPartial('application.components.views.gaTrackingScript', array('propertyId' => $propertyId));
203: }
204:
205: /**
206: * For server config or other expected errors which are not bugs but
207: * prevent the software from functioning properly.
208: * @param type $error
209: */
210: public function errorMessage($message, $code = 500, $type = "PHP Error") {
211: $error = array(
212: 'code' => $code,
213: 'type' => $type,
214: 'message' => $message,
215: 'file' => '',
216: 'line' => '',
217: 'trace' => '',
218: 'source' => ''
219: );
220: $this->owner->render('/site/errorDisplay', $error);
221: Yii::app()->end();
222: }
223:
224: }
225:
226: ?>
227: