Skip to content

Commit 53bc03b

Browse files
Merge pull request #609 from nextcloud/enh/606/invited-by
+invitedBy
2 parents f9cb432 + 827340b commit 53bc03b

18 files changed

Lines changed: 432 additions & 34 deletions

lib/AppInfo/Capabilities.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ private function getCapabilitiesCircleConstants(): array {
180180
Member::TYPE_APP => $this->l10n->t('Nextcloud App')
181181
],
182182
'extra' => [
183-
Member::APP_CIRCLES => 'Circles App'
183+
Member::APP_CIRCLES => 'Circles App',
184+
Member::APP_OCC => 'occ Command Line'
184185
]
185186
]
186187
];

lib/Command/MembersList.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
230230
$table = new Table($output);
231231
$table->setHeaders(
232232
[
233-
'Circle Id', 'Circle Name', 'Member Id', 'Single Id', 'Type', 'Source', 'Username',
234-
'Instance', 'Level'
233+
'Circle Id', 'Circle Name', 'Member Id', 'Single Id', 'Type', 'Source', 'Invited By',
234+
'Username', 'Instance', 'Level'
235235
]
236236
);
237237
$table->render();
@@ -251,6 +251,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
251251
$member->getSingleId(),
252252
Member::$TYPE[$member->getUserType()],
253253
$member->hasBasedOn() ? Circle::$DEF_SOURCE[$member->getBasedOn()->getSource()] : '',
254+
($member->hasInvitedBy()) ? $member->getInvitedBy()->getUserId()
255+
. $this->configService->displayInstance(
256+
$member->getInvitedBy()->getInstance(), true
257+
) : 'Unknown',
254258
($this->input->getOption('display-name')) ?
255259
$member->getBasedOn()->getDisplayName() : $member->getUserId(),
256260
$this->configService->displayInstance($member->getInstance()),

