Test Failed
Pull Request — master (#120)
by Maximo
05:58
created

Users::isFirstSignup()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Canvas\Models;
5
6
use Canvas\Traits\PermissionsTrait;
7
use Canvas\Traits\SubscriptionPlanLimitTrait;
8
use Phalcon\Cashier\Billable;
9
use Canvas\Exception\ServerErrorHttpException;
10
use Exception;
11
use Carbon\Carbon;
12
use Phalcon\Validation;
13
use Phalcon\Validation\Validator\Email;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\Email was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Phalcon\Validation\Validator\PresenceOf;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\PresenceOf was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Phalcon\Validation\Validator\Regex;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\Regex was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Phalcon\Validation\Validator\Uniqueness;
17
use Canvas\Traits\FileSystemModelTrait;
18
use Phalcon\Security\Random;
0 ignored issues
show
Bug introduced by
The type Phalcon\Security\Random was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Baka\Database\Contracts\HashTableTrait;
20
21
/**
22
 * Class Users.
23
 *
24
 * @package Canvas\Models
25
 *
26
 * @property Users $user
27
 * @property Config $config
28
 * @property Apps $app
29
 * @property Companies $defaultCompany
30
 * @property \Phalcon\Di $di
31
 */
32
class Users extends \Baka\Auth\Models\Users
33
{
34
    use PermissionsTrait;
35
    use Billable;
36
    use SubscriptionPlanLimitTrait;
37
    use FileSystemModelTrait;
38
    use HashTableTrait;
39
40
    /**
41
     * Default Company Branch.
42
     *
43
     * @var integer
44
     */
45
    public $default_company_branch;
46
47
    /**
48
     * Roles id.
49
     *
50
     * @var integer
51
     */
52
    public $roles_id;
53
54
    /**
55
     * Stripe id.
56
     *
57
     * @var string
58
     */
59
    public $stripe_id;
60
61
    /**
62
     * Card last four numbers.
63
     *
64
     * @var integer
65
     */
66
    public $card_last_four;
67
68
    /**
69
     * Card Brand.
70
     *
71
     * @var integer
72
     */
73
    public $card_brand;
74
75
    /**
76
     * Trial end date.
77
     *
78
     * @var string
79
     */
80
    public $trial_ends_at;
81
82
    /**
83
     * Provide the app plan id
84
     * if the user is signing up a new company.
85
     *
86
     * @var integer
87
     */
88
    public $appPlanId = null;
89
90
    /**
91
     * Active subscription id.Not an actual table field, used temporarily.
92
     * @var string
93
     */
94
    public $active_subscription_id;
95
96
    /**
97
     * System Module Id.
98
     * @var integer
99
     */
100
    public $system_modules_id = 2;
101
102
    /**
103
     * User email activation code.
104
     *
105
     * @var string
106
     */
107
    public $user_activation_email;
108
109
    /**
110
     * Initialize method for model.
111
     */
112
    public function initialize()
113
    {
114
        $this->setSource('users');
115
116
        //overwrite parent relationships
117
        $this->hasOne('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'session']);
118
        $this->hasMany('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'sessions']);
119
        $this->hasMany('id', 'Baka\Auth\Models\SessionKeys', 'users_id', ['alias' => 'sessionKeys']);
120
        $this->hasMany('id', 'Baka\Auth\Models\Banlist', 'users_id', ['alias' => 'bans']);
121
        $this->hasMany('id', 'Baka\Auth\Models\Sessions', 'users_id', ['alias' => 'sessions']);
122
        $this->hasMany('id', 'Canvas\Models\UserConfig', 'users_id', ['alias' => 'config']);
123
        $this->hasMany('id', 'Canvas\Models\UserLinkedSources', 'users_id', ['alias' => 'sources']);
124
        $this->hasOne('default_company', 'Canvas\Models\Companies', 'id', ['alias' => 'defaultCompany']);
125
        $this->hasOne('default_company', 'Canvas\Models\Companies', 'id', ['alias' => 'currentCompany']);
126
127
        $this->hasOne(
128
            'id',
129
            'Canvas\Models\UserRoles',
130
            'users_id',
131
            ['alias' => 'permission']
132
        );
133
134
        $this->hasMany(
135
            'id',
136
            'Canvas\Models\UserRoles',
137
            'users_id',
138
            ['alias' => 'permissions']
139
        );
140
141
        $this->hasManyToMany(
142
            'id',
143
            'Canvas\Models\UserRoles',
144
            'users_id',
145
            'roles_id',
146
            'Canvas\Models\Roles',
147
            'id',
148
            [
149
                'alias' => 'roles',
150
                'params' => [
151
                    'limit' => 1,
152
                    'conditions' => 'Canvas\Models\UserRoles.apps_id = ' . $this->di->getApp()->getId(),
153
                ]
154
            ]
155
        );
156
157
        $this->hasMany(
158
            'id',
159
            'Canvas\Models\Subscription',
160
            'user_id',
161
            [
162
                'alias' => 'allSubscriptions',
163
                'params' => [
164
                    'conditions' => 'apps_id = ?0',
165
                    'bind' => [$this->di->getApp()->getId()],
166
                    'order' => 'id DESC'
167
                ]
168
            ]
169
        );
170
171
        $this->hasMany(
172
            'id',
173
            'Canvas\Models\UsersAssociatedCompanies',
174
            'users_id',
175
            [
176
                'alias' => 'companies',
177
            ]
178
        );
179
180
        $this->hasMany(
181
            'id',
182
            'Canvas\Models\UsersAssociatedApps',
183
            'users_id',
184
            [
185
                'alias' => 'apps',
186
            ]
187
        );
188
189
        $this->hasMany(
190
            'id',
191
            'Canvas\Models\UserWebhooks',
192
            'users_id',
193
            ['alias' => 'userWebhook']
194
        );
195
196
        $systemModule = SystemModules::getSystemModuleByModelName(self::class);
197
        $this->hasOne(
198
            'id',
199
            'Canvas\Models\FileSystemEntities',
200
            'entity_id',
201
            [
202
                'alias' => 'files',
203
                'conditions' => 'system_modules_id = ?0',
204
                'bind' => [$systemModule->getId()]
205
            ]
206
        );
207
208
        $this->hasOne(
209
            'id',
210
            'Canvas\Models\FileSystemEntities',
211
            'entity_id',
212
            [
213
                'alias' => 'photo',
214
                'conditions' => 'system_modules_id = ?0',
215
                'bind' => [$systemModule->getId()]
216
            ]
217
        );
218
    }
219
220
    /**
221
     * Validations and business logic.
222
     */
223
    public function validation()
224
    {
225
        $validator = new Validation();
226
        $validator->add(
227
            'email',
228
            new Email([
229
                'field' => 'email',
230
                'required' => true,
231
            ])
232
        );
233
234
        $validator->add(
235
            'displayname',
236
            new PresenceOf([
237
                'field' => 'displayname',
238
                'required' => true,
239
            ])
240
        );
241
242
        $validator->add(
243
            'displayname',
244
            new Regex([
245
                'field' => 'displayname',
246
                'message' => _('Please use alphanumerics only.'),
247
                'pattern' => '/^[A-Za-z0-9_-]{1,32}$/',
248
            ])
249
        );
250
251
        // Unique values
252
        $validator->add(
253
            'email',
254
            new Uniqueness([
255
                'field' => 'email',
256
                'message' => _('This email already has an account.'),
257
            ])
258
        );
259
260
        return $this->validate($validator);
261
    }
262
263
    /**
264
     * Returns table name mapped in the model.
265
     *
266
     * @return string
267
     */
268
    public function getSource() : string
269
    {
270
        return 'users';
271
    }
272
273
    /**
274
    * Set hashtable settings table, userConfig ;).
275
    *
276
    * @return void
277
    */
278
    private function createSettingsModel(): void
0 ignored issues
show
Unused Code introduced by
The method createSettingsModel() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
279
    {
280
        $this->settingsModel = new UserConfig();
281
    }
282
283
    /**
284
     * Get the User key for redis.
285
     *
286
     * @return string
287
     */
288
    public function getKey() : int
289
    {
290
        return $this->id;
291
    }
292
293
    /**
294
     * A company owner is the first person that register this company
295
     * This only ocurres when signing up the first time, after that all users invites
296
     * come with a default_company id attached.
297
     *
298
     * @return boolean
299
     */
300
    public function isFirstSignup(): bool
301
    {
302
        return empty($this->default_company) ? true : false;
303
    }
304
305
    /**
306
     * Does the user have a role assign to him?
307
     *
308
     * @return boolean
309
     */
310
    public function hasRole(): bool
311
    {
312
        return !empty($this->roles_id) ? true : false;
313
    }
314
315
    /**
316
     * Get all of the subscriptions for the user.
317
     */
318
    public function subscriptions()
319
    {
320
        $this->hasMany(
321
            'id',
322
            'Canvas\Models\Subscription',
323
            'user_id',
324
            [
325
                'alias' => 'subscriptions',
326
                'params' => [
327
                    'conditions' => 'apps_id = ?0 and companies_id = ?1',
328
                    'bind' => [$this->di->getApp()->getId(), $this->default_company],
329
                    'order' => 'id DESC'
330
                ]
331
            ]
332
        );
333
334
        return $this->getRelated('subscriptions');
335
    }
336
337
    /**
338
     * Strat a free trial.
339
     *
340
     * @param Users $user
341
     * @return Subscription
342
     */
343
    public function startFreeTrial() : Subscription
344
    {
345
        $defaultPlan = AppsPlans::getDefaultPlan();
346
        $trialEndsAt = Carbon::now()->addDays($this->di->getApp()->plan->free_trial_dates);
347
348
        $subscription = new Subscription();
349
        $subscription->user_id = $this->getId();
350
        $subscription->companies_id = $this->default_company;
351
        $subscription->apps_id = $this->di->getApp()->getId();
352
        $subscription->apps_plans_id = $this->di->getApp()->default_apps_plan_id;
353
        $subscription->name = $defaultPlan->name;
354
        $subscription->stripe_id = $defaultPlan->stripe_id;
355
        $subscription->stripe_plan = $defaultPlan->stripe_plan;
356
        $subscription->quantity = 1;
357
        $subscription->trial_ends_at = $trialEndsAt->toDateTimeString();
358
        $subscription->trial_ends_days = $trialEndsAt->diffInDays(Carbon::now());
359
        $subscription->is_freetrial = 1;
360
        $subscription->is_active = 1;
361
362
        if (!$subscription->save()) {
363
            throw new ServerErrorHttpException((string)current($this->getMessages()));
364
        }
365
366
        $this->trial_ends_at = $subscription->trial_ends_at;
367
        $this->update();
368
369
        return $subscription;
370
    }
371
372
    /**
373
     * Before create.
374
     *
375
     * @return void
376
     */
377
    public function beforeCreate()
378
    {
379
        parent::beforeCreate();
380
        $random = new Random();
381
        $this->user_activation_email = $random->uuid();
382
383
        //this is only empty when creating a new user
384
        if (!$this->isFirstSignup()) {
385
            //confirm if the app reach its limit
386
            $this->isAtLimit();
387
        }
388
389
        //Assign admin role to the system if we dont get a specify role
390
        if (!$this->hasRole()) {
391
            $role = Roles::findFirstByName('Admins');
392
            $this->roles_id = $role->getId();
393
        }
394
    }
395
396
    /**
397
     * What the current company the users is logged in with
398
     * in this current session?
399
     *
400
     * @return integer
401
     */
402
    public function currentCompanyId(): int
403
    {
404
        return (int) $this->default_company;
405
    }
406
407
    /**
408
     * What the current company brach the users is logged in with
409
     * in this current session?
410
     *
411
     * @return integer
412
     */
413
    public function currentCompanyBranchId(): int
414
    {
415
        return (int) $this->default_company_branch;
416
    }
417
418
    /**
419
     * What to do after the creation of a new users
420
     *  - Assign default role.
421
     *
422
     * @return void
423
     */
424
    public function afterCreate()
425
    {
426
        //need to run it here, since we overwirte the default_company id and null this function objective
427
        $isFirstSignup = $this->isFirstSignup();
428
429
        /**
430
         * if we dont find the userdata di lets create it.
431
         * @todo this is not ideal lets fix it later
432
         */
433
        if (!$this->di->has('userData')) {
434
            $this->di->setShared('userData', $this);
435
        }
436
437
        /**
438
         * User signing up for a new app / plan
439
         * How do we know? well he doesnt have a default_company.
440
         */
441
        if ($isFirstSignup) {
442
            $company = new Companies();
443
            $company->name = $this->defaultCompanyName;
444
            $company->users_id = $this->getId();
445
446
            if (!$company->save()) {
447
                throw new Exception((string) current($company->getMessages()));
448
            }
449
450
            $this->default_company = $company->getId();
451
452
            if (!$this->update()) {
453
                throw new ServerErrorHttpException((string) current($this->getMessages()));
454
            }
455
456
            $this->stripe_id = $company->getPaymentGatewayCustomerId();
457
            $this->default_company_branch = $this->defaultCompany->branch->getId();
458
            $this->update();
459
460
        //update default subscription free trial
461
            //$company->app->subscriptions_id = $this->startFreeTrial()->getId();
462
            //$company->update();
463
        } else {
464
            //we have the company id
465
            if (empty($this->default_company_branch)) {
466
                $this->default_company_branch = $this->defaultCompany->branch->getId();
467
            }
468
469
            /**
470
             * asociate user to app.
471
             * @todo move most of the aftersave function to events
472
             */
473
            $this->di->getApp()->associate($this, $this->defaultCompany);
474
        }
475
476
        //Create new company associated company
477
        $this->defaultCompany->associate($this, $this->defaultCompany);
478
479
        //Insert record into user_roles
480
        $userRole = new UserRoles();
481
        $userRole->users_id = $this->id;
482
        $userRole->roles_id = $this->roles_id;
483
        $userRole->apps_id = $this->di->getApp()->getId();
484
        $userRole->companies_id = $this->default_company;
485
486
        if (!$userRole->save()) {
487
            throw new ServerErrorHttpException((string)current($userRole->getMessages()));
488
        }
489
490
        //update user activity when its not a empty user
491
        if (!$isFirstSignup) {
492
            $this->updateAppActivityLimit();
493
        }
494
    }
495
496
    /**
497
     * Upload Files.
498
     *
499
     * @todo move this to the baka class
500
     *
501
     * @return void
502
     */
503
    public function afterSave()
504
    {
505
        $this->associateFileSystem();
506
    }
507
508
    /**
509
     * Get the list of all the associated apps this users has.
510
     *
511
     * @return array
512
     */
513
    public function getAssociatedApps(): array
514
    {
515
        return array_map(function ($apps) {
516
            return $apps['apps_id'];
517
        }, $this->getApps(['columns' => 'apps_id', 'group' => 'apps_id'])->toArray());
518
    }
519
520
    /**
521
     * Get an array of the associates companies Ids.
522
     *
523
     * @return array
524
     */
525
    public function getAssociatedCompanies(): array
526
    {
527
        return array_map(function ($company) {
528
            return $company['companies_id'];
529
        }, $this->getCompanies(['columns' => 'companies_id'])->toArray());
530
    }
531
532
    /**
533
     * Get user by key.
534
     * @param string $userActivationEmail
535
     * @return Users
536
     */
537
    public static function getByUserActivationEmail(string $userActivationEmail): Users
538
    {
539
        return self::findFirst([
540
            'conditions' => 'user_activation_email = ?0 and user_active =?1 and is_deleted = 0',
541
            'bind' => [$userActivationEmail, 1],
542
        ]);
543
    }
544
545
    /**
546
     * Overwrite the relationship.
547
     *
548
     * @return void
549
     */
550
    public function getPhoto()
551
    {
552
        return $this->getAttachementByName('photo');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getAttachementByName('photo') also could return the type object which is incompatible with the documented return type void.
Loading history...
Deprecated Code introduced by
The function Canvas\Models\Users::getAttachementByName() has been deprecated: version 0.2 ( Ignorable by Annotation )

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

552
        return /** @scrutinizer ignore-deprecated */ $this->getAttachementByName('photo');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
553
    }
554
}
555