Completed
Pull Request — master (#551)
by Maxence
02:25
created

Circle::compareWith()   B

Complexity

Conditions 10
Paths 4

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 7.6666
c 0
b 0
f 0
cc 10
nc 4
nop 1

How to fix   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
declare(strict_types=1);
4
5
6
/**
7
 * Circles - Bring cloud-users closer together.
8
 *
9
 * This file is licensed under the Affero General Public License version 3 or
10
 * later. See the COPYING file.
11
 *
12
 * @author Maxence Lange <[email protected]>
13
 * @copyright 2021
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
31
32
namespace OCA\Circles\Model;
33
34
35
use daita\MySmallPhpTools\Db\Nextcloud\nc21\INC21QueryRow;
36
use daita\MySmallPhpTools\Exceptions\InvalidItemException;
37
use daita\MySmallPhpTools\IDeserializable;
38
use daita\MySmallPhpTools\Traits\Nextcloud\nc21\TNC21Deserialize;
39
use daita\MySmallPhpTools\Traits\TArrayTools;
40
use DateTime;
41
use JsonSerializable;
42
use OCA\Circles\Exceptions\CircleNotFoundException;
43
use OCA\Circles\Exceptions\OwnerNotFoundException;
44
45
46
/**
47
 * Class Circle
48
 *
49
 * ** examples of use of bitwise flags for members management:
50
 *      CFG_OPEN, CFG_REQUEST, CFG_INVITE, CFG_FRIEND
51
 *
52
 * - CFG_OPEN                             => everyone can enter. moderator can add members.
53
 * - CFG_OPEN | CFG_REQUEST               => anyone can initiate a request to join the circle, moderator can
54
 *                                           add members
55
 * - CFG_OPEN | CFG_INVITE                => every one can enter, moderator must send invitation.
56
 * - CFG_OPEN | CFG_INVITE | CFG_REQUEST  => every one send a request, moderator must send invitation.
57
 * - CFG_OPEN | CFG_FRIEND                => useless
58
 * - CFG_OPEN | CFG_FRIEND | *            => useless
59
 *
60
 * - CFG_CIRCLE                           => no one can enter, moderator can add members.
61
 *                                           default config, this is only for code readability.
62
 * - CFG_INVITE                           => no one can enter, moderator must send invitation.
63
 * - CFG_FRIEND                           => no one can enter, but all members can add new member.
64
 * - CFG_REQUEST                          => useless (use CFG_OPEN | CFG_REQUEST)
65
 * - CFG_FRIEND | CFG_REQUEST             => no one can join the circle, but all members can request a
66
 *                                           moderator to accept new member
67
 * - CFG_FRIEND | CFG_INVITE              => no one can join the circle, but all members can add new member.
68
 *                                           An invitation will be generated
69
 * - CFG_FRIEND | CFG_INVITE | CFG_REQUEST  => no one can join the circle, but all members can request a
70
 *                                             moderator to accept new member. An invitation will be generated
71
 *
72
 * @package OCA\Circles\Model
73
 */
74
class Circle extends ManagedModel implements IDeserializable, INC21QueryRow, JsonSerializable {
75
76
77
	use TArrayTools;
78
	use TNC21Deserialize;
79
80
81
	const TYPES_SHORT = 1;
82
	const TYPES_LONG = 2;
83
84
85
	// specific value
86
	const CFG_CIRCLE = 0;        // only for code readability. Circle is locked by default.
87
	const CFG_SINGLE = 1;        // Circle with only one single member.
88
	const CFG_PERSONAL = 2;      // Personal circle, only the owner can see it.
89
90
	// bitwise
91
	const CFG_SYSTEM = 4;         // System Circl (not managed by the official front-end). Meaning some config are limited
92
	const CFG_VISIBLE = 8;        // Visible to everyone, if not visible, people have to know its name to be able to find it
93
	const CFG_OPEN = 16;          // Circle is open, people can join
94
	const CFG_INVITE = 32;        // Adding a member generate an invitation that needs to be accepted
95
	const CFG_REQUEST = 64;       // Request to join Circles needs to be confirmed by a moderator
96
	const CFG_FRIEND = 128;       // Members of the circle can invite their friends
97
	const CFG_PROTECTED = 256;    // Password protected to join/request
98
	const CFG_NO_OWNER = 512;     // no owner, only members
99
	const CFG_HIDDEN = 1024;      // hidden from listing, but available as a share entity
100
	const CFG_BACKEND = 2048;     // Fully hidden, only backend Circles
101
	const CFG_ROOT = 4096;        // Circle cannot be inside another Circle
102
	const CFG_FEDERATED = 8192;   // Federated
103
104
105
	public static $DEF_CFG_MAX = 16383;
106
107
	public static $DEF_CFG = [
108
		1    => 'S|Single',
109
		2    => 'P|Personal',
110
		4    => 'Y|System',
111
		8    => 'V|Visible',
112
		16   => 'O|Open',
113
		32   => 'I|Invite',
114
		64   => 'JR|Join Request',
115
		128  => 'F|Friends',
116
		256  => 'PP|Password Protected',
117
		512  => 'NO|No Owner',
118
		1024 => 'H|Hidden',
119
		2048 => 'T|Backend',
120
		4096 => 'T|Root',
121
		8192 => 'F|Federated'
122
	];
123
124
	public static $DEF_CFG_CORE_FILTER = [
125
		1,
126
		2,
127
		4
128
	];
129
130
	public static $DEF_CFG_SYSTEM_FILTER = [
131
		512,
132
		1024,
133
		2048,
134
		4096
135
	];
136
137
138
	/** @var string */
139
	private $id = '';
140
141
	/** @var int */
142
	private $config = 0;
143
144
	/** @var int */
145
	private $type = 0;
146
147
	/** @var string */
148
	private $name = '';
149
150
	/** @var string */
151
	private $displayName = '';
152
153
	/** @var Member */
154
	private $owner;
155
156
	/** @var array */
157
	private $members = [];
158
159
	/** @var Member */
160
	private $initiator;
161
162
	/** @var array */
163
	private $settings = [];
164
165
	/** @var string */
166
	private $description = '';
167
168
	/** @var int */
169
	private $contactAddressBook = 0;
170
171
	/** @var string */
172
	private $contactGroupName = '';
173
174
	/** @var string */
175
	private $instance = '';
176
177
//	/** @var bool */
178
//	private $hidden = false;
179
180
	/** @var int */
181
	private $creation = 0;
182
183
184
	/** @var Circle[] */
185
	private $memberOf = null;
186
187
	private $completeJson = false;
188
189
190
	/**
191
	 * Circle constructor.
192
	 */
193
	public function __construct() {
194
	}
195
196
	/**
197
	 * @param string $id
198
	 *
199
	 * @return self
200
	 */
201
	public function setId(string $id): self {
202
		$this->id = $id;
203
204
		return $this;
205
	}
206
207
	/**
208
	 * @return string
209
	 */
210
	public function getId(): string {
211
		return $this->id;
212
	}
213
214
215
	/**
216
	 * @param int $config
217
	 *
218
	 * @return self
219
	 */
220
	public function setConfig(int $config): self {
221
		$this->config = $config;
222
223
//		$this->hidden = false;
224
//		foreach (array_keys(self::$DEF) as $def) {
225
//			if ($this->isType($def) && substr(self::$DEF[$def], 0, 1) === '*') {
226
//				$this->setHidden(true);
227
//				break;
228
//			}
229
//		}
230
231
		return $this;
232
	}
233
234
	/**
235
	 * @return int
236
	 */
237
	public function getConfig(): int {
238
		return $this->config;
239
	}
240
241
	/**
242
	 * @param int $flag
243
	 * @param int $test
244
	 *
245
	 * @return bool
246
	 */
247
	public function isConfig(int $flag, int $test = 0): bool {
248
		if ($test === 0) {
249
			$test = $this->getConfig();
250
		}
251
252
		return (($test & $flag) !== 0);
253
	}
254
255
	/**
256
	 * @param int $flag
257
	 */
258
	public function addConfig(int $flag): void {
259
		if (!$this->isConfig($flag)) {
260
			$this->config += $flag;
261
		}
262
	}
263
264
	/**
265
	 * @param int $flag
266
	 */
267
	public function remConfig(int $flag): void {
268
		if ($this->isConfig($flag)) {
269
			$this->config -= $flag;
270
		}
271
	}
272
273
274
	/**
275
	 * @param string $name
276
	 *
277
	 * @return self
278
	 */
279
	public function setName(string $name): self {
280
		$this->name = $name;
281
		if ($this->displayName === '') {
282
			$this->displayName = $name;
283
		}
284
285
		return $this;
286
	}
287
288
	/**
289
	 * @return string
290
	 */
291
	public function getName(): string {
292
		return $this->name;
293
	}
294
295
296
	/**
297
	 * @param string $displayName
298
	 *
299
	 * @return self
300
	 */
301
	public function setDisplayName(string $displayName): self {
302
		if ($displayName !== '') {
303
			$this->displayName = $displayName;
304
		}
305
306
		return $this;
307
	}
308
309
	/**
310
	 * @return string
311
	 */
312
	public function getDisplayName(): string {
313
		return $this->displayName;
314
	}
315
316
317
	/**
318
	 * @param Member $owner
319
	 *
320
	 * @return self
321
	 */
322
	public function setOwner(Member $owner): self {
323
		$this->owner = $owner;
324
325
		return $this;
326
	}
327
328
	/**
329
	 * @return Member
330
	 */
331
	public function getOwner(): Member {
332
		return $this->owner;
333
	}
334
335
	/**
336
	 * @return bool
337
	 */
338
	public function hasOwner(): bool {
339
		return ($this->owner !== null);
340
	}
341
342
343
	/**
344
	 * @param array $members
345
	 *
346
	 * @return self
347
	 */
348
	public function setMembers(array $members): self {
349
		$this->members = $members;
350
351
		return $this;
352
	}
353
354
	/**
355
	 * @return array
356
	 */
357
	public function getMembers(): array {
358
		if (empty($this->members)) {
359
			$this->getManager()->getMembers($this);
360
		}
361
362
		return $this->members;
363
	}
364
365
366
	/**
367
	 * @param Member $initiator
368
	 *
369
	 * @return Circle
370
	 */
371
	public function setInitiator(Member $initiator): self {
372
		$this->initiator = $initiator;
373
374
		return $this;
375
	}
376
377
	/**
378
	 * @return Member
379
	 */
380
	public function getInitiator(): Member {
381
		return $this->initiator;
382
	}
383
384
	/**
385
	 * @return bool
386
	 */
387
	public function hasInitiator(): bool {
388
		return ($this->initiator !== null);
389
	}
390
391
	/**
392
	 * @param string $instance
393
	 *
394
	 * @return Circle
395
	 */
396
	public function setInstance(string $instance): self {
397
		if ($this->isConfig(self::CFG_NO_OWNER)) {
398
			$this->instance = $instance;
399
		}
400
401
		return $this;
402
	}
403
404
	/**
405
	 * @return string
406
	 * @throws OwnerNotFoundException
407
	 */
408
	public function getInstance(): string {
409
		if ($this->isConfig(self::CFG_NO_OWNER)) {
410
			return $this->instance;
411
		}
412
413
		if (!$this->hasOwner()) {
414
			throw new OwnerNotFoundException('circle has no owner, or not set to have no owner');
415
		}
416
417
		return $this->getOwner()->getInstance();
418
	}
419
420
421
	/**
422
	 * @param array $settings
423
	 *
424
	 * @return self
425
	 */
426
	public function setSettings(array $settings): self {
427
		$this->settings = $settings;
428
429
		return $this;
430
	}
431
432
	/**
433
	 * @return array
434
	 */
435
	public function getSettings(): array {
436
		return $this->settings;
437
	}
438
439
440
	/**
441
	 * @param string $description
442
	 *
443
	 * @return self
444
	 */
445
	public function setDescription(string $description): self {
446
		$this->description = $description;
447
448
		return $this;
449
	}
450
451
	/**
452
	 * @return string
453
	 */
454
	public function getDescription(): string {
455
		return $this->description;
456
	}
457
458
459
	/**
460
	 * @param int $contactAddressBook
461
	 *
462
	 * @return self
463
	 */
464
	public function setContactAddressBook(int $contactAddressBook): self {
465
		$this->contactAddressBook = $contactAddressBook;
466
467
		return $this;
468
	}
469
470
	/**
471
	 * @return int
472
	 */
473
	public function getContactAddressBook(): int {
474
		return $this->contactAddressBook;
475
	}
476
477
478
	/**
479
	 * @param string $contactGroupName
480
	 *
481
	 * @return self
482
	 */
483
	public function setContactGroupName(string $contactGroupName): self {
484
		$this->contactGroupName = $contactGroupName;
485
486
		return $this;
487
	}
488
489
	/**
490
	 * @return string
491
	 */
492
	public function getContactGroupName(): string {
493
		return $this->contactGroupName;
494
	}
495
496
497
//	/**
498
//	 * @param bool $hidden
499
//	 *
500
//	 * @return Circle
501
//	 */
502
//	public function setHidden(bool $hidden): self {
503
//		$this->hidden = $hidden;
504
//
505
//		return $this;
506
//	}
507
//
508
//	/**
509
//	 * @return bool
510
//	 */
511
//	public function isHidden(): bool {
512
//		return $this->hidden;
513
//	}
514
515
516
	/**
517
	 * @param array $memberOf
518
	 *
519
	 * @return $this
520
	 */
521
	public function setMemberOf(array $memberOf): self {
522
		$this->memberOf = $memberOf;
523
524
		return $this;
525
	}
526
527
	/**
528
	 * @return Circle[]
529
	 */
530
	public function memberOf(): array {
531
		if ($this->memberOf === null) {
532
			$this->getManager()->memberOf($this);
533
		}
534
535
		return $this->memberOf;
536
	}
537
538
539
	/**
540
	 * @param int $creation
541
	 *
542
	 * @return self
543
	 */
544
	public function setCreation(int $creation): self {
545
		$this->creation = $creation;
546
547
		return $this;
548
	}
549
550
	/**
551
	 * @return int
552
	 */
553
	public function getCreation(): int {
554
		return $this->creation;
555
	}
556
557
558
	/**
559
	 * @param array $data
560
	 *
561
	 * @return $this
562
	 * @throws InvalidItemException
563
	 */
564
	public function import(array $data): IDeserializable {
565
		if ($this->get('id', $data) === '') {
566
			throw new InvalidItemException();
567
		}
568
569
		$this->setId($this->get('id', $data))
570
			 ->setName($this->get('name', $data))
571
			 ->setDisplayName($this->get('displayName', $data))
572
			 ->setConfig($this->getInt('config', $data))
573
			 ->setSettings($this->getArray('settings', $data))
574
//			 ->setContactAddressBook($this->get('contact_addressbook', $data))
575
//			 ->setContactGroupName($this->get('contact_groupname', $data))
576
			 ->setDescription($this->get('description', $data))
577
			 ->setCreation($this->getInt('creation', $data));
578
579
580
		try {
581
			/** @var Member $owner */
582
			$owner = $this->deserialize($this->getArray('owner', $data), Member::class);
583
			$this->setOwner($owner);
584
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
585
		}
586
587
		try {
588
			/** @var Member $initiator */
589
			$initiator = $this->deserialize($this->getArray('initiator', $data), Member::class);
590
			$this->setInitiator($initiator);
591
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
592
		}
593
594
		return $this;
595
	}
596
597
598
	/**
599
	 * @return array
600
	 */
601
	public function jsonSerialize(): array {
602
		$arr = [
603
			'id'          => $this->getId(),
604
			'name'        => $this->getName(),
605
			'displayName' => $this->getDisplayName(),
606
			'config'      => $this->getConfig(),
607
			'description' => $this->getDescription(),
608
			'settings'    => $this->getSettings(),
609
			//			'hidden'      => $this->isHidden(),
610
			'creation'    => $this->getCreation()
611
		];
612
613
		if ($this->hasOwner()) {
614
			$arr['owner'] = $this->getOwner();
615
		}
616
617
		if ($this->hasInitiator()) {
618
			$arr['initiator'] = $this->getInitiator();
619
		}
620
621
		if ($this->getManager()->isFullDetails()) {
622
			$arr['memberOf'] = $this->memberOf();
623
		}
624
625
		return array_filter($arr);
626
	}
627
628
629
	/**
630
	 * @param array $data
631
	 * @param string $prefix
632
	 *
633
	 * @return INC21QueryRow
634
	 * @throws CircleNotFoundException
635
	 */
636
	public function importFromDatabase(array $data, string $prefix = ''): INC21QueryRow {
637
		if (!array_key_exists($prefix . 'unique_id', $data)) {
638
			throw new CircleNotFoundException();
639
		}
640
641
		$this->setId($this->get($prefix . 'unique_id', $data))
642
			 ->setName($this->get($prefix . 'name', $data))
643
			 ->setDisplayName($this->get($prefix . 'alt_name', $data))
644
			 ->setConfig($this->getInt($prefix . 'config', $data))
645
			 ->setInstance($this->get($prefix . 'instance', $data))
646
			 ->setSettings($this->getArray($prefix . 'settings', $data))
647
			 ->setContactAddressBook($this->getInt($prefix . 'contact_addressbook', $data))
648
			 ->setContactGroupName($this->get($prefix . 'contact_groupname', $data))
649
			 ->setDescription($this->get($prefix . 'description', $data));
650
651
		$creation = $this->get($prefix . 'creation', $data);
652
		$this->setCreation(DateTime::createFromFormat('Y-m-d H:i:s', $creation)->getTimestamp());
653
654
		$this->getManager()->importOwnerFromDatabase($this, $data);
655
		$this->getManager()->importInitiatorFromDatabase($this, $data);
656
657
		return $this;
658
	}
659
660
661
	/**
662
	 * @param Circle $circle
663
	 *
664
	 * @return bool
665
	 * @throws OwnerNotFoundException
666
	 */
667
	public function compareWith(Circle $circle): bool {
668
		if ($this->getId() !== $circle->getId()
669
			|| $this->getInstance() !== $circle->getInstance()
670
			|| $this->getConfig() !== $circle->getConfig()) {
671
			return false;
672
		}
673
674
		if ($this->hasOwner()
675
			&& (!$circle->hasOwner()
676
				|| !$this->getOwner()->compareWith($circle->getOwner()))) {
677
			return false;
678
		}
679
680
		if ($this->hasInitiator()
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !($this->hasIniti...cle->getInitiator())));.
Loading history...
681
			&& (!$circle->hasInitiator()
682
				|| !$this->getInitiator()->compareWith($circle->getInitiator()))) {
683
			return false;
684
		}
685
686
		return true;
687
	}
688
689
690
	/**
691
	 * @param Circle $circle
692
	 * @param int $display
693
	 *
694
	 * @return array
695
	 */
696
	public static function getCircleTypes(Circle $circle, int $display = self::TYPES_LONG): array {
697
		$types = [];
698
		foreach (array_keys(Circle::$DEF_CFG) as $def) {
699
			if ($circle->isConfig($def)) {
700
				list($short, $long) = explode('|', Circle::$DEF_CFG[$def]);
701
				switch ($display) {
702
703
					case self::TYPES_SHORT:
704
						$types[] = $short;
705
						break;
706
707
					case self::TYPES_LONG:
708
						$types[] = $long;
709
						break;
710
				}
711
			}
712
		}
713
714
		return $types;
715
	}
716
717
}
718
719