Vtiger_WebUI::process()   F
last analyzed

Complexity

Conditions 52
Paths > 20000

Size

Total Lines 135
Code Lines 104

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2756

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 104
c 1
b 0
f 0
dl 0
loc 135
ccs 0
cts 96
cp 0
rs 0
cc 52
nc 17085000
nop 1
crap 2756

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/* +**********************************************************************************
3
 * The contents of this file are subject to the vtiger CRM Public License Version 1.1
4
 * ("License"); You may not use this file except in compliance with the License
5
 * The Original Code is:  vtiger CRM Open Source
6
 * The Initial Developer of the Original Code is vtiger.
7
 * Portions created by vtiger are Copyright (C) vtiger.
8
 * All Rights Reserved.
9
 * Contributor(s): YetiForce S.A.
10
 * ********************************************************************************** */
11
require_once 'include/ConfigUtils.php';
12
require_once 'include/utils/CommonUtils.php';
13
require_once 'include/fields/DateTimeField.php';
14
require_once 'include/fields/DateTimeRange.php';
15
require_once 'include/fields/CurrencyField.php';
16
require_once 'include/CRMEntity.php';
17
include_once 'modules/Vtiger/CRMEntity.php';
18
require_once 'include/runtime/Cache.php';
19
require_once 'modules/Vtiger/helpers/Util.php';
20
require_once 'modules/PickList/DependentPickListUtils.php';
21
require_once 'modules/Users/Users.php';
22
require_once 'include/Webservices/Utils.php';
23
require_once 'include/Loader.php';
24
Vtiger_Loader::includeOnce('include.runtime.EntryPoint');
25
App\Cache::init();
26
App\Debuger::init();
27
App\Db::$connectCache = App\Config::performance('ENABLE_CACHING_DB_CONNECTION');
28
App\Log::$logToProfile = Yii::$logToProfile = App\Config::debug('LOG_TO_PROFILE');
29
App\Log::$logToConsole = App\Config::debug('DISPLAY_LOGS_IN_CONSOLE');
30
App\Log::$logToFile = App\Config::debug('LOG_TO_FILE');
31
32
class Vtiger_WebUI extends Vtiger_EntryPoint
33
{
34
	/**
35
	 * User privileges model instance.
36
	 *
37
	 * @var Users_Privileges_Model
38
	 */
39
	protected $userPrivilegesModel;
40
41
	/**
42
	 * Function to check if the User has logged in.
43
	 *
44
	 * @param \App\Request $request
45
	 *
46
	 * @throws \App\Exceptions\Unauthorized
47
	 */
48
	protected function checkLogin(App\Request $request)
49
	{
50
		if (!$this->hasLogin()) {
51
			if ($request->isAjax()) {
52
				throw new \App\Exceptions\Unauthorized('LBL_LOGIN_IS_REQUIRED', 401);
53
			}
54
			header('location: index.php');
55
			return true;
56
		}
57
		return false;
58
	}
59
60
	/**
61
	 * Function to get the instance of the logged in User.
62
	 *
63
	 * @return Users object
64
	 */
65
	public function getLogin()
66
	{
67
		$user = parent::getLogin();
68
		if (!$user && App\Session::has('authenticated_user_id')) {
69
			$userId = App\Session::get('authenticated_user_id');
70
			if ($userId && App\Config::main('application_unique_key') === App\Session::get('app_unique_key') && \App\User::isExists($userId)) {
0 ignored issues
show
Bug introduced by
$userId of type array|string is incompatible with the type integer expected by parameter $id of App\User::isExists(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
			if ($userId && App\Config::main('application_unique_key') === App\Session::get('app_unique_key') && \App\User::isExists(/** @scrutinizer ignore-type */ $userId)) {
Loading history...
71
				\App\User::setCurrentUserId($userId);
0 ignored issues
show
Bug introduced by
$userId of type array|string is incompatible with the type integer expected by parameter $userId of App\User::setCurrentUserId(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

71
				\App\User::setCurrentUserId(/** @scrutinizer ignore-type */ $userId);
Loading history...
72
				$this->setLogin();
73
			}
74
		}
75
		return $user;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $user returns the type boolean which is incompatible with the documented return type Users.
Loading history...
76
	}
77
78
	/**
79
	 * Process.
80
	 *
81
	 * @param \App\Request $request
82
	 *
83
	 * @throws Exception
84
	 * @throws \App\Exceptions\AppException
85
	 */
86
	public function process(App\Request $request)
87
	{
88
		if (\Config\Security::$forceHttpsRedirection && !\App\RequestUtil::isHttps()) {
89
			header("location: https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]", true, 301);
90
		}
91
		if (\Config\Security::$forceUrlRedirection) {
92
			$requestUrl = (\App\RequestUtil::isHttps() ? 'https' : 'http') . '://' . $request->getServer('HTTP_HOST') . $request->getServer('REQUEST_URI');
0 ignored issues
show
Bug introduced by
Are you sure $request->getServer('HTTP_HOST') of type false can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

92
			$requestUrl = (\App\RequestUtil::isHttps() ? 'https' : 'http') . '://' . /** @scrutinizer ignore-type */ $request->getServer('HTTP_HOST') . $request->getServer('REQUEST_URI');
Loading history...
Bug introduced by
Are you sure $request->getServer('REQUEST_URI') of type false can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

92
			$requestUrl = (\App\RequestUtil::isHttps() ? 'https' : 'http') . '://' . $request->getServer('HTTP_HOST') . /** @scrutinizer ignore-type */ $request->getServer('REQUEST_URI');
Loading history...
93
			if (0 !== stripos($requestUrl, App\Config::main('site_URL'))) {
94
				header('location: ' . App\Config::main('site_URL'), true, 301);
95
			}
96
		}
97
		try {
98
			App\Session::init();
99
			App\Process::init();
100
			// common utils api called, depend on this variable right now
101
			$this->getLogin();
102
			App\Debuger::initConsole();
103
			$hasLogin = $this->hasLogin();
104
			$moduleName = $request->getModule();
105
			$qualifiedModuleName = $request->getModule(false);
106
			$view = $request->getByType('view', 2);
107
			$action = $request->getByType('action', 2);
108
			$response = false;
109
			if (!$hasLogin && 'GET' === $_SERVER['REQUEST_METHOD'] && 'Users' !== $moduleName && ($returnUrl = $request->getServer('QUERY_STRING')) && !\App\Session::has('return_params')) {
0 ignored issues
show
introduced by
$hasLogin is of type Users, thus it always evaluated to true.
Loading history...
110
				//Take the url that user would like to redirect after they have successfully logged in.
111
				\App\Session::set('return_params', str_replace('&amp;', '&', $returnUrl));
112
			}
113
			if (empty($moduleName)) {
114
				if ($hasLogin) {
0 ignored issues
show
introduced by
$hasLogin is of type Users, thus it always evaluated to true.
Loading history...
115
					$defaultModule = App\Config::main('default_module');
116
					if (!empty($defaultModule) && 'Home' !== $defaultModule && \App\Privilege::isPermitted($defaultModule)) {
117
						$moduleName = $defaultModule;
118
						$qualifiedModuleName = $defaultModule;
119
						if (empty($view = Vtiger_Module_Model::getInstance($moduleName)->getDefaultViewName())) {
120
							$view = 'List';
121
						}
122
					} else {
123
						$qualifiedModuleName = $moduleName = 'Home';
124
						$view = 'DashBoard';
125
					}
126
				} else {
127
					$qualifiedModuleName = $moduleName = 'Users';
128
					$view = 'Login';
129
				}
130
				$request->set('module', $moduleName);
131
				$request->set('view', $view);
132
			}
133
			if (!empty($action)) {
134
				$componentType = 'Action';
135
				$componentName = $action;
136
				\App\Config::setJsEnv('action', $action);
137
			} else {
138
				$componentType = 'View';
139
				if (empty($view)) {
140
					$view = 'Index';
141
				}
142
				$componentName = $view;
143
				\App\Config::setJsEnv('view', $view);
144
			}
145
			if ('Login' === $view && 'Users' === $moduleName) {
146
				if (!\App\Session::has('CSP_TOKEN')) {
147
					\App\Controller\Headers::generateCspToken();
148
				}
149
				if ($hasLogin) {
0 ignored issues
show
introduced by
$hasLogin is of type Users, thus it always evaluated to true.
Loading history...
150
					header('location: index.php');
151
					return false;
152
				}
153
			}
154
			\App\Process::$processName = $componentName;
0 ignored issues
show
Documentation Bug introduced by
It seems like $componentName can also be of type boolean. However, the property $processName is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
155
			\App\Process::$processType = $componentType;
156
			\App\Config::setJsEnv('module', $moduleName);
157
			\App\Config::setJsEnv('mode', $request->getMode());
158
			if ($qualifiedModuleName && 0 === stripos($qualifiedModuleName, 'Settings') && empty(\App\User::getCurrentUserId())) {
159
				header('location: ' . App\Config::main('site_URL'), true);
160
			}
161
			if ('AppComponents' === $moduleName) {
162
				$handlerClass = "App\\Controller\\Components\\{$componentType}\\{$componentName}";
163
			} else {
164
				$handlerClass = Vtiger_Loader::getComponentClassName($componentType, $componentName, $qualifiedModuleName);
165
			}
166
			$handler = new $handlerClass();
167
			if (!$handler) {
0 ignored issues
show
introduced by
$handler is of type object, thus it always evaluated to true.
Loading history...
168
				\App\Log::error("HandlerClass: $handlerClass", 'Loader');
169
				throw new \App\Exceptions\AppException('LBL_HANDLER_NOT_FOUND', 405);
170
			}
171
			$handler->validateRequest($request);
172
			if ($handler->loginRequired() && $this->checkLogin($request)) {
173
				return true;
174
			}
175
			if ($handler->isSessionExtend($request)) {
176
				\App\Session::set('last_activity', \App\Process::$startTime);
177
			}
178
			if ('ModComments' === $moduleName && 'List' === $view) {
179
				header('location: index.php?module=Home&view=DashBoard');
180
			}
181
			$skipList = ['Users', 'Home', 'CustomView', 'Import', 'Export', 'Install', 'ModTracker', 'AppComponents'];
182
			if ($handler->loginRequired() && !\in_array($moduleName, $skipList) && false === stripos($qualifiedModuleName, 'Settings')) {
183
				$this->triggerCheckPermission($handler, $request);
184
			} elseif (0 === stripos($qualifiedModuleName, 'Settings') || \in_array($moduleName, $skipList) || !$handler->loginRequired()) {
185
				$handler->checkPermission($request);
186
			}
187
			$this->triggerPreProcess($handler, $request);
188
			$response = $handler->process($request);
189
			$this->triggerPostProcess($handler, $request);
190
		} catch (Throwable $e) {
191
			\App\Log::error($e->getMessage() . PHP_EOL . $e->__toString());
192
			$messageHeader = 'LBL_ERROR';
193
			if ($e instanceof \App\Exceptions\NoPermittedToRecord || $e instanceof WebServiceException) {
194
				$messageHeader = 'LBL_PERMISSION_DENIED';
195
			} elseif ($e instanceof \App\Exceptions\Security) {
196
				$messageHeader = 'LBL_BAD_REQUEST';
197
			} elseif ($e instanceof \yii\db\Exception) {
198
				$messageHeader = 'LBL_ERROR';
199
			}
200
			\vtlib\Functions::throwNewException($e, false, $messageHeader);
201
			if (!($request->isAjax() && $request->isJSON())) {
202
				if (App\Config::debug('DISPLAY_EXCEPTION_BACKTRACE')) {
203
					echo '<pre class="my-5 mx-auto card p-3 u-w-fit shadow js-exception-backtrace">' . App\Purifier::encodeHtml(str_replace(ROOT_DIRECTORY . DIRECTORY_SEPARATOR, '', $e->__toString())) . '</pre>';
204
					$response = false;
205
				}
206
				if (App\Config::debug('DISPLAY_EXCEPTION_LOGS')) {
207
					echo '<pre class="my-5 mx-auto card p-3 u-w-fit shadow js-exception-logs">' . App\Purifier::encodeHtml(str_replace(ROOT_DIRECTORY . DIRECTORY_SEPARATOR, '', \App\Log::getlastLogs())) . '</pre>';
208
					$response = false;
209
				}
210
			}
211
			if ('test' === App\Config::main('systemMode')) {
212
				file_put_contents('cache/logs/request.log', print_r($request->getAll(), true));
213
				if (\function_exists('apache_request_headers')) {
214
					file_put_contents('cache/logs/request.log', print_r(apache_request_headers(), true));
215
				}
216
				throw $e;
217
			}
218
		}
219
		if (isset($response) && \is_object($response)) {
220
			$response->emit();
221
		}
222
	}
223
224
	/**
225
	 * Trigger check permission.
226
	 *
227
	 * @param \App\Controller\Base $handler
228
	 * @param \App\Request         $request
229
	 *
230
	 * @throws \App\Exceptions\AppException
231
	 * @throws \App\Exceptions\NoPermitted
232
	 *
233
	 * @return bool
234
	 */
235
	protected function triggerCheckPermission(App\Controller\Base $handler, App\Request $request)
236
	{
237
		$moduleName = $request->getModule();
238
		$moduleModel = Vtiger_Module_Model::getInstance($moduleName);
239
		if (empty($moduleModel)) {
240
			\App\Log::error('HandlerModule: ' . $moduleName, 'Loader');
241
			throw new \App\Exceptions\AppException('ERR_MODULE_DOES_NOT_EXIST||' . $moduleName, 405);
242
		}
243
		$this->userPrivilegesModel = Users_Privileges_Model::getCurrentUserPrivilegesModel();
244
		if ($this->userPrivilegesModel->hasModulePermission($moduleName)) {
0 ignored issues
show
Bug introduced by
$moduleName of type string is incompatible with the type integer expected by parameter $mixed of Users_Privileges_Model::hasModulePermission(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

244
		if ($this->userPrivilegesModel->hasModulePermission(/** @scrutinizer ignore-type */ $moduleName)) {
Loading history...
245
			$handler->checkPermission($request);
246
			return true;
247
		}
248
		\App\Log::error("No permissions to the module: $moduleName", 'NoPermitted');
249
		throw new \App\Exceptions\NoPermitted('ERR_NOT_ACCESSIBLE', 403);
250
	}
251
252
	/**
253
	 * Trigger pre process.
254
	 *
255
	 * @param \App\Controller\Base $handler
256
	 * @param \App\Request         $request
257
	 *
258
	 * @return bool
259
	 */
260
	protected function triggerPreProcess(App\Controller\Base $handler, App\Request $request)
261
	{
262
		$handler->sendHeaders();
263
		if ($request->isAjax()) {
264
			$handler->preProcessAjax($request);
265
			return true;
266
		}
267
		$handler->preProcess($request);
268
	}
269
270
	/**
271
	 * Trigger post process.
272
	 *
273
	 * @param \App\Controller\Base $handler
274
	 * @param \App\Request         $request
275
	 *
276
	 * @return bool
277
	 */
278
	protected function triggerPostProcess(App\Controller\Base $handler, App\Request $request)
279
	{
280
		if ($request->isAjax()) {
281
			$handler->postProcessAjax($request);
282
			return true;
283
		}
284
		$handler->postProcess($request);
285
	}
286
}
287