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

Circle::setId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
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 $altName = '';
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
282
		return $this;
283
	}
284
285
	/**
286
	 * @return string
287
	 */
288
	public function getName(): string {
289
		return $this->name;
290
	}
291
292
293
	/**
294
	 * @param string $altName
295
	 *
296
	 * @return self
297
	 */
298
	public function setAltName(string $altName): self {
299
		$this->altName = $altName;
300
301
		return $this;
302
	}
303
304
	/**
305
	 * @return string
306
	 */
307
	public function getAltName(): string {
308
		return $this->altName;
309
	}
310
311
312
	/**
313
	 * @param Member $owner
314
	 *
315
	 * @return self
316
	 */
317
	public function setOwner(Member $owner): self {
318
		$this->owner = $owner;
319
320
		return $this;
321
	}
322
323
	/**
324
	 * @return Member
325
	 */
326
	public function getOwner(): Member {
327
		return $this->owner;
328
	}
329
330
	/**
331
	 * @return bool
332
	 */
333
	public function hasOwner(): bool {
334
		return ($this->owner !== null);
335
	}
336
337
338
	/**
339
	 * @param array $members
340
	 *
341
	 * @return self
342
	 */
343
	public function setMembers(array $members): self {
344
		$this->members = $members;
345
346
		return $this;
347
	}
348
349
	/**
350
	 * @return array
351
	 */
352
	public function getMembers(): array {
353
		if (empty($this->members)) {
354
			$this->getManager()->getMembers($this);
355
		}
356
357
		return $this->members;
358
	}
359
360
361
	/**
362
	 * @param Member $initiator
363
	 *
364
	 * @return Circle
365
	 */
366
	public function setInitiator(Member $initiator): self {
367
		$this->initiator = $initiator;
368
369
		return $this;
370
	}
371
372
	/**
373
	 * @return Member
374
	 */
375
	public function getInitiator(): Member {
376
		return $this->initiator;
377
	}
378
379
	/**
380
	 * @return bool
381
	 */
382
	public function hasInitiator(): bool {
383
		return ($this->initiator !== null);
384
	}
385
386
	/**
387
	 * @param string $instance
388
	 *
389
	 * @return Circle
390
	 */
391
	public function setInstance(string $instance): self {
392
		if ($this->isConfig(self::CFG_NO_OWNER)) {
393
			$this->instance = $instance;
394
		}
395
396
		return $this;
397
	}
398
399
	/**
400
	 * @return string
401
	 * @throws OwnerNotFoundException
402
	 */
403
	public function getInstance(): string {
404
		if ($this->isConfig(self::CFG_NO_OWNER)) {
405
			return $this->instance;
406
		}
407
408
		if (!$this->hasOwner()) {
409
			throw new OwnerNotFoundException('circle has no owner, or not set to have no owner');
410
		}
411
412
		return $this->getOwner()->getInstance();
413
	}
414
415
416
	/**
417
	 * @param array $settings
418
	 *
419
	 * @return self
420
	 */
421
	public function setSettings(array $settings): self {
422
		$this->settings = $settings;
423
424
		return $this;
425
	}
426
427
	/**
428
	 * @return array
429
	 */
430
	public function getSettings(): array {
431
		return $this->settings;
432
	}
433
434
435
	/**
436
	 * @param string $description
437
	 *
438
	 * @return self
439
	 */
440
	public function setDescription(string $description): self {
441
		$this->description = $description;
442
443
		return $this;
444
	}
445
446
	/**
447
	 * @return string
448
	 */
449
	public function getDescription(): string {
450
		return $this->description;
451
	}
452
453
454
	/**
455
	 * @param int $contactAddressBook
456
	 *
457
	 * @return self
458
	 */
459
	public function setContactAddressBook(int $contactAddressBook): self {
460
		$this->contactAddressBook = $contactAddressBook;
461
462
		return $this;
463
	}
464
465
	/**
466
	 * @return int
467
	 */
468
	public function getContactAddressBook(): int {
469
		return $this->contactAddressBook;
470
	}
471
472
473
	/**
474
	 * @param string $contactGroupName
475
	 *
476
	 * @return self
477
	 */
478
	public function setContactGroupName(string $contactGroupName): self {
479
		$this->contactGroupName = $contactGroupName;
480
481
		return $this;
482
	}
483
484
	/**
485
	 * @return string
486
	 */
487
	public function getContactGroupName(): string {
488
		return $this->contactGroupName;
489
	}
490
491
492
//	/**
493
//	 * @param bool $hidden
494
//	 *
495
//	 * @return Circle
496
//	 */
497
//	public function setHidden(bool $hidden): self {
498
//		$this->hidden = $hidden;
499
//
500
//		return $this;
501
//	}
502
//
503
//	/**
504
//	 * @return bool
505
//	 */
506
//	public function isHidden(): bool {
507
//		return $this->hidden;
508
//	}
509
510
511
	/**
512
	 * @param array $memberOf
513
	 *
514
	 * @return $this
515
	 */
516
	public function setMemberOf(array $memberOf): self {
517
		$this->memberOf = $memberOf;
518
519
		return $this;
520
	}
521
522
	/**
523
	 * @return Circle[]
524
	 */
525
	public function memberOf(): array {
526
		if ($this->memberOf === null) {
527
			$this->getManager()->memberOf($this);
528
		}
529
530
		return $this->memberOf;
531
	}
532
533
534
	/**
535
	 * @param int $creation
536
	 *
537
	 * @return self
538
	 */
