Completed
Push — master ( 598dbf...2a5079 )
by Nazar
04:19
created

profile   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 299
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 10

Test Coverage

Coverage 98.22%

Importance

Changes 0
Metric Value
dl 0
loc 299
ccs 166
cts 169
cp 0.9822
rs 4.5454
c 0
b 0
f 0
wmc 59
lcom 0
cbo 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A profile_get() 0 18 2
B profile_patch() 0 20 6
A can_change_login_to() 0 9 4
B profile_change_password() 0 23 6
C profile_registration() 0 37 8
A try_to_register() 0 13 4
B fill_optional_profile_data() 0 18 6
B profile_restore_password() 0 32 6
C profile_sign_in() 0 49 13
A profile_sign_out() 0 16 3
A profile_configuration() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like profile often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use profile, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package    CleverStyle Framework
4
 * @subpackage System module
5
 * @category   modules
6
 * @author     Nazar Mokrynskyi <[email protected]>
7
 * @copyright  Copyright (c) 2015-2016, Nazar Mokrynskyi
8
 * @license    MIT License, see license.txt
9
 */
10
namespace cs\modules\System\api\Controller;
11
use
12
	cs\Config,
13
	cs\Core,
14
	cs\ExitException,
15
	cs\Language,
16
	cs\Mail,
17
	cs\Session,
18
	cs\User;
