@amazeelabs/silverback-preview-link 1.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/drupal/silverback_preview_link/CHANGELOG.md +195 -0
- package/drupal/silverback_preview_link/README.md +30 -0
- package/drupal/silverback_preview_link/composer.json +13 -0
- package/drupal/silverback_preview_link/config/install/silverback_preview_link.settings.yml +3 -0
- package/drupal/silverback_preview_link/config/schema/silverback_preview_link.schema.yml +14 -0
- package/drupal/silverback_preview_link/css/modal.css +17 -0
- package/drupal/silverback_preview_link/js/copy.js +39 -0
- package/drupal/silverback_preview_link/silverback_preview_link.info.yml +12 -0
- package/drupal/silverback_preview_link/silverback_preview_link.install +6 -0
- package/drupal/silverback_preview_link/silverback_preview_link.libraries.yml +9 -0
- package/drupal/silverback_preview_link/silverback_preview_link.links.menu.yml +5 -0
- package/drupal/silverback_preview_link/silverback_preview_link.module +79 -0
- package/drupal/silverback_preview_link/silverback_preview_link.permissions.yml +9 -0
- package/drupal/silverback_preview_link/silverback_preview_link.routing.yml +40 -0
- package/drupal/silverback_preview_link/silverback_preview_link.services.yml +31 -0
- package/drupal/silverback_preview_link/src/Access/PreviewEnabledAccessCheck.php +80 -0
- package/drupal/silverback_preview_link/src/Access/PreviewLinkAccessCheck.php +49 -0
- package/drupal/silverback_preview_link/src/Authentication/Provider/PreviewToken.php +118 -0
- package/drupal/silverback_preview_link/src/Controller/PreviewController.php +83 -0
- package/drupal/silverback_preview_link/src/Entity/SilverbackPreviewLink.php +224 -0
- package/drupal/silverback_preview_link/src/Entity/SilverbackPreviewLinkInterface.php +110 -0
- package/drupal/silverback_preview_link/src/Form/PreviewLinkForm.php +320 -0
- package/drupal/silverback_preview_link/src/Form/SettingsForm.php +204 -0
- package/drupal/silverback_preview_link/src/Plugin/EntityReferenceSelection/SilverbackPreviewLinkSelection.php +25 -0
- package/drupal/silverback_preview_link/src/Plugin/Field/FieldWidget/PreviewLinkEntitiesWidget.php +103 -0
- package/drupal/silverback_preview_link/src/Plugin/Validation/Constraint/SilverbackPreviewLinkEntitiesUniqueConstraint.php +26 -0
- package/drupal/silverback_preview_link/src/Plugin/Validation/Constraint/SilverbackPreviewLinkEntitiesUniqueConstraintValidator.php +44 -0
- package/drupal/silverback_preview_link/src/PreviewLinkExpiry.php +55 -0
- package/drupal/silverback_preview_link/src/PreviewLinkHooks.php +61 -0
- package/drupal/silverback_preview_link/src/PreviewLinkHost.php +70 -0
- package/drupal/silverback_preview_link/src/PreviewLinkHostInterface.php +49 -0
- package/drupal/silverback_preview_link/src/PreviewLinkStorage.php +99 -0
- package/drupal/silverback_preview_link/src/PreviewLinkStorageInterface.php +19 -0
- package/drupal/silverback_preview_link/src/PreviewLinkUtility.php +27 -0
- package/drupal/silverback_preview_link/src/QRCodeWithLogo.php +84 -0
- package/drupal/silverback_preview_link/src/QRMarkupSVGWithLogo.php +51 -0
- package/drupal/silverback_preview_link/src/Routing/PreviewLinkRouteProvider.php +61 -0
- package/drupal/silverback_preview_link/src/images/amazee-labs_logo-square-green.svg +13 -0
- package/drupal/silverback_preview_link/templates/preview-link.html.twig +39 -0
- package/package.json +13 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_preview_link\Authentication\Provider;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Authentication\AuthenticationProviderInterface;
|
|
6
|
+
use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
|
|
7
|
+
use Drupal\Core\Session\AccountInterface;
|
|
8
|
+
use Drupal\silverback_preview_link\PreviewLinkExpiry;
|
|
9
|
+
use Drupal\silverback_preview_link\PreviewLinkStorageInterface;
|
|
10
|
+
use Symfony\Component\HttpFoundation\Request;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Authentication provider based on a token from a preview link.
|
|
14
|
+
*/
|
|
15
|
+
class PreviewToken implements AuthenticationProviderInterface {
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The preview link storage service.
|
|
19
|
+
*
|
|
20
|
+
* @var \Drupal\silverback_preview_link\PreviewLinkStorageInterface
|
|
21
|
+
*/
|
|
22
|
+
protected PreviewLinkStorageInterface $previewLinkStorage;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The page cache kill switch service.
|
|
26
|
+
*
|
|
27
|
+
* @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch
|
|
28
|
+
*/
|
|
29
|
+
protected KillSwitch $killSwitch;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The preview link expiry service.
|
|
33
|
+
* @var PreviewLinkExpiry
|
|
34
|
+
*/
|
|
35
|
+
protected PreviewLinkExpiry $previewLinkExpiry;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Constructs a new token authentication provider.
|
|
39
|
+
*/
|
|
40
|
+
public function __construct() {
|
|
41
|
+
// If we inject the entity entity type manager service, we get a circular
|
|
42
|
+
// dependency error, that is why we access the entity type manager service
|
|
43
|
+
// from here.
|
|
44
|
+
/** @var \Drupal\silverback_preview_link\PreviewLinkStorageInterface $storage */
|
|
45
|
+
$storage = \Drupal::entityTypeManager()->getStorage('silverback_preview_link');
|
|
46
|
+
$this->previewLinkStorage = $storage;
|
|
47
|
+
$this->killSwitch = \Drupal::service('page_cache_kill_switch');
|
|
48
|
+
$this->previewLinkExpiry = \Drupal::service('silverback_preview_link.link_expiry');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* {@inheritdoc}
|
|
53
|
+
*/
|
|
54
|
+
public function applies(Request $request) {
|
|
55
|
+
$previewToken = $this->getPreviewTokenFromRequest($request);
|
|
56
|
+
return !empty($previewToken);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* {@inheritdoc}
|
|
61
|
+
*/
|
|
62
|
+
public function authenticate(Request $request) {
|
|
63
|
+
// If we authenticate the user with a token, we do not want to cache the
|
|
64
|
+
// page.
|
|
65
|
+
$this->killSwitch->trigger();
|
|
66
|
+
$previewToken = $this->getPreviewTokenFromRequest($request);
|
|
67
|
+
return $this->getUserFromPreviewToken($previewToken);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Returns a preview token from the request (the preview_access_token query
|
|
72
|
+
* parameter).
|
|
73
|
+
*
|
|
74
|
+
* @param \Symfony\Component\HttpFoundation\Request $request
|
|
75
|
+
* @return bool|float|int|string|null
|
|
76
|
+
*/
|
|
77
|
+
protected function getPreviewTokenFromRequest(Request $request) {
|
|
78
|
+
return $request->query->get('preview_access_token');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Returns the User object for a given preview token.
|
|
83
|
+
*
|
|
84
|
+
* The preview link entity which corresponds to the token will be loaded and
|
|
85
|
+
* if there is a user associated with that token it will be returned.
|
|
86
|
+
*
|
|
87
|
+
* @param string $previewToken
|
|
88
|
+
* The preview token.
|
|
89
|
+
*
|
|
90
|
+
* @return \Drupal\Core\Session\AccountInterface|null
|
|
91
|
+
* The User object for the current user, or NULL for anonymous.
|
|
92
|
+
*/
|
|
93
|
+
protected function getUserFromPreviewToken(string $previewToken): AccountInterface|null {
|
|
94
|
+
$previewLink = $this->previewLinkStorage->loadByProperties(['token' => $previewToken]);
|
|
95
|
+
if (empty($previewLink)) {
|
|
96
|
+
return NULL;
|
|
97
|
+
}
|
|
98
|
+
// The loadByProperties() method returns the result as an array, so just
|
|
99
|
+
// take the first element.
|
|
100
|
+
$previewLink = reset($previewLink);
|
|
101
|
+
$referencedEntities = $previewLink->get('entities')->referencedEntities();
|
|
102
|
+
if (empty($referencedEntities)) {
|
|
103
|
+
return NULL;
|
|
104
|
+
}
|
|
105
|
+
$referencedUser = array_reduce($referencedEntities, function ($carry, $entity) {
|
|
106
|
+
if ($entity->getEntityTypeId() === 'user' && $entity->isActive()) {
|
|
107
|
+
$carry = $entity;
|
|
108
|
+
}
|
|
109
|
+
return $carry;
|
|
110
|
+
}, NULL);
|
|
111
|
+
// No active user entity reference found for the preview link, just return
|
|
112
|
+
// NULL.
|
|
113
|
+
if (empty($referencedUser)) {
|
|
114
|
+
return NULL;
|
|
115
|
+
}
|
|
116
|
+
return $referencedUser;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_preview_link\Controller;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Cache\CacheableResponse;
|
|
6
|
+
use Drupal\Core\Controller\ControllerBase;
|
|
7
|
+
use Drupal\silverback_preview_link\QRCodeWithLogo;
|
|
8
|
+
use Drupal\user\Entity\User;
|
|
9
|
+
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
10
|
+
|
|
11
|
+
class PreviewController extends ControllerBase {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Checks if the current user has access to the Preview app.
|
|
15
|
+
*/
|
|
16
|
+
public function hasAccess() {
|
|
17
|
+
/** @var \Drupal\Core\Session\AccountProxyInterface $userAccount */
|
|
18
|
+
$userAccount = $this->currentUser();
|
|
19
|
+
// Verify permission against User entity.
|
|
20
|
+
$userEntity = User::load($userAccount->id());
|
|
21
|
+
if ($userEntity->hasPermission('use external preview')) {
|
|
22
|
+
return new JsonResponse([
|
|
23
|
+
'access' => TRUE,
|
|
24
|
+
], 200);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return new JsonResponse([
|
|
28
|
+
'access' => FALSE,
|
|
29
|
+
], 403);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Skip Drupal authentication if there is a valid preview token.
|
|
35
|
+
*
|
|
36
|
+
* @todo: previously, this method used to also check if the preview access
|
|
37
|
+
* token has been attached to an entity (the entity type and entity ids were
|
|
38
|
+
* sent as parameters). This approach will change in the future (as part of a
|
|
39
|
+
* bigger refactoring) where preview links won't be attached to content
|
|
40
|
+
* entities anymore, so this method might change again.
|
|
41
|
+
*/
|
|
42
|
+
public function hasLinkAccess() {
|
|
43
|
+
$requestContent = \Drupal::request()->getContent();
|
|
44
|
+
$body = json_decode($requestContent, TRUE);
|
|
45
|
+
if (!empty($body['preview_access_token'])) {
|
|
46
|
+
try {
|
|
47
|
+
$storage = \Drupal::entityTypeManager()->getStorage('silverback_preview_link');
|
|
48
|
+
$previewLink = $storage->loadByProperties(['token' => $body['preview_access_token']]);
|
|
49
|
+
if (empty($previewLink)) {
|
|
50
|
+
return new JsonResponse([
|
|
51
|
+
'access' => FALSE,
|
|
52
|
+
], 403);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// @todo: optionally, we could also check if the link has expired.
|
|
56
|
+
// Expired links should be, however, deleted by the cron job. As this
|
|
57
|
+
// part of the code will probably suffer modifications during the next
|
|
58
|
+
// bigger refactoring (see the todo in the method's description), we
|
|
59
|
+
// will just check for now if the link simply exists.
|
|
60
|
+
return new JsonResponse([
|
|
61
|
+
'access' => TRUE,
|
|
62
|
+
], 200);
|
|
63
|
+
}
|
|
64
|
+
catch (\Exception $e) {
|
|
65
|
+
$this->getLogger('silverback_preview_link')->error($e->getMessage());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return new JsonResponse([
|
|
69
|
+
'access' => FALSE,
|
|
70
|
+
], 403);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Returns the QR SVG file.
|
|
75
|
+
*/
|
|
76
|
+
public function getQRCode(string $base64_url): CacheableResponse {
|
|
77
|
+
$decodedUrl = base64_decode(str_replace(['_'], ['/'], $base64_url));
|
|
78
|
+
$qrCode = new QRCodeWithLogo();
|
|
79
|
+
$result = $qrCode->getQRCode($decodedUrl);
|
|
80
|
+
return new CacheableResponse($result, 200, ['Content-Type' => 'image/svg+xml']);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types = 1);
|
|
4
|
+
|
|
5
|
+
namespace Drupal\silverback_preview_link\Entity;
|
|
6
|
+
|
|
7
|
+
use Drupal\Component\Assertion\Inspector;
|
|
8
|
+
use Drupal\Core\Entity\ContentEntityBase;
|
|
9
|
+
use Drupal\Core\Entity\EntityInterface;
|
|
10
|
+
use Drupal\Core\Entity\EntityStorageInterface;
|
|
11
|
+
use Drupal\Core\Entity\EntityTypeInterface;
|
|
12
|
+
use Drupal\Core\Field\BaseFieldDefinition;
|
|
13
|
+
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
|
14
|
+
use Drupal\Core\Url;
|
|
15
|
+
use Drupal\user\Entity\User;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Defines the Silverback preview link entity class.
|
|
19
|
+
*
|
|
20
|
+
* @ContentEntityType(
|
|
21
|
+
* id = "silverback_preview_link",
|
|
22
|
+
* label = @Translation("Preview Link"),
|
|
23
|
+
* base_table = "silverback_preview_link",
|
|
24
|
+
* handlers = {
|
|
25
|
+
* "views_data" = "Drupal\views\EntityViewsData",
|
|
26
|
+
* "storage" = "Drupal\silverback_preview_link\PreviewLinkStorage",
|
|
27
|
+
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
|
|
28
|
+
* "form" = {
|
|
29
|
+
* "silverback_preview_link" = "Drupal\silverback_preview_link\Form\PreviewLinkForm",
|
|
30
|
+
* "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
|
|
31
|
+
* },
|
|
32
|
+
* "route_provider" = {
|
|
33
|
+
* "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
|
|
34
|
+
* },
|
|
35
|
+
* },
|
|
36
|
+
* links = {
|
|
37
|
+
* "canonical" = "/admin/content/preview-link/{silverback_preview_link}",
|
|
38
|
+
* "delete-form" = "/admin/content/preview-link/{silverback_preview_link}/delete",
|
|
39
|
+
* },
|
|
40
|
+
* admin_permission = "administer site configuration",
|
|
41
|
+
* entity_keys = {
|
|
42
|
+
* "id" = "id"
|
|
43
|
+
* }
|
|
44
|
+
* )
|
|
45
|
+
*
|
|
46
|
+
* @property \Drupal\dynamic_entity_reference\Plugin\Field\FieldType\DynamicEntityReferenceFieldItemList $entities
|
|
47
|
+
*/
|
|
48
|
+
class SilverbackPreviewLink extends ContentEntityBase implements SilverbackPreviewLinkInterface {
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Keep track on whether we need a new token upon save.
|
|
52
|
+
*
|
|
53
|
+
* @var bool
|
|
54
|
+
*/
|
|
55
|
+
protected $needsNewToken = FALSE;
|
|
56
|
+
/**
|
|
57
|
+
* {@inheritDoc}
|
|
58
|
+
*/
|
|
59
|
+
public function postCreate(EntityStorageInterface $storage) {
|
|
60
|
+
parent::postCreate($storage);
|
|
61
|
+
// If there is a default preview user set, then we add that user to the list
|
|
62
|
+
// of entities attached to the link.
|
|
63
|
+
$state = \Drupal::state();
|
|
64
|
+
$default_preview_user = $state->get('silverback_preview_link.default_preview_user');
|
|
65
|
+
if ($default_preview_user) {
|
|
66
|
+
$this->addEntity(User::load($default_preview_user));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* {@inheritdoc}
|
|
72
|
+
*/
|
|
73
|
+
public function getUrl(EntityInterface $entity): Url {
|
|
74
|
+
return Url::fromRoute(sprintf('entity.%s.silverback_preview_link', $entity->getEntityTypeId()), [
|
|
75
|
+
$entity->getEntityTypeId() => $entity->id(),
|
|
76
|
+
'preview_token' => $this->getToken(),
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* {@inheritdoc}
|
|
82
|
+
*/
|
|
83
|
+
public function getToken(): string {
|
|
84
|
+
return $this->get('token')->value;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* {@inheritdoc}
|
|
89
|
+
*/
|
|
90
|
+
public function setToken($token) {
|
|
91
|
+
$this->set('token', $token);
|
|
92
|
+
// Add a second so our testing always works.
|
|
93
|
+
$this->set('generated_timestamp', time() + 1);
|
|
94
|
+
return $this;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* {@inheritdoc}
|
|
99
|
+
*/
|
|
100
|
+
public function regenerateToken($needs_new_token = FALSE): bool {
|
|
101
|
+
$current_value = $this->needsNewToken;
|
|
102
|
+
$this->needsNewToken = $needs_new_token;
|
|
103
|
+
return $current_value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* {@inheritdoc}
|
|
108
|
+
*/
|
|
109
|
+
public function getGeneratedTimestamp(): int {
|
|
110
|
+
return (int) $this->get('generated_timestamp')->value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* {@inheritdoc}
|
|
115
|
+
*/
|
|
116
|
+
public function getEntities(): array {
|
|
117
|
+
return $this->entities->referencedEntities();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* {@inheritdoc}
|
|
122
|
+
*/
|
|
123
|
+
public function setEntities(array $entities) {
|
|
124
|
+
assert(Inspector::assertAllObjects($entities, EntityInterface::class));
|
|
125
|
+
return $this->set('entities', $entities);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* {@inheritdoc}
|
|
130
|
+
*/
|
|
131
|
+
public function addEntity(EntityInterface $entity) {
|
|
132
|
+
$this->entities->appendItem($entity);
|
|
133
|
+
return $this;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* {@inheritdoc}
|
|
138
|
+
*/
|
|
139
|
+
public function getExpiry(): ?\DateTimeImmutable {
|
|
140
|
+
$value = $this->expiry->value ?? NULL;
|
|
141
|
+
if (!is_numeric($value)) {
|
|
142
|
+
return NULL;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return new \DateTimeImmutable('@' . $value);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* {@inheritdoc}
|
|
150
|
+
*/
|
|
151
|
+
public function setExpiry(\DateTimeInterface $expiry) {
|
|
152
|
+
return $this->set('expiry', $expiry->getTimestamp());
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* {@inheritdoc}
|
|
157
|
+
*/
|
|
158
|
+
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
|
159
|
+
$fields = parent::baseFieldDefinitions($entity_type);
|
|
160
|
+
$fields['token'] = BaseFieldDefinition::create('string')
|
|
161
|
+
->setLabel(t('Preview Token'))
|
|
162
|
+
->setDescription(t('A token that allows any user to view a preview of this entity.'))
|
|
163
|
+
->setRequired(TRUE);
|
|
164
|
+
|
|
165
|
+
$fields['entities'] = BaseFieldDefinition::create('dynamic_entity_reference')
|
|
166
|
+
->setLabel(t('Entities'))
|
|
167
|
+
->setDescription(t('The associated entities this preview link unlocks.'))
|
|
168
|
+
->setRequired(TRUE)
|
|
169
|
+
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
|
|
170
|
+
->addConstraint('SilverbackPreviewLinkEntitiesUniqueConstraint', [])
|
|
171
|
+
->setSettings(static::entitiesDefaultFieldSettings())
|
|
172
|
+
->setDisplayOptions('form', [
|
|
173
|
+
'type' => 'silverback_preview_link_entities_widget',
|
|
174
|
+
'weight' => 10,
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
$fields['generated_timestamp'] = BaseFieldDefinition::create('timestamp')
|
|
178
|
+
->setLabel(t('Generated Timestamp'))
|
|
179
|
+
->setDescription(t('The time the link was generated'))
|
|
180
|
+
->setRequired(TRUE);
|
|
181
|
+
|
|
182
|
+
$fields['expiry'] = BaseFieldDefinition::create('timestamp')
|
|
183
|
+
->setLabel(t('Time this Preview Link expires.'))
|
|
184
|
+
->setDescription(t('The time after which the preview link is no longer valid.'))
|
|
185
|
+
->setDefaultValueCallback(static::class . '::expiryDefaultValue')
|
|
186
|
+
->setRequired(TRUE);
|
|
187
|
+
|
|
188
|
+
return $fields;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Rewrites settings for 'entities' dynamic_entity_reference field.
|
|
193
|
+
*
|
|
194
|
+
* DynamicEntityReferenceItem::defaultFieldSettings doesn't receive any context,
|
|
195
|
+
* so we need to change the default handlers manually.
|
|
196
|
+
*/
|
|
197
|
+
public static function entitiesDefaultFieldSettings(): array {
|
|
198
|
+
$labels = \Drupal::service('entity_type.repository')->getEntityTypeLabels(TRUE);
|
|
199
|
+
$options = $labels[(string) t('Content', [], ['context' => 'Entity type group'])];
|
|
200
|
+
$settings = [
|
|
201
|
+
'exclude_entity_types' => TRUE,
|
|
202
|
+
'entity_type_ids' => [],
|
|
203
|
+
];
|
|
204
|
+
$settings += array_fill_keys(array_keys($options), [
|
|
205
|
+
'handler' => 'silverback_preview_link',
|
|
206
|
+
'handler_settings' => [],
|
|
207
|
+
]);
|
|
208
|
+
return $settings;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get default value for 'expiry' field.
|
|
213
|
+
*
|
|
214
|
+
* @return int<0, max>
|
|
215
|
+
* A timestamp.
|
|
216
|
+
*/
|
|
217
|
+
public static function expiryDefaultValue(): int {
|
|
218
|
+
$time = \Drupal::time();
|
|
219
|
+
/** @var \Drupal\silverback_preview_link\PreviewLinkExpiry $linkExpiry */
|
|
220
|
+
$linkExpiry = \Drupal::service('silverback_preview_link.link_expiry');
|
|
221
|
+
return max(0, $time->getRequestTime() + $linkExpiry->getLifetime());
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types = 1);
|
|
4
|
+
|
|
5
|
+
namespace Drupal\silverback_preview_link\Entity;
|
|
6
|
+
|
|
7
|
+
use Drupal\Core\Entity\ContentEntityInterface;
|
|
8
|
+
use Drupal\Core\Entity\EntityInterface;
|
|
9
|
+
use Drupal\Core\Url;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Interface for the preview link entity.
|
|
13
|
+
*/
|
|
14
|
+
interface SilverbackPreviewLinkInterface extends ContentEntityInterface {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The URL for this preview link for an entity.
|
|
18
|
+
*
|
|
19
|
+
* @param \Drupal\Core\Entity\EntityInterface $entity
|
|
20
|
+
* A host entity.
|
|
21
|
+
*
|
|
22
|
+
* @return \Drupal\Core\Url
|
|
23
|
+
* The url object.
|
|
24
|
+
*/
|
|
25
|
+
public function getUrl(EntityInterface $entity): Url;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Gets thew new token.
|
|
29
|
+
*
|
|
30
|
+
* @return string
|
|
31
|
+
* The token.
|
|
32
|
+
*/
|
|
33
|
+
public function getToken(): string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set the new token.
|
|
37
|
+
*
|
|
38
|
+
* @param string $token
|
|
39
|
+
* The new token.
|
|
40
|
+
*
|
|
41
|
+
* @return $this
|
|
42
|
+
* Return this for chaining.
|
|
43
|
+
*/
|
|
44
|
+
public function setToken(string $token);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Mark the entity needing a new token. Only updated upon save.
|
|
48
|
+
*
|
|
49
|
+
* @param bool $needs_new_token
|
|
50
|
+
* Tell this entity to generate a new token.
|
|
51
|
+
*
|
|
52
|
+
* @return bool
|
|
53
|
+
* TRUE if it was currently marked to generate otherwise FALSE.
|
|
54
|
+
*/
|
|
55
|
+
public function regenerateToken($needs_new_token = FALSE): bool;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Gets the timestamp stamp of when the token was generated.
|
|
59
|
+
*
|
|
60
|
+
* @return int
|
|
61
|
+
* The timestamp.
|
|
62
|
+
*/
|
|
63
|
+
public function getGeneratedTimestamp(): int;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get entities this preview link unlocks.
|
|
67
|
+
*
|
|
68
|
+
* Ideally preview link access is determined via PreviewLinkHost service.
|
|
69
|
+
*
|
|
70
|
+
* @return \Drupal\Core\Entity\EntityInterface[]
|
|
71
|
+
* Associated entities.
|
|
72
|
+
*/
|
|
73
|
+
public function getEntities(): array;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Set the entity this preview link unlocks.
|
|
77
|
+
*
|
|
78
|
+
* @return $this
|
|
79
|
+
* Return this for chaining.
|
|
80
|
+
*/
|
|
81
|
+
public function setEntities(array $entities);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Add an entity for this preview link to unlock.
|
|
85
|
+
*
|
|
86
|
+
* @return $this
|
|
87
|
+
* Return this for chaining.
|
|
88
|
+
*/
|
|
89
|
+
public function addEntity(EntityInterface $entity);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get expiration date.
|
|
93
|
+
*
|
|
94
|
+
* @return \DateTimeImmutable|null
|
|
95
|
+
* The expiration date, or NULL if not set.
|
|
96
|
+
*/
|
|
97
|
+
public function getExpiry(): ?\DateTimeImmutable;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Set the expiration date.
|
|
101
|
+
*
|
|
102
|
+
* @param \DateTimeInterface $expiry
|
|
103
|
+
* The expiration date.
|
|
104
|
+
*
|
|
105
|
+
* @return $this
|
|
106
|
+
* Return this for chaining.
|
|
107
|
+
*/
|
|
108
|
+
public function setExpiry(\DateTimeInterface $expiry);
|
|
109
|
+
|
|
110
|
+
}
|