539
	public function setCreation(int $creation): self {
540
		$this->creation = $creation;
541
542
		return $this;
543
	}
544
545
	/**
546
	 * @return int
547
	 */
548
	public function getCreation(): int {
549
		return $this->creation;
550
	}
551
552
553
	/**
554
	 * @param array $data
555
	 *
556
	 * @return $this
557
	 * @throws InvalidItemException
558
	 */
559
	public function import(array $data): IDeserializable {
560
		if ($this->get('id', $data) === '') {
561
			throw new InvalidItemException();
562
		}
563
564
		$this->setId($this->get('id', $data))
565
			 ->setName($this->get('name', $data))
566
			 ->setAltName($this->get('alt_name', $data))
567
			 ->setConfig($this->getInt('config', $data))
568
			 ->setSettings($this->getArray('settings', $data))
569
//			 ->setContactAddressBook($this->get('contact_addressbook', $data))
570
//			 ->setContactGroupName($this->get('contact_groupname', $data))
571
			 ->setDescription($this->get('description', $data))
572
			 ->setCreation($this->getInt('creation', $data));
573
574
575
		try {
576
			/** @var Member $owner */
577
			$owner = $this->deserialize($this->getArray('owner', $data), Member::class);
578
			$this->setOwner($owner);
579
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
580
		}
581
582
		try {
583
			/** @var Member $initiator */
584
			$initiator = $this->deserialize($this->getArray('initiator', $data), Member::class);
585
			$this->setInitiator($initiator);
586
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
587
		}
588
589
		return $this;
590
	}
591
592
593
	/**
594
	 * @return array
595
	 */
596
	public function jsonSerialize(): array {
597
		$arr = [
598
			'id'          => $this->getId(),
599
			'name'        => $this->getName(),
600
			'alt_name'    => $this->getAltName(),
601
			'config'      => $this->getConfig(),
602
			'description' => $this->getDescription(),
603
			'settings'    => $this->getSettings(),
604
			//			'hidden'      => $this->isHidden(),
605
			'creation'    => $this->getCreation()
606
		];
607
608
		if ($this->hasOwner()) {
609
			$arr['owner'] = $this->getOwner();
610
		}
611
612
		if ($this->hasInitiator()) {
613
			$arr['initiator'] = $this->getInitiator();
614
		}
615
616
		if ($this->getManager()->isFullDetails()) {
617
			$arr['memberOf'] = $this->memberOf();
618
		}
619
620
		return array_filter($arr);
621
	}
622
623
624
	/**
625
	 * @param array $data
626
	 * @param string $prefix
627
	 *
628
	 * @return INC21QueryRow
629
	 * @throws CircleNotFoundException
630
	 */
631
	public function importFromDatabase(array $data, string $prefix = ''): INC21QueryRow {
632
		if (!array_key_exists($prefix . 'unique_id', $data)) {
633
			throw new CircleNotFoundException();
634
		}
635
636
		$this->setId($this->get($prefix . 'unique_id', $data))
637
			 ->setName($this->get($prefix . 'name', $data))
638
			 ->setAltName($this->get($prefix . 'alt_name', $data))
639
			 ->setConfig($this->getInt($prefix . 'config', $data))
640
			 ->setInstance($this->get($prefix . 'instance', $data))
641
			 ->setSettings($this->getArray($prefix . 'settings', $data))
642
			 ->setContactAddressBook($this->getInt($prefix . 'contact_addressbook', $data))
643
			 ->setContactGroupName($this->get($prefix . 'contact_groupname', $data))
644
			 ->setDescription($this->get($prefix . 'description', $data));
645
646
		$creation = $this->get($prefix . 'creation', $data);
647
		$this->setCreation(DateTime::createFromFormat('Y-m-d H:i:s', $creation)->getTimestamp());
648
649
		$this->getManager()->importOwnerFromDatabase($this, $data);
650
		$this->getManager()->importInitiatorFromDatabase($this, $data);
651
652
		return $this;
653
	}
654
655
656
	/**
657
	 * @param Circle $circle
658
	 *
659
	 * @return bool
660
	 * @throws OwnerNotFoundException
661
	 */
662
	public function compareWith(Circle $circle): bool {
663
		if ($this->getId() !== $circle->getId()
664
			|| $this->getInstance() !== $circle->getInstance()
665
			|| $this->getConfig() !== $circle->getConfig()) {
666
			return false;
667
		}
668
669
		if ($this->hasOwner()
670
			&& (!$circle->hasOwner()
671
				|| !$this->getOwner()->compareWith($circle->getOwner()))) {
672
			return false;
673
		}
674
675
		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...
676
			&& (!$circle->hasInitiator()
677
				|| !$this->getInitiator()->compareWith($circle->getInitiator()))) {
678
			return false;
679
		}
680
681
		return true;
682
	}
683
684
685
	/**
686
	 * @param Circle $circle
687
	 * @param int $display
688
	 *
689
	 * @return array
690
	 */
691
	public static function getCircleTypes(Circle $circle, int $display = self::TYPES_LONG): array {
692
		$types = [];
693
		foreach (array_keys(Circle::$DEF_CFG) as $def) {
694
			if ($circle->isConfig($def)) {
695
				list($short, $long) = explode('|', Circle::$DEF_CFG[$def]);
696
				switch ($display) {
697
698
					case self::TYPES_SHORT:
699
						$types[] = $short;
700
						break;
701
702
					case self::TYPES_LONG:
703
						$types[] = $long;
704
						break;
705
				}
706
			}
707
		}
708
709
		return $types;
710
	}
711
712
}
713
714