Automatically update the created date for talks
Automatically update the created dates for talk nodes so that they match the most-future event. This means that the talks are ordered correctly on the Talks page.
This commit is contained in:
parent
fe5340d1c7
commit
7a9bf80a89
|
@ -73,6 +73,14 @@
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
<option name="TAB_SIZE" value="2" />
|
<option name="TAB_SIZE" value="2" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<groups>
|
||||||
|
<group>
|
||||||
|
<type>GETTERS_AND_SETTERS</type>
|
||||||
|
<order>KEEP</order>
|
||||||
|
</group>
|
||||||
|
</groups>
|
||||||
|
</arrangement>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
|
@ -9,10 +9,14 @@ symfony php vendor/bin/phpcs -n \
|
||||||
--exclude="Drupal.Commenting.ClassComment,Drupal.Commenting.FunctionComment" \
|
--exclude="Drupal.Commenting.ClassComment,Drupal.Commenting.FunctionComment" \
|
||||||
web/modules/custom
|
web/modules/custom
|
||||||
|
|
||||||
# symfony php vendor/bin/phpcs -ns \
|
symfony php vendor/bin/phpcs -n \
|
||||||
# --standard="Drupal,DrupalPractice" \
|
--standard="Drupal,DrupalPractice" \
|
||||||
# --extensions="php,module,inc,install,test,profile,theme" \
|
--extensions="php,module,inc,install,test,profile,theme" \
|
||||||
# --exclude="Drupal.Commenting.ClassComment,Drupal.Commenting.DocComment,Drupal.Commenting.FunctionComment,Drupal.NamingConventions.ValidFunctionName" \
|
--exclude="Drupal.Commenting.ClassComment,Drupal.Commenting.DocComment,Drupal.Commenting.FunctionComment,Drupal.NamingConventions.ValidFunctionName" \
|
||||||
# web/modules/custom/**/tests
|
web/modules/custom/**/tests
|
||||||
|
|
||||||
vendor/bin/phpstan analyze
|
vendor/bin/phpstan analyze
|
||||||
|
|
||||||
|
symfony php vendor/bin/phpunit \
|
||||||
|
-c web/core \
|
||||||
|
"$@"
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"drupal/config_ignore": "^2.2",
|
"drupal/config_ignore": "^2.2",
|
||||||
"drupal/core-composer-scaffold": "^8.8",
|
"drupal/core-composer-scaffold": "^8.8",
|
||||||
"drupal/core-recommended": "^8.8",
|
"drupal/core-recommended": "^8.8",
|
||||||
|
"drupal/hook_event_dispatcher": "^1.28",
|
||||||
"drupal/markdown": "^1.3",
|
"drupal/markdown": "^1.3",
|
||||||
"drupal/metatag": "^1.11",
|
"drupal/metatag": "^1.11",
|
||||||
"drupal/migrate_plus": "^5.0",
|
"drupal/migrate_plus": "^5.0",
|
||||||
|
|
104
composer.lock
generated
104
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "48b1c5f01bc8e5486f59462cb6b68de4",
|
"content-hash": "2800d415e7b235b84c6922ceaa045ed8",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "asm89/stack-cors",
|
"name": "asm89/stack-cors",
|
||||||
|
@ -2177,6 +2177,108 @@
|
||||||
"source": "https://git.drupalcode.org/project/entity_reference_revisions"
|
"source": "https://git.drupalcode.org/project/entity_reference_revisions"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "drupal/hook_event_dispatcher",
|
||||||
|
"version": "1.28.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.drupalcode.org/project/hook_event_dispatcher.git",
|
||||||
|
"reference": "8.x-1.28"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://ftp.drupal.org/files/projects/hook_event_dispatcher-8.x-1.28.zip",
|
||||||
|
"reference": "8.x-1.28",
|
||||||
|
"shasum": "a8e836015f27c87ad0584452cea3bfb02f0e4f2b"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"drupal/core": "^8 || ^9"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"drupal/coder": "8.3.1",
|
||||||
|
"drupal/core": "^8.8",
|
||||||
|
"drupal/eck": "^1.0@alpha",
|
||||||
|
"drupal/paragraphs": "^1.10",
|
||||||
|
"drupal/webform": "*",
|
||||||
|
"mockery/mockery": "^1.3",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.0",
|
||||||
|
"phpmd/phpmd": "2.7.0",
|
||||||
|
"phpunit/phpunit": "^7",
|
||||||
|
"squizlabs/php_codesniffer": "^3.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"drupal/paragraphs": "Enables the creation of paragraphs entities.",
|
||||||
|
"drupal/token": "Provides additional tokens not supported by core (most notably fields), as well as a UI for browsing tokens."
|
||||||
|
},
|
||||||
|
"type": "drupal-module",
|
||||||
|
"extra": {
|
||||||
|
"drupal": {
|
||||||
|
"version": "8.x-1.28",
|
||||||
|
"datestamp": "1588695401",
|
||||||
|
"security-coverage": {
|
||||||
|
"status": "covered",
|
||||||
|
"message": "Covered by Drupal's security advisory policy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Drupal\\hook_event_dispatcher\\": "src/",
|
||||||
|
"Drupal\\webform_event_dispatcher\\": "modules/webform_event_dispatcher/src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Drupal\\Tests\\hook_event_dispatcher\\": "tests/src/",
|
||||||
|
"Drupal\\Tests\\webform_event_dispatcher\\": "modules/webform_event_dispatcher/tests/src/",
|
||||||
|
"Drupal\\Tests\\": "vendor/drupal/core/tests/Drupal/Tests",
|
||||||
|
"Drupal\\TestTools\\": "vendor/drupal/core/tests/Drupal/TestTools",
|
||||||
|
"Drupal\\views\\": "vendor/drupal/core/modules/views/src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packages.drupal.org/8/downloads",
|
||||||
|
"scripts": {
|
||||||
|
"test": [
|
||||||
|
"@phplint",
|
||||||
|
"@phpunit",
|
||||||
|
"@phpcs",
|
||||||
|
"@phpmd"
|
||||||
|
],
|
||||||
|
"phplint": [
|
||||||
|
"vendor/bin/parallel-lint --exclude vendor/ --exclude .idea/ -e php,module,inc,install,profile,theme ."
|
||||||
|
],
|
||||||
|
"phpunit": [
|
||||||
|
"vendor/bin/phpunit --configuration=phpunit.xml"
|
||||||
|
],
|
||||||
|
"phpcs": [
|
||||||
|
"vendor/bin/phpcs --ignore=vendor/,.idea/ --standard=vendor/drupal/coder/coder_sniffer/Drupal/ruleset.xml --extensions=php,module,inc,install,profile,theme --report=full --warning-severity=0 ."
|
||||||
|
],
|
||||||
|
"phpcbf": [
|
||||||
|
"vendor/bin/phpcbf --ignore=vendor/,.idea/ --standard=vendor/drupal/coder/coder_sniffer/Drupal/ruleset.xml --extensions=php,module,inc,install,profile,theme ."
|
||||||
|
],
|
||||||
|
"phpmd": [
|
||||||
|
"vendor/bin/phpmd --exclude vendor/,.idea/ --suffixes php,module,inc,install,profile,theme . text phpmd.xml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"license": [
|
||||||
|
"GPL-2.0-or-later"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "pdenooijer",
|
||||||
|
"homepage": "https://www.drupal.org/user/3175437"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "robin.ingelbrecht",
|
||||||
|
"homepage": "https://www.drupal.org/user/2339074"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Dispatches events for several drupal core hooks.",
|
||||||
|
"homepage": "https://www.drupal.org/project/hook_event_dispatcher",
|
||||||
|
"support": {
|
||||||
|
"source": "https://git.drupalcode.org/project/hook_event_dispatcher"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "drupal/markdown",
|
"name": "drupal/markdown",
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
|
|
|
@ -17,6 +17,7 @@ module:
|
||||||
field_ui: 0
|
field_ui: 0
|
||||||
file: 0
|
file: 0
|
||||||
filter: 0
|
filter: 0
|
||||||
|
hook_event_dispatcher: 0
|
||||||
image: 0
|
image: 0
|
||||||
link: 0
|
link: 0
|
||||||
markdown: 0
|
markdown: 0
|
||||||
|
|
|
@ -7,6 +7,9 @@ parameters:
|
||||||
- *Test.php
|
- *Test.php
|
||||||
- *TestBase.php
|
- *TestBase.php
|
||||||
checkMissingIterableValueType: false
|
checkMissingIterableValueType: false
|
||||||
|
ignoreErrors:
|
||||||
|
- '#Call to an undefined method Drupal\\Core\\Entity\\EntityInterface::get()#'
|
||||||
|
- '#Call to an undefined method Drupal\\Core\\Entity\\EntityInterface::set()#'
|
||||||
includes:
|
includes:
|
||||||
- vendor/mglaman/phpstan-drupal/extension.neon
|
- vendor/mglaman/phpstan-drupal/extension.neon
|
||||||
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
|
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
|
||||||
|
|
|
@ -3,3 +3,7 @@ type: module
|
||||||
core: 8.x
|
core: 8.x
|
||||||
core_version_requirements: ^8 || ^9
|
core_version_requirements: ^8 || ^9
|
||||||
package: Custom
|
package: Custom
|
||||||
|
dependencies:
|
||||||
|
- drupal:node
|
||||||
|
- hook_event_dispatcher:hook_event_dispatcher
|
||||||
|
- paragraphs:paragraphs
|
||||||
|
|
4
web/modules/custom/custom/custom.services.yml
Normal file
4
web/modules/custom/custom/custom.services.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
services:
|
||||||
|
Drupal\custom\EventSubscriber\UpdateTalkCreatedDateOnSave:
|
||||||
|
tags:
|
||||||
|
- { name: event_subscriber }
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Drupal\custom\EventSubscriber;
|
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityInterface;
|
||||||
|
use Drupal\hook_event_dispatcher\Event\Entity\BaseEntityEvent;
|
||||||
|
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
|
||||||
|
use Drupal\paragraphs\ParagraphInterface;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the created date for a talk to be the last date that the talk is given.
|
||||||
|
*/
|
||||||
|
final class UpdateTalkCreatedDateOnSave implements EventSubscriberInterface {
|
||||||
|
|
||||||
|
public static function getSubscribedEvents() {
|
||||||
|
return [
|
||||||
|
HookEventDispatcherInterface::ENTITY_INSERT => 'entityInsertOrUpdate',
|
||||||
|
HookEventDispatcherInterface::ENTITY_UPDATE => 'entityInsertOrUpdate',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function entityInsertOrUpdate(BaseEntityEvent $event): void {
|
||||||
|
if ($event->getEntity()->getEntityTypeId() != 'node') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($event->getEntity()->bundle() != 'talk') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->updateCreatedDate($event->getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateCreatedDate(EntityInterface $talk): void {
|
||||||
|
if (!$eventDate = $this->findLatestEventDate($talk)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$talkDate = (new \DateTime($eventDate))->getTimestamp();
|
||||||
|
|
||||||
|
if ($talkDate == $talk->get('created')->getString()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$talk->set('created', $talkDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the date for the latest event.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
private function findLatestEventDate(EntityInterface $talk) {
|
||||||
|
return Collection::make($talk->get('field_events')->referencedEntities())
|
||||||
|
->map(fn(ParagraphInterface $event) => $event->get('field_date')
|
||||||
|
->getString())
|
||||||
|
->max();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ use Drupal\paragraphs\Entity\Paragraph;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A migrate destination for a talk node.
|
||||||
|
*
|
||||||
* @MigrateDestination(
|
* @MigrateDestination(
|
||||||
* id="opd_talk"
|
* id="opd_talk"
|
||||||
* )
|
* )
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.storage.node.field_events
|
||||||
|
- node.type.talk
|
||||||
|
- paragraphs.paragraphs_type.event
|
||||||
|
module:
|
||||||
|
- entity_reference_revisions
|
||||||
|
id: node.talk.field_events
|
||||||
|
field_name: field_events
|
||||||
|
entity_type: node
|
||||||
|
bundle: talk
|
||||||
|
label: Events
|
||||||
|
description: ''
|
||||||
|
required: false
|
||||||
|
translatable: false
|
||||||
|
default_value: { }
|
||||||
|
default_value_callback: ''
|
||||||
|
settings:
|
||||||
|
handler: 'default:paragraph'
|
||||||
|
handler_settings:
|
||||||
|
negate: 0
|
||||||
|
target_bundles:
|
||||||
|
event: event
|
||||||
|
target_bundles_drag_drop:
|
||||||
|
event:
|
||||||
|
enabled: true
|
||||||
|
weight: 2
|
||||||
|
field_type: entity_reference_revisions
|
|
@ -0,0 +1,20 @@
|
||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.storage.paragraph.field_date
|
||||||
|
- paragraphs.paragraphs_type.event
|
||||||
|
module:
|
||||||
|
- datetime
|
||||||
|
id: paragraph.event.field_date
|
||||||
|
field_name: field_date
|
||||||
|
entity_type: paragraph
|
||||||
|
bundle: event
|
||||||
|
label: Date
|
||||||
|
description: ''
|
||||||
|
required: true
|
||||||
|
translatable: false
|
||||||
|
default_value: { }
|
||||||
|
default_value_callback: ''
|
||||||
|
settings: { }
|
||||||
|
field_type: datetime
|
|
@ -0,0 +1,20 @@
|
||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
module:
|
||||||
|
- entity_reference_revisions
|
||||||
|
- node
|
||||||
|
- paragraphs
|
||||||
|
id: node.field_events
|
||||||
|
field_name: field_events
|
||||||
|
entity_type: node
|
||||||
|
type: entity_reference_revisions
|
||||||
|
settings:
|
||||||
|
target_type: paragraph
|
||||||
|
module: entity_reference_revisions
|
||||||
|
locked: false
|
||||||
|
cardinality: -1
|
||||||
|
translatable: true
|
||||||
|
indexes: { }
|
||||||
|
persist_with_no_fields: false
|
||||||
|
custom_storage: false
|
|
@ -0,0 +1,19 @@
|
||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
module:
|
||||||
|
- datetime
|
||||||
|
- paragraphs
|
||||||
|
id: paragraph.field_date
|
||||||
|
field_name: field_date
|
||||||
|
entity_type: paragraph
|
||||||
|
type: datetime
|
||||||
|
settings:
|
||||||
|
datetime_type: date
|
||||||
|
module: datetime
|
||||||
|
locked: false
|
||||||
|
cardinality: 1
|
||||||
|
translatable: true
|
||||||
|
indexes: { }
|
||||||
|
persist_with_no_fields: false
|
||||||
|
custom_storage: false
|
|
@ -0,0 +1,10 @@
|
||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies: { }
|
||||||
|
name: Talk
|
||||||
|
type: talk
|
||||||
|
description: ''
|
||||||
|
help: ''
|
||||||
|
new_revision: true
|
||||||
|
preview_mode: 1
|
||||||
|
display_submitted: false
|
|
@ -0,0 +1,9 @@
|
||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies: { }
|
||||||
|
id: event
|
||||||
|
label: Event
|
||||||
|
icon_uuid: null
|
||||||
|
icon_default: null
|
||||||
|
description: ''
|
||||||
|
behavior_plugins: { }
|
|
@ -0,0 +1,5 @@
|
||||||
|
name: Custom Test
|
||||||
|
type: module
|
||||||
|
core: 8.x
|
||||||
|
core_version_requirements: ^8 || ^9
|
||||||
|
hidden: true
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Drupal\Tests\custom\Kernel;
|
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityInterface;
|
||||||
|
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||||
|
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||||
|
use Drupal\node\Entity\Node;
|
||||||
|
use Drupal\paragraphs\Entity\Paragraph;
|
||||||
|
use Drupal\paragraphs\ParagraphInterface;
|
||||||
|
|
||||||
|
final class UpdatesTalkCreatedDateTest extends EntityKernelTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public static $modules = [
|
||||||
|
// Core.
|
||||||
|
'node',
|
||||||
|
'file',
|
||||||
|
'datetime',
|
||||||
|
|
||||||
|
// Contrib.
|
||||||
|
'entity_reference_revisions',
|
||||||
|
'paragraphs',
|
||||||
|
'hook_event_dispatcher',
|
||||||
|
|
||||||
|
// Custom.
|
||||||
|
'custom',
|
||||||
|
'custom_test',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function testCreatingNode() {
|
||||||
|
$eventDate = (new \DateTime('today'))->modify('+1 week');
|
||||||
|
$eventDateToFormat = $eventDate->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
|
||||||
|
$eventDateToTimestamp = $eventDate->getTimestamp();
|
||||||
|
|
||||||
|
$talk = $this->createTalk($eventDateToFormat);
|
||||||
|
|
||||||
|
$this->assertEqual($eventDateToTimestamp, $talk->get('created')
|
||||||
|
->getString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdatingNode() {
|
||||||
|
$talk = $this->createTalk();
|
||||||
|
|
||||||
|
$eventDate = (new \DateTime('today'))->modify('+1 week');
|
||||||
|
$eventDateToFormat = $eventDate->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
|
||||||
|
$eventDateToTimestamp = $eventDate->getTimestamp();
|
||||||
|
|
||||||
|
$event = $this->createEvent($eventDateToFormat);
|
||||||
|
$talk->set('field_events', [$event]);
|
||||||
|
$talk->save();
|
||||||
|
|
||||||
|
$this->assertEqual($eventDateToTimestamp, $talk->get('created')
|
||||||
|
->getString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->installEntitySchema('paragraph');
|
||||||
|
$this->installSchema('node', ['node_access']);
|
||||||
|
|
||||||
|
$this->installConfig(['custom_test']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createTalk(?string $eventDateToFormat = NULL): EntityInterface {
|
||||||
|
if ($eventDateToFormat) {
|
||||||
|
$event = $this->createEvent($eventDateToFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
$talk = Node::create([
|
||||||
|
'title' => 'TDD - Test Driven Drupal',
|
||||||
|
'type' => 'talk',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isset($event)) {
|
||||||
|
$talk->set('field_events', [$event]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$talk->save();
|
||||||
|
|
||||||
|
return $talk;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createEvent(string $eventDateToFormat): ParagraphInterface {
|
||||||
|
$event = Paragraph::create([
|
||||||
|
'field_date' => $eventDateToFormat,
|
||||||
|
'type' => 'event',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$event->save();
|
||||||
|
|
||||||
|
return $event;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue