Skip to content

Commit

Permalink
Merge pull request #4823 from carlobeltrame/copy-category
Browse files Browse the repository at this point in the history
Copy category from other category or from activity
  • Loading branch information
carlobeltrame authored Mar 30, 2024
2 parents 0805b25 + ccfdcfc commit 37794d5
Show file tree
Hide file tree
Showing 14 changed files with 530 additions and 38 deletions.
2 changes: 1 addition & 1 deletion api/src/Entity/Activity.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class Activity extends BaseEntity implements BelongsToCampInterface {
public ?Category $category = null;

/**
* Copy Contents from this Source-Activity.
* Copy contents from this source activity.
*/
#[ApiProperty(example: '/activities/1a2b3c4d')]
#[Groups(['create'])]
Expand Down
7 changes: 7 additions & 0 deletions api/src/Entity/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ class Category extends BaseEntity implements BelongsToCampInterface, CopyFromPro
#[ORM\OneToMany(targetEntity: Activity::class, mappedBy: 'category', orphanRemoval: true)]
public Collection $activities;

/**
* Copy contents from this source category or activity.
*/
#[ApiProperty(example: '/categories/1a2b3c4d')]
#[Groups(['create'])]
public null|Activity|Category $copyCategorySource;

/**
* The id of the category that was used as a template for creating this category. Internal for now, is
* not published through the API.
Expand Down
7 changes: 7 additions & 0 deletions api/src/State/CategoryCreateProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use App\Entity\ContentNode\ColumnLayout;
use App\Entity\ContentType;
use App\State\Util\AbstractPersistProcessor;
use App\Util\EntityMap;
use Doctrine\ORM\EntityManagerInterface;

/**
Expand Down Expand Up @@ -35,6 +36,12 @@ public function onBefore($data, Operation $operation, array $uriVariables = [],
$rootContentNode->data = ['columns' => [['slot' => '1', 'width' => 12]]];
$data->setRootContentNode($rootContentNode);

if (isset($data->copyCategorySource)) {
// CopyActivity Source is set -> copy it's content (rootContentNode)
$entityMap = new EntityMap();
$rootContentNode->copyFromPrototype($data->copyCategorySource->getRootContentNode(), $entityMap);
}

return $data;
}
}
101 changes: 99 additions & 2 deletions api/tests/Api/Categories/CreateCategoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ public function testCreateCategoryCreatesNewColumnLayoutAsRootContentNode() {

$this->assertResponseStatusCodeSame(201);
$newestColumnLayout = $this->getEntityManager()->getRepository(ContentNode::class)
->findBy(['contentType' => static::$fixtures['contentTypeColumnLayout']], ['createTime' => 'DESC'])[0]
->findBy(['contentType' => static::$fixtures['contentTypeColumnLayout'], 'instanceName' => null], ['createTime' => 'DESC'], 1)[0]
;
$this->assertJsonContains(['_links' => [
'rootContentNode' => ['href' => '/content_node/column_layouts/'.$newestColumnLayout->getId()],
'rootContentNode' => ['href' => $this->getIriFor($newestColumnLayout)],
]]);
}

Expand Down Expand Up @@ -456,6 +456,102 @@ public function testCreateCategoryValidatesInvalidNumberingStyle() {
]);
}

public function testCreateCategoryFromCopySourceValidatesAccess() {
static::createClientWithCredentials(['email' => static::$fixtures['user8memberOnlyInCamp2']->getEmail()])->request(
'POST',
'/categories',
['json' => $this->getExampleWritePayload(
[
'camp' => $this->getIriFor('camp2'),
'copyCategorySource' => $this->getIriFor('category1'),
]
)]
);

// No Access on category1 -> BadRequest
$this->assertResponseStatusCodeSame(400);
}

public function testCreateCategoryFromCopySourceWithinSameCamp() {
static::createClientWithCredentials()->request(
'POST',
'/categories',
['json' => $this->getExampleWritePayload(
[
'camp' => $this->getIriFor('camp1'),
'copyCategorySource' => $this->getIriFor('category1'),
],
)]
);

// Category created
$this->assertResponseStatusCodeSame(201);
}

public function testCreateCategoryFromCopySourceAcrossCamp() {
static::createClientWithCredentials()->request(
'POST',
'/categories',
['json' => $this->getExampleWritePayload(
[
'camp' => $this->getIriFor('camp2'),
'copyCategorySource' => $this->getIriFor('category1'),
],
)]
);

// Category created
$this->assertResponseStatusCodeSame(201);
}

public function testCreateCategoryFromCopySourceActivityValidatesAccess() {
static::createClientWithCredentials(['email' => static::$fixtures['user8memberOnlyInCamp2']->getEmail()])->request(
'POST',
'/categories',
['json' => $this->getExampleWritePayload(
[
'camp' => $this->getIriFor('camp2'),
'copyCategorySource' => $this->getIriFor('activity1'),
]
)]
);

// No Access on activity1 -> BadRequest
$this->assertResponseStatusCodeSame(400);
}

public function testCreateCategoryFromCopySourceActivityWithinSameCamp() {
static::createClientWithCredentials()->request(
'POST',
'/categories',
['json' => $this->getExampleWritePayload(
[
'camp' => $this->getIriFor('camp1'),
'copyCategorySource' => $this->getIriFor('activity1'),
],
)]
);

// Category created
$this->assertResponseStatusCodeSame(201);
}

public function testCreateCategoryFromCopySourceActivityAcrossCamp() {
static::createClientWithCredentials()->request(
'POST',
'/categories',
['json' => $this->getExampleWritePayload(
[
'camp' => $this->getIriFor('camp2'),
'copyCategorySource' => $this->getIriFor('activity1'),
],
)]
);

// Category created
$this->assertResponseStatusCodeSame(201);
}

/**
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
Expand Down Expand Up @@ -488,6 +584,7 @@ public function getExampleWritePayload($attributes = [], $except = []) {
Category::class,
Post::class,
array_merge([
'copyCategorySource' => null,
'camp' => $this->getIriFor('camp1'),
'preferredContentTypes' => [$this->getIriFor('contentTypeSafetyConcept')],
], $attributes),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ components:
format: iri-reference
type: string
copyActivitySource:
description: 'Copy Contents from this Source-Activity.'
description: 'Copy contents from this source activity.'
example: /activities/1a2b3c4d
format: iri-reference
type:
Expand Down Expand Up @@ -759,7 +759,7 @@ components:
format: iri-reference
type: string
copyActivitySource:
description: 'Copy Contents from this Source-Activity.'
description: 'Copy contents from this source activity.'
example: /activities/1a2b3c4d
format: iri-reference
type:
Expand Down Expand Up @@ -1168,7 +1168,7 @@ components:
format: iri-reference
type: string
copyActivitySource:
description: 'Copy Contents from this Source-Activity.'
description: 'Copy contents from this source activity.'
example: /activities/1a2b3c4d
format: iri-reference
type:
Expand Down Expand Up @@ -1607,7 +1607,7 @@ components:
format: iri-reference
type: string
copyActivitySource:
description: 'Copy Contents from this Source-Activity.'
description: 'Copy contents from this source activity.'
example: /activities/1a2b3c4d
format: iri-reference
type:
Expand Down Expand Up @@ -7938,6 +7938,13 @@ components:
maxLength: 8
pattern: '^(#[0-9a-zA-Z]{6})$'
type: string
copyCategorySource:
description: 'Copy contents from this source category or activity.'
example: /categories/1a2b3c4d
format: iri-reference
type:
- 'null'
- string
name:
description: 'The full name of the category.'
example: Lagersport
Expand Down Expand Up @@ -8296,6 +8303,13 @@ components:
maxLength: 8
pattern: '^(#[0-9a-zA-Z]{6})$'
type: string
copyCategorySource:
description: 'Copy contents from this source category or activity.'
example: /categories/1a2b3c4d
format: iri-reference
type:
- 'null'
- string
name:
description: 'The full name of the category.'
example: Lagersport
Expand Down Expand Up @@ -8690,6 +8704,13 @@ components:
maxLength: 8
pattern: '^(#[0-9a-zA-Z]{6})$'
type: string
copyCategorySource:
description: 'Copy contents from this source category or activity.'
example: /categories/1a2b3c4d
format: iri-reference
type:
- 'null'
- string
name:
description: 'The full name of the category.'
example: Lagersport
Expand Down Expand Up @@ -9062,6 +9083,13 @@ components:
maxLength: 8
pattern: '^(#[0-9a-zA-Z]{6})$'
type: string
copyCategorySource:
description: 'Copy contents from this source category or activity.'
example: /categories/1a2b3c4d
format: iri-reference
type:
- 'null'
- string
name:
description: 'The full name of the category.'
example: Lagersport
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/activity/ScheduleEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ export default {
async copyUrlToClipboard() {
try {
const res = await navigator.permissions.query({ name: 'clipboard-read' })
if (res.state == 'prompt') {
if (res.state === 'prompt') {
this.$refs.copyInfoDialog.open()
}
} catch {
Expand Down
Loading

0 comments on commit 37794d5

Please sign in to comment.