lib/Db/CoreQueryBuilder.php

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class CoreQueryBuilder extends NC22ExtendedQueryBuilder {
7171
const UPSTREAM_MEMBERSHIPS = 'upstreammemberships';
7272
const INHERITANCE_FROM = 'inheritancefrom';
7373
const INHERITED_BY = 'inheritedby';
74+
const INVITED_BY = 'invitedby';
7475
const MOUNT = 'mount';
7576
const MOUNTPOINT = 'mountpoint';
7677
const SHARE = 'share';
@@ -109,7 +110,7 @@ class CoreQueryBuilder extends NC22ExtendedQueryBuilder {
109110
self::MEMBER => [
110111
self::MEMBERSHIPS,
111112
self::INHERITANCE_FROM,
112-
self::CIRCLE => [
113+
self::CIRCLE => [
113114
self::OPTIONS => [
114115
'getData' => true
115116
],
@@ -123,10 +124,14 @@ class CoreQueryBuilder extends NC22ExtendedQueryBuilder {
123124
self::BASED_ON,
124125
self::INHERITED_BY => [
125126
self::MEMBERSHIPS
127+
],
128+
self::INVITED_BY => [
129+
self::OWNER,
130+
self::BASED_ON
126131
]
127132
]
128133
],
129-
self::BASED_ON => [
134+
self::BASED_ON => [
130135
self::OWNER,
131136
self::MEMBERSHIPS,
132137
self::INITIATOR => [
@@ -136,11 +141,15 @@ class CoreQueryBuilder extends NC22ExtendedQueryBuilder {
136141
]
137142
]
138143
],
139-
self::REMOTE => [
144+
self::REMOTE => [
140145
self::MEMBER,
141146
self::CIRCLE => [
142147
self::OWNER
143148
]
149+
],
150+
self::INVITED_BY => [
151+
self::OWNER,
152+
self::BASED_ON
144153
]
145154
],
146155
self::SHARE => [
@@ -690,6 +699,33 @@ public function leftJoinCircle(
690699
}
691700

692701

702+
/**
703+
* @param string $aliasMember
704+
*
705+
* @throws RequestBuilderException
706+
*/
707+
public function leftJoinInvitedBy(string $aliasMember): void {
708+
if ($this->getType() !== QueryBuilder::SELECT) {
709+
return;
710+
}
711+
712+
try {
713+
$aliasInvitedBy = $this->generateAlias($aliasMember, self::INVITED_BY);
714+
} catch (RequestBuilderException $e) {
715+
return;
716+
}
717+
718+
$expr = $this->expr();
719+
$this->generateCircleSelectAlias($aliasInvitedBy)
720+
->leftJoin(
721+
$aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasInvitedBy,
722+
$expr->eq($aliasMember . '.invited_by', $aliasInvitedBy . '.unique_id')
723+
);
724+
725+
$this->leftJoinOwner($aliasInvitedBy);
726+
}
727+
728+
693729
/**
694730
* @param string $aliasMember
695731
* @param IFederatedUser|null $initiator
@@ -811,7 +847,10 @@ public function leftJoinMember(
811847
*
812848
* @throws RequestBuilderException
813849
*/
814-
public function leftJoinInheritedMembers(string $alias, string $field = '', string $aliasInheritedBy = ''
850+
public function leftJoinInheritedMembers(
851+
string $alias,
852+
string $field = '',
853+
string $aliasInheritedBy = ''
815854
): void {
816855
$expr = $this->expr();
817856

lib/Db/MemberRequest.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ public function save(Member $member): void {
7070
->setValue('level', $qb->createNamedParameter($member->getLevel()))
7171
->setValue('status', $qb->createNamedParameter($member->getStatus()))
7272
->setValue('contact_id', $qb->createNamedParameter($member->getContactId()))
73-
->setValue('note', $qb->createNamedParameter($member->getNote()));
73+
->setValue('note', $qb->createNamedParameter(json_encode($member->getNotes())));
74+
75+
if ($member->hasInvitedBy()) {
76+
$qb->setValue('invited_by', $qb->createNamedParameter($member->getInvitedBy()->getSingleId()));
77+
}
7478

7579
$qb->execute();
7680
}
@@ -91,7 +95,7 @@ public function update(Member $member): void {
9195
->set('level', $qb->createNamedParameter($member->getLevel()))
9296
->set('status', $qb->createNamedParameter($member->getStatus()))
9397
->set('contact_id', $qb->createNamedParameter($member->getContactId()))
94-
->set('note', $qb->createNamedParameter($member->getNote()));
98+
->set('note', $qb->createNamedParameter(json_encode($member->getNotes())));
9599

96100
$qb->limitToCircleId($member->getCircleId());
97101
$qb->limitToUserId($member->getUserId());
@@ -212,6 +216,7 @@ public function getMembers(
212216

213217
$qb->setOptions([CoreQueryBuilder::MEMBER], ['canBeVisitorOnOpen' => true]);
214218
$qb->leftJoinCircle(CoreQueryBuilder::MEMBER, $initiator);
219+
$qb->leftJoinInvitedBy(CoreQueryBuilder::MEMBER);
215220

216221
if (!is_null($remoteInstance)) {
217222
$aliasCircle = $qb->generateAlias(CoreQueryBuilder::MEMBER, CoreQueryBuilder::CIRCLE);
@@ -351,7 +356,7 @@ public function searchFederatedUsers(string $needle): array {
351356
* @return FederatedUser[]|Member
352357
* @throws RequestBuilderException
353358
*/
354-
public function getAlternateSingleId(IFederatedUser $federatedUser) {
359+
public function getAlternateSingleId(IFederatedUser $federatedUser): array {
355360
$qb = $this->getMemberSelectSql();
356361
$qb->limitToSingleId($federatedUser->getSingleId());
357362

lib/FederatedItems/CircleCreate.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ public function manage(FederatedEvent $event): void {
125125
} catch (MemberNotFoundException $e) {
126126
}
127127

128+
if ($owner->hasInvitedBy()) {
129+
$owner->setNoteObj('invitedBy', $owner->getInvitedBy());
130+
}
131+
128132
$this->circleRequest->save($circle);
129133
$this->memberRequest->save($owner);
130134
$this->membershipService->onUpdate($owner->getSingleId());

lib/FederatedItems/MassiveMemberAdd.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public function verify(FederatedEvent $event): void {
7777

7878
foreach ($members as $member) {
7979
try {
80-
$filtered[] = $this->generateMember($circle, $member);
80+
$filtered[] = $this->generateMember($event, $circle, $member);
8181
} catch (Exception $e) {
8282
}
8383
}

lib/FederatedItems/SingleMemberAdd.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public function verify(FederatedEvent $event): void {
177177
$initiatorHelper = new MemberHelper($initiator);
178178
$initiatorHelper->mustBeModerator();
179179

180-
$member = $this->generateMember($circle, $member);
180+
$member = $this->generateMember($event, $circle, $member);
181181

182182
$event->setMembers([$member]);
183183
$event->setOutcome($member->jsonSerialize());
@@ -328,7 +328,7 @@ public function result(FederatedEvent $event, array $results): void {
328328
* @throws UserTypeNotFoundException
329329
* @throws RequestBuilderException
330330
*/
331-
protected function generateMember(Circle $circle, Member $member): Member {
331+
protected function generateMember(FederatedEvent $event, Circle $circle, Member $member): Member {
332332
try {
333333
if ($member->getSingleId() !== '') {
334334
$userId = $member->getSingleId() . '@' . $member->getInstance();
@@ -366,6 +366,10 @@ protected function generateMember(Circle $circle, Member $member): Member {
366366
$member->importFromIFederatedUser($federatedUser);
367367
$member->setCircleId($circle->getSingleId());
368368
$member->setCircle($circle);
369+
370+
$this->confirmPatron($event, $member);
371+
$member->setNoteObj('invitedBy', $member->getInvitedBy());
372+
369373
$this->manageMemberStatus($circle, $member);
370374

371375
$this->circleService->confirmCircleNotFull($circle);
@@ -387,6 +391,7 @@ protected function generateMember(Circle $circle, Member $member): Member {
387391
* @param Member $member
388392
*
389393
* @throws FederatedItemBadRequestException
394+
* @throws RequestBuilderException
390395
*/
391396
private function manageMemberStatus(Circle $circle, Member $member) {
392397
try {
@@ -428,6 +433,28 @@ private function manageMemberStatus(Circle $circle, Member $member) {
428433
}
429434

430435

436+
/**
437+
* @param FederatedEvent $event
438+
* @param Member $member
439+
*
440+
* @throws FederatedItemBadRequestException
441+
* @throws FederatedUserException
442+
* @throws RequestBuilderException
443+
*/
444+
private function confirmPatron(FederatedEvent $event, Member $member): void {
445+
if (!$member->hasInvitedBy()) {
446+
throw new FederatedItemBadRequestException(StatusCode::$MEMBER_ADD[129], 129);
447+
}
448+
449+
$patron = $member->getInvitedBy();
450+
if ($patron->getInstance() !== $event->getOrigin()) {
451+
throw new FederatedItemBadRequestException(StatusCode::$MEMBER_ADD[130], 130);
452+
}
453+
454+
$this->federatedUserService->confirmLocalSingleId($patron);
455+
}
456+
457+
431458
/**
432459
* @param Member $member
433460
*

lib/Migration/Version0022Date20220526113601.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
210210
'length' => 255
211211
]
212212
);
213+
$table->addColumn(
214+
'invited_by', 'string', [
215+
'notnull' => false,
216+
'length' => 31,
217+
]
218+
);
213219
$table->addColumn(
214220
'level', 'smallint', [
215221
'notnull' => true,
@@ -375,6 +381,11 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
375381
'notnull' => false
376382
]
377383
);
384+
$table->addColumn(
385+
'updated', 'datetime', [
386+
'notnull' => false,
387+
]
388+
);
378389
$table->addColumn(
379390
'creation', 'bigint', [
380391
'length' => 14,
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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 <maxence@artificial-owl.com>
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\Migration;
33+
34+
use Closure;
35+
use Doctrine\DBAL\Schema\SchemaException;
36+
use OCP\DB\ISchemaWrapper;
37+
use OCP\IDBConnection;
38+
use OCP\Migration\IOutput;
39+
use OCP\Migration\SimpleMigrationStep;
40+
41+
42+
/**
43+
* Class Version0022Date20220526113601
44+
*
45+
* @package OCA\Circles\Migration
46+
*/
47+
class Version0022Date20220601121545 extends SimpleMigrationStep {
48+
49+
50+
/**
51+
* @param IDBConnection $connection
52+
*/
53+
public function __construct(IDBConnection $connection) {
54+
}
55+
56+
57+
/**
58+
* @param IOutput $output
59+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
60+
* @param array $options
61+
*
62+
* @return null|ISchemaWrapper
63+
* @throws SchemaException
64+
*/
65+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
66+
/** @var ISchemaWrapper $schema */
67+
$schema = $schemaClosure();
68+
69+
if ($schema->hasTable('circles_event')) {
70+
$table = $schema->getTable('circles_event');
71+
if (!$table->hasColumn('updated')) {
72+
$table->addColumn(
73+
'updated', 'datetime', [
74+
'notnull' => false,
75+
]
76+
);
77+
}
78+
}
79+
80+
if ($schema->hasTable('circles_member')) {
81+
$table = $schema->getTable('circles_member');
82+
if (!$table->hasColumn('invited_by')) {
83+
$table->addColumn(
84+
'invited_by', 'string', [
85+
'notnull' => false,
86+
'length' => 31,
87+
]
88+
);
89+
}
90+
}
91+
92+
return $schema;
93+
}
94+
95+
}
96+

0 commit comments

Comments
 (0)