Completed
Pull Request — master (#551)
by Maxence
01:59
created

Circle::isConfig()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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