19
20
trait profile {
21 2
	public static function profile_get () {
22 2
		$User         = User::instance();
23
		$fields       = [
24 2
			'id',
25
			'login',
26
			'username',
27
			'language',
28
			'timezone',
29
			'avatar'
30
		];
31 2
		$result       = $User->get($fields, $User->id);
32 2
		$result['id'] = (int)$result['id'];
33 2
		if ($User->guest()) {
34 2
			$result['username'] = Language::instance()->system_profile_guest;
35 2
			$result['avatar']   = $User->avatar();
36
		}
37 2
		return $result;
38
	}
39
	/**
40
	 * @param \cs\Request $Request
41
	 *
42
	 * @throws ExitException
43
	 */
44 2
	public static function profile_patch ($Request) {
45 2
		$user_data = $Request->data('login', 'username', 'language', 'timezone', 'avatar');
46
		if (
47 2
			!$user_data ||
48 2
			!$user_data['login']
49
		) {
50 2
			throw new ExitException(400);
51
		}
52 2
		$User = User::instance();
53 2
		if ($User->guest()) {
54 2
			throw new ExitException(403);
55
		}
56 2
		$user_data['login'] = mb_strtolower($user_data['login']);
57 2
		if (static::can_change_login_to($User, $user_data['login'])) {
58 2
			throw new ExitException(Language::instance()->system_admin_users_login_occupied, 400);
59
		}
60 2
		if (!$User->set($user_data)) {
61 2
			throw new ExitException(500);
62
		}
63
	}
64
	/**
65
	 * Check for changing login to new one and whether it is available
66
	 *
67
	 * @param User   $User
68
	 * @param string $login
69
	 *
70
	 * @return bool
71
	 */
72 2
	protected static function can_change_login_to ($User, $login) {
73
		return
74 2
			$login == $User->login ||
75 2
			$login == $User->email ||
76
			(
77 2
				!filter_var($login, FILTER_VALIDATE_EMAIL) &&
78 2
				$User->get_id(hash('sha224', $login)) === false
79
			);
80
	}
81
	/**
82
	 * @param \cs\Request $Request
83
	 *
84
	 * @throws ExitException
85
	 */
86 2
	public static function profile_change_password ($Request) {
87 2
		$data = $Request->data('current_password', 'new_password');
88 2
		if (!$data) {
89 2
			throw new ExitException(400);
90
		}
91 2
		$User = User::instance();
92 2
		if (!$User->user()) {
93 2
			throw new ExitException(403);
94
		}
95 2
		$L = Language::prefix('system_profile_');
96 2
		if (!$data['new_password']) {
97 2
			throw new ExitException($L->please_type_new_password, 400);
98
		}
99 2
		if (!$User->validate_password($data['current_password'], $User->id, true)) {
100 2
			throw new ExitException($L->wrong_current_password, 400);
101
		}
102 2
		$id = $User->id;
103 2
		if ($User->set_password($data['new_password'], $id, true)) {
104 2
			Session::instance()->add($id);
105
		} else {
106 2
			throw new ExitException($L->change_password_server_error, 500);
107
		}
108 2
	}
109
	/**
110
	 * @param \cs\Request  $Request
111
	 * @param \cs\Response $Response
112
	 *
113
	 * @throws ExitException
114
	 */
115 2
	public static function profile_registration ($Request, $Response) {
116 2
		$Config = Config::instance();
117 2
		$L      = Language::prefix('system_profile_registration_');
118 2
		$User   = User::instance();
119 2
		if (!$User->guest()) {
120 2
			throw new ExitException(403);
121
		}
122 2
		if (!$Config->core['allow_user_registration']) {
123 2
			throw new ExitException($L->prohibited, 403);
124
		}
125 2
		$email   = $Request->data('email');
126 2
		$email   = mb_strtolower($email);
127 2
		$result  = static::try_to_register($User, $L, $email);
128 2
		$confirm = $result['reg_key'] !== true;
129 2
		static::fill_optional_profile_data($Request, $User, $result['id']);
130 2
		$username  = $User->username($result['id']);
131 2
		$login     = $User->get('login', $result['id']);
132 2
		$site_name = $Config->core['site_name'];
133 2
		$title     = $L->success_mail($site_name);
134 2
		$body      = $L->success_mail($username, $site_name, $Config->core_url().'/profile/settings', $login);
135 2
		if ($confirm) {
136 2
			$title = $L->need_confirmation_mail($site_name);
137 2
			$body  = $L->need_confirmation_mail_body(
138
				$username,
139
				$site_name,
140 2
				$Config->core_url()."/profile/registration_confirmation/$result[reg_key]",
141 2
				$L->time($Config->core['registration_confirmation_time'], 'd')
142
			);
143 2
		} elseif (!$Request->data('password') && $result['password']) {
144 2
			$body = $L->success_mail_with_password_body($username, $site_name, $Config->core_url().'/profile/settings', $login, $result['password']);
145
		}
146 2
		if (!Mail::instance()->send_to($email, $title, $body)) {
147 2
			$User->registration_cancel();
148 2
			throw new ExitException($L->mail_sending_error, 500);
149
		}
150 2
		$Response->code = $confirm ? 202 : 201;
151 2
	}
152
	/**
153
	 * @param User            $User
154
	 * @param Language\Prefix $L
155
	 * @param string          $email
156
	 *
157
	 * @return array
158
	 *
159
	 * @throws ExitException
160
	 */
161 2
	protected static function try_to_register ($User, $L, $email) {
162 2
		$result = $User->registration($email);
163 2
		if ($result === false) {
164 2
			throw new ExitException($L->please_type_correct_email, 400);
165
		}
166 2
		if ($result == 'error') {
167
			throw new ExitException($L->server_error, 500);
168
		}
169 2
		if ($result == 'exists') {
170 2
			throw new ExitException($L->error_exists, 400);
171
		}
172 2
		return $result;
173
	}
174
	/**
175
	 * @param \cs\Request $Request
176
	 * @param User        $User
177
	 * @param int         $user_id
178
	 */
179 2
	protected static function fill_optional_profile_data ($Request, $User, $user_id) {
180 2
		if ($Request->data('username')) {
181 2
			$User->set('username', $Request->data['username'], $user_id);
182
		}
183
		// Actually `sha512(sha512(password) + public_key)` instead of plain password
184 2
		if ($Request->data('password')) {
185 2
			$User->set_password($Request->data['password'], $user_id, true);
186
		}
187 2
		if ($Request->data('language')) {
188 2
			$User->set('language', $Request->data['language'], $user_id);
189
		}
190 2
		if ($Request->data('timezone')) {
191 2
			$User->set('timezone', $Request->data['timezone'], $user_id);
192
		}
193 2
		if ($Request->data('avatar')) {
194 2
			$User->set('avatar', $Request->data['avatar'], $user_id);
195
		}
196 2
	}
197
	/**
198
	 * @param \cs\Request $Request
199
	 *
200
	 * @throws ExitException
201
	 */
202 2
	public static function profile_restore_password ($Request) {
203 2
		$User = User::instance();
204 2
		if (!$User->guest()) {
205 2
			throw new ExitException(403);
206
		}
207 2
		$L     = Language::prefix('system_profile_restore_password_');
208 2
		$email = $Request->data('email');
209 2
		if (!$email) {
210 2
			throw new ExitException($L->please_type_your_email, 400);
211
		}
212 2
		$id = $User->get_id(mb_strtolower($email));
213 2
		if (!$id) {
214 2
			throw new ExitException($L->user_with_such_login_email_not_found, 400);
215
		}
216 2
		$key    = $User->restore_password($id);
217 2
		$Config = Config::instance();
218
		if (
219 2
			!$key ||
220 2
			!Mail::instance()->send_to(
221 2
				$User->get('email', $id),
222 2
				$L->confirmation_mail($Config->core['site_name']),
223 2
				$L->confirmation_mail_body(
224 2
					$User->username($id),
225 2
					$Config->core['site_name'],
226 2
					$Config->core_url()."/profile/restore_password_confirmation/$key",
227 2
					$L->time($Config->core['registration_confirmation_time'], 'd')
228
				)
229
			)
230
		) {
231 2
			throw new ExitException($L->server_error, 500);
232
		}
233 2
	}
234
	/**
235
	 * @param \cs\Request $Request
236
	 *
237
	 * @throws ExitException
238
	 */
239 2
	public static function profile_sign_in ($Request) {
240 2
		$Config = Config::instance();
241 2
		$L      = Language::prefix('system_profile_sign_in_');
242 2
		$User   = User::instance();
243 2
		$data   = $Request->data('login', 'password');
244 2
		if (!$data) {
245 2
			throw new ExitException(400);
246
		}
247 2
		if (!$User->guest()) {
248 2
			return;
249
		}
250 2
		$attempts = $User->get_sign_in_attempts_count($data['login']);
251
		if (
252 2
			$Config->core['sign_in_attempts_block_count'] &&
253 2
			$attempts >= $Config->core['sign_in_attempts_block_count']
254
		) {
255 2
			$User->sign_in_result(false, $data['login']);
256 2
			throw new ExitException($L->attempts_are_over_try_again_in(format_time($Config->core['sign_in_attempts_block_time'])), 403);
257
		}
258 2
		$id = $User->get_id($data['login']);
259 2
		if ($id && $User->validate_password($data['password'], $id, true)) {
260 2
			$status      = $User->get('status', $id);
261 2
			$block_until = $User->get('block_until', $id);
262 2
			if ($status == User::STATUS_NOT_ACTIVATED) {
263 2
				throw new ExitException($L->your_account_is_not_active, 403);
264
			}
265 2
			if ($status == User::STATUS_INACTIVE) {
266 2
				throw new ExitException($L->your_account_disabled, 403);
267
			}
268 2
			if ($block_until > time()) {
269 2
				throw new ExitException($L->your_account_blocked_until(date($L->_datetime, $block_until)), 403);
270
			}
271 2
			Session::instance()->add($id);
272 2
			$User->sign_in_result(true, $data['login']);
273
		} else {
274 2
			$User->sign_in_result(false, $data['login']);
275 2
			++$attempts;
276 2
			$content = $L->authentication_error;
277 2
			if ($Config->core['sign_in_attempts_block_count']) {
278 2
				$attempts_left = $Config->core['sign_in_attempts_block_count'] - $attempts;
279 2
				if (!$attempts_left) {
280 2
					throw new ExitException($L->attempts_are_over_try_again_in(format_time($Config->core['sign_in_attempts_block_time'])), 403);
281 2
				} elseif ($attempts >= $Config->core['sign_in_attempts_block_count'] * 2 / 3) {
282 2
					$content .= ' '.$L->attempts_left($attempts_left);
283
				}
284
			}
285 2
			throw new ExitException($content, 400);
286
		}
287 2
	}
288
	/**
289
	 * @param \cs\Request  $Request
290
	 * @param \cs\Response $Response
291
	 *
292
	 * @throws ExitException
293
	 */
294 2
	public static function profile_sign_out (
295
		/** @noinspection PhpUnusedParameterInspection */
296
		$Request,
297
		$Response
298
	) {
299 2
		if (User::instance()->guest()) {
300 2
			return;
301
		}
302 2
		if (!Session::instance()->del()) {
303
			throw new ExitException(500);
304
		}
305
		/**
306
		 * Hack for 403 after sign out in administration
307
		 */
308 2
		$Response->cookie('sign_out', 1, time() + 5, true);
309 2
	}
310 2
	public static function profile_configuration () {
311 2
		$Config = Config::instance();
312
		return [
313 2
			'public_key'            => Core::instance()->public_key,
314 2
			'password_min_length'   => (int)$Config->core['password_min_length'],
315 2
			'password_min_strength' => (int)$Config->core['password_min_strength']
316
		];
317
	}
318
}
319