From 5adf725a7265c13df3a686f08259316fb349dc45 Mon Sep 17 00:00:00 2001 From: Andreas Hennings Date: Tue, 26 Nov 2024 19:25:47 +0100 Subject: [PATCH 1/3] Issue #61: Don't ignore indentation anymore in phpcs. --- phpcs.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/phpcs.xml b/phpcs.xml index b98b14a9..a0eca955 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -9,13 +9,6 @@ . - - - - - - - From 8695cbc6c85036e5c669ba75673a80cd9de0d230 Mon Sep 17 00:00:00 2001 From: Andreas Hennings Date: Wed, 27 Nov 2024 13:00:19 +0100 Subject: [PATCH 2/3] Issue #61: Fix indentation in php files. --- collabora_online.module | 224 +++---- collabora_online.theme.inc | 6 +- collabora_online.views.inc | 28 +- .../tests/src/Kernel/AccessTest.php | 604 +++++++++--------- .../tests/src/Kernel/PermissionTest.php | 202 +++--- src/CollaboraMediaPermissions.php | 116 ++-- src/Controller/ViewerController.php | 110 ++-- src/Controller/WopiController.php | 480 +++++++------- src/Cool/CoolRequest.php | 218 +++---- src/Cool/CoolUtils.php | 466 +++++++------- src/Form/ConfigForm.php | 168 ++--- .../Field/FieldFormatter/CoolPreview.php | 76 +-- src/Plugin/views/field/CollaboraEdit.php | 36 +- src/Plugin/views/field/CollaboraPreview.php | 36 +- .../CollaboraIntegrationTest.php | 124 ++-- tests/src/Kernel/CollaboraMediaAccessTest.php | 496 +++++++------- tests/src/Kernel/PermissionTest.php | 156 ++--- tests/src/Kernel/ViewsLinkFieldsTest.php | 284 ++++---- tests/src/Traits/MediaCreationTrait.php | 6 +- 19 files changed, 1918 insertions(+), 1918 deletions(-) diff --git a/collabora_online.module b/collabora_online.module index fd2cc452..75d34f77 100644 --- a/collabora_online.module +++ b/collabora_online.module @@ -33,42 +33,42 @@ use Drupal\media\MediaInterface; function collabora_online_theme($existing, $type, $theme, $path) { return [ - 'collabora_online' => [ - 'render element' => 'children', - 'template' => 'collabora-online', - 'variables' => [ - 'accessToken' => 'test', - 'accessTokenTtl' => '86400', - 'iFrameStyle' => 'width:95%;', - 'closebutton' => '', - 'allowfullscreen' => '', - 'wopiSrc' => 'http://localhost:9980/', - 'wopiClient' => 'https://localhost:9980/', - ], + 'collabora_online' => [ + 'render element' => 'children', + 'template' => 'collabora-online', + 'variables' => [ + 'accessToken' => 'test', + 'accessTokenTtl' => '86400', + 'iFrameStyle' => 'width:95%;', + 'closebutton' => '', + 'allowfullscreen' => '', + 'wopiSrc' => 'http://localhost:9980/', + 'wopiClient' => 'https://localhost:9980/', ], - // This is the template for the field preview. - 'collabora_online_preview' => [ - 'render element' => 'children', - 'template' => 'collabora-online-preview', - 'variables' => [ - 'editorUrl' => 'about:blank', - 'fileName' => '', - ], + ], + // This is the template for the field preview. + 'collabora_online_preview' => [ + 'render element' => 'children', + 'template' => 'collabora-online-preview', + 'variables' => [ + 'editorUrl' => 'about:blank', + 'fileName' => '', ], - // This is the template for the complete page with embedding. - 'collabora_online_full' => [ - 'template' => 'collabora-online-full', - 'variables' => [ - 'accessToken' => 'test', - 'accessTokenTtl' => '86400', - 'iFrameStyle' => '', - 'closebutton' => '', - 'allowfullscreen' => '', - 'wopiSrc' => '/wopi/files/123', - 'wopiClient' => 'https://localhost:9980/', - ], - 'file' => 'collabora_online.theme.inc', + ], + // This is the template for the complete page with embedding. + 'collabora_online_full' => [ + 'template' => 'collabora-online-full', + 'variables' => [ + 'accessToken' => 'test', + 'accessTokenTtl' => '86400', + 'iFrameStyle' => '', + 'closebutton' => '', + 'allowfullscreen' => '', + 'wopiSrc' => '/wopi/files/123', + 'wopiClient' => 'https://localhost:9980/', ], + 'file' => 'collabora_online.theme.inc', + ], ]; } @@ -79,42 +79,42 @@ function collabora_online_theme($existing, $type, $theme, $path) { * open the viewer/editor directly. */ function collabora_online_entity_operation(EntityInterface $entity) { - if (($entity->getEntityTypeId() != "media") || - ($entity->getSource()->getPluginId() != "file")) { - return []; - } + if (($entity->getEntityTypeId() != "media") || + ($entity->getSource()->getPluginId() != "file")) { + return []; + } - /** @var \Drupal\media\MediaInterface $media */ - $media = $entity; + /** @var \Drupal\media\MediaInterface $media */ + $media = $entity; - if (!$media->access('preview in collabora')) { - return []; - } + if (!$media->access('preview in collabora')) { + return []; + } - $file = CoolUtils::getFile($media); - $type = CoolUtils::getDocumentType($file); + $file = CoolUtils::getFile($media); + $type = CoolUtils::getDocumentType($file); - if ($type == NULL) { - return []; - } + if ($type == NULL) { + return []; + } - $entries = [ - 'collabora_online_view' => [ - 'title' => t("View in Collabora Online"), - 'weight' => 50, - 'url' => CoolUtils::getEditorUrl($media, FALSE), - ], - ]; + $entries = [ + 'collabora_online_view' => [ + 'title' => t("View in Collabora Online"), + 'weight' => 50, + 'url' => CoolUtils::getEditorUrl($media, FALSE), + ], + ]; - if (CoolUtils::canEdit($file) && $media->access('edit in collabora')) { - $entries['collabora_online_edit'] = [ - 'title' => t("Edit in Collabora Online"), - 'weight' => 50, - 'url' => CoolUtils::getEditorUrl($media, TRUE), - ]; - } + if (CoolUtils::canEdit($file) && $media->access('edit in collabora')) { + $entries['collabora_online_edit'] = [ + 'title' => t("Edit in Collabora Online"), + 'weight' => 50, + 'url' => CoolUtils::getEditorUrl($media, TRUE), + ]; + } - return $entries; + return $entries; } /** @@ -123,56 +123,56 @@ function collabora_online_entity_operation(EntityInterface $entity) { * Checks access for the new media operations provided by this module. */ function collabora_online_media_access(MediaInterface $media, string $operation, AccountInterface $account): AccessResultInterface { - $type = $media->bundle(); - switch ($operation) { - case 'preview in collabora': - if ($media->isPublished()) { - return AccessResult::allowedIfHasPermission($account, "preview $type in collabora") - ->addCacheableDependency($media); - } - $preview_own_permission = "preview own unpublished $type in collabora"; - $access_result = AccessResult::allowedIfHasPermission($account, $preview_own_permission) - ->addCacheableDependency($media); - if (!$access_result->isAllowed()) { - return $access_result; - } - // Use '==' because Drupal sometimes loads integers as strings. - $is_owner = ($account->id() && $account->id() == $media->getOwnerId()); - if ($is_owner) { - $access_result = AccessResult::allowed(); - } - else { - $access_result = AccessResult::neutral() - ->setReason("The user has the '$preview_own_permission' permission, but is not the owner of the media item."); - } - return $access_result - ->cachePerUser() - ->addCacheableDependency($media); + $type = $media->bundle(); + switch ($operation) { + case 'preview in collabora': + if ($media->isPublished()) { + return AccessResult::allowedIfHasPermission($account, "preview $type in collabora") + ->addCacheableDependency($media); + } + $preview_own_permission = "preview own unpublished $type in collabora"; + $access_result = AccessResult::allowedIfHasPermission($account, $preview_own_permission) + ->addCacheableDependency($media); + if (!$access_result->isAllowed()) { + return $access_result; + } + // Use '==' because Drupal sometimes loads integers as strings. + $is_owner = ($account->id() && $account->id() == $media->getOwnerId()); + if ($is_owner) { + $access_result = AccessResult::allowed(); + } + else { + $access_result = AccessResult::neutral() + ->setReason("The user has the '$preview_own_permission' permission, but is not the owner of the media item."); + } + return $access_result + ->cachePerUser() + ->addCacheableDependency($media); - case 'edit in collabora': - if ($account->hasPermission("edit any $type in collabora")) { - return AccessResult::allowed() - ->cachePerPermissions(); - } - $edit_own_permission = "edit own $type in collabora"; - $access_result = AccessResult::allowedIfHasPermission($account, $edit_own_permission); - if (!$access_result->isAllowed()) { - return $access_result; - } - // Use '==' because Drupal sometimes loads integers as strings. - $is_owner = ($account->id() && $account->id() == $media->getOwnerId()); - if (!$is_owner) { - $access_result = AccessResult::neutral() - ->setReason("The user has the '$edit_own_permission' permission, but is not the owner of the media item."); - } - else { - $access_result = AccessResult::allowed(); - } - return $access_result - ->cachePerUser() - ->addCacheableDependency($media); + case 'edit in collabora': + if ($account->hasPermission("edit any $type in collabora")) { + return AccessResult::allowed() + ->cachePerPermissions(); + } + $edit_own_permission = "edit own $type in collabora"; + $access_result = AccessResult::allowedIfHasPermission($account, $edit_own_permission); + if (!$access_result->isAllowed()) { + return $access_result; + } + // Use '==' because Drupal sometimes loads integers as strings. + $is_owner = ($account->id() && $account->id() == $media->getOwnerId()); + if (!$is_owner) { + $access_result = AccessResult::neutral() + ->setReason("The user has the '$edit_own_permission' permission, but is not the owner of the media item."); + } + else { + $access_result = AccessResult::allowed(); + } + return $access_result + ->cachePerUser() + ->addCacheableDependency($media); - default: - return AccessResult::neutral(); - } + default: + return AccessResult::neutral(); + } } diff --git a/collabora_online.theme.inc b/collabora_online.theme.inc index 3a9966c0..1dae081f 100644 --- a/collabora_online.theme.inc +++ b/collabora_online.theme.inc @@ -14,7 +14,7 @@ use Drupal\Core\Extension\ExtensionPathResolver; * Theme hook variables. */ function template_preprocess_collabora_online_full(array &$variables): void { - /** @var \Drupal\Core\Extension\ExtensionPathResolver $path_resolver */ - $path_resolver = \Drupal::service(ExtensionPathResolver::class); - $variables['module_path'] = $path_resolver->getPath('module', 'collabora_online'); + /** @var \Drupal\Core\Extension\ExtensionPathResolver $path_resolver */ + $path_resolver = \Drupal::service(ExtensionPathResolver::class); + $variables['module_path'] = $path_resolver->getPath('module', 'collabora_online'); } diff --git a/collabora_online.views.inc b/collabora_online.views.inc index 275511b5..01e9c2c6 100644 --- a/collabora_online.views.inc +++ b/collabora_online.views.inc @@ -11,18 +11,18 @@ declare(strict_types=1); * Implements hook_views_data_alter(). */ function collabora_online_views_data_alter(array &$data): void { - $data['media']['collabora_preview'] = [ - 'title' => t('Link to view in Collabora Online'), - 'group' => t('Media'), - 'field' => [ - 'id' => 'media_collabora_preview', - ], - ]; - $data['media']['collabora_edit'] = [ - 'title' => t('Link to edit in Collabora Online'), - 'group' => t('Media'), - 'field' => [ - 'id' => 'media_collabora_edit', - ], - ]; + $data['media']['collabora_preview'] = [ + 'title' => t('Link to view in Collabora Online'), + 'group' => t('Media'), + 'field' => [ + 'id' => 'media_collabora_preview', + ], + ]; + $data['media']['collabora_edit'] = [ + 'title' => t('Link to edit in Collabora Online'), + 'group' => t('Media'), + 'field' => [ + 'id' => 'media_collabora_edit', + ], + ]; } diff --git a/modules/collabora_online_group/tests/src/Kernel/AccessTest.php b/modules/collabora_online_group/tests/src/Kernel/AccessTest.php index b4d6e231..830837a6 100644 --- a/modules/collabora_online_group/tests/src/Kernel/AccessTest.php +++ b/modules/collabora_online_group/tests/src/Kernel/AccessTest.php @@ -17,316 +17,316 @@ */ class AccessTest extends GroupKernelTestBase { - use MediaTypeCreationTrait; - use UserCreationTrait; - use MediaCreationTrait; - use GroupRelationTrait; + use MediaTypeCreationTrait; + use UserCreationTrait; + use MediaCreationTrait; + use GroupRelationTrait; - /** - * {@inheritdoc} - */ - protected static $modules = [ - 'file', - 'media', - 'image', - 'group', - 'groupmedia', - 'collabora_online', - 'collabora_online_group', - 'user', - ]; + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'file', + 'media', + 'image', + 'group', + 'groupmedia', + 'collabora_online', + 'collabora_online_group', + 'user', + ]; - /** - * {@inheritdoc} - */ - protected function setUp(): void { - parent::setUp(); + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); - $this->installEntitySchema('file'); - $this->installEntitySchema('media'); - $this->installEntitySchema('group_role'); - $this->installSchema('file', ['file_usage']); - } + $this->installEntitySchema('file'); + $this->installEntitySchema('media'); + $this->installEntitySchema('group_role'); + $this->installSchema('file', ['file_usage']); + } - /** - * Tests that access to Collabora group permissions is handled. - */ - public function testCollaboraAccess(): void { - // Create group type, media type and enable plugin. - $group_type = $this->createGroupType(); - $group_role = $this->createGroupRole([ - 'group_type' => $group_type->id(), - 'scope' => PermissionScopeInterface::INSIDER_ID, - 'global_role' => RoleInterface::AUTHENTICATED_ID, - 'permissions' => [], - ]); - $this->createMediaType('file', ['id' => 'document']); - $this->createPluginRelation($group_type, 'group_media:document', [ - 'group_cardinality' => 0, - 'entity_cardinality' => 1, - 'use_creation_wizard' => FALSE, - ]); + /** + * Tests that access to Collabora group permissions is handled. + */ + public function testCollaboraAccess(): void { + // Create group type, media type and enable plugin. + $group_type = $this->createGroupType(); + $group_role = $this->createGroupRole([ + 'group_type' => $group_type->id(), + 'scope' => PermissionScopeInterface::INSIDER_ID, + 'global_role' => RoleInterface::AUTHENTICATED_ID, + 'permissions' => [], + ]); + $this->createMediaType('file', ['id' => 'document']); + $this->createPluginRelation($group_type, 'group_media:document', [ + 'group_cardinality' => 0, + 'entity_cardinality' => 1, + 'use_creation_wizard' => FALSE, + ]); - // Add group, media and relation between both. - $group = $this->createGroup(['type' => $group_type->id()]); - $media = $this->createMediaEntity('document'); - $group->addRelationship($media, 'group_media:document'); + // Add group, media and relation between both. + $group = $this->createGroup(['type' => $group_type->id()]); + $media = $this->createMediaEntity('document'); + $group->addRelationship($media, 'group_media:document'); - // Iterate over each scenario. - foreach ($this->getTestScenarios() as $scenario_name => $scenario) { - // Apply status to media. - $media->set('status', $scenario['status'])->save(); - // Set the current permissions for the existing role. - $group_role->set('permissions', $scenario['group_permissions'])->save(); - // Create the user with the given permissions and as member of the - // group. - $user = $this->createUser($scenario['permissions']); - $group->addMember($user); - // Set user as owner if the scope is 'own'. - $owner = $scenario['scope'] === 'own' ? $user->id() : 0; - $media->setOwnerId($owner)->save(); + // Iterate over each scenario. + foreach ($this->getTestScenarios() as $scenario_name => $scenario) { + // Apply status to media. + $media->set('status', $scenario['status'])->save(); + // Set the current permissions for the existing role. + $group_role->set('permissions', $scenario['group_permissions'])->save(); + // Create the user with the given permissions and as member of the + // group. + $user = $this->createUser($scenario['permissions']); + $group->addMember($user); + // Set user as owner if the scope is 'own'. + $owner = $scenario['scope'] === 'own' ? $user->id() : 0; + $media->setOwnerId($owner)->save(); - // Check access. - $this->assertEquals( - $scenario['result'], - $media->access($scenario['operation'], $user), - sprintf('Access check failed for scenario: "%s"', $scenario_name) - ); - } + // Check access. + $this->assertEquals( + $scenario['result'], + $media->access($scenario['operation'], $user), + sprintf('Access check failed for scenario: "%s"', $scenario_name) + ); } + } - /** - * Retrieves the scenarios to be tested. - * - * @return array - * An array of test scenarios. - */ - protected function getTestScenarios(): array { - // The scenario keys contains values used for each scenario: - // 'operation:status:scope:global_permission:group_permission'. - return [ - // Preview no permissions cases. - 'preview:published:any::' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => [], - 'operation' => 'preview in collabora', - 'status' => 1, - 'scope' => 'any', - ], - 'preview:published:own::' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => [], - 'operation' => 'preview in collabora', - 'status' => 1, - 'scope' => 'own', - ], - // The global permissions that would allow to preview, doesn't work - // in a media related to a group. - 'preview:published:any:preview:' => [ - 'result' => FALSE, - 'permissions' => ['preview document in collabora'], - 'group_permissions' => [], - 'operation' => 'preview in collabora', - 'status' => 1, - 'scope' => 'any', - ], - 'preview:published:own:preview:' => [ - 'result' => FALSE, - 'permissions' => ['preview document in collabora'], - 'group_permissions' => [], - 'operation' => 'preview in collabora', - 'status' => 1, - 'scope' => 'own', - ], - // User can only see published entities with the group preview - // permission. - 'preview:published:any::preview' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['preview group_media:document in collabora'], - 'operation' => 'preview in collabora', - 'status' => 1, - 'scope' => 'any', - ], - 'preview:published:own::preview' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['preview group_media:document in collabora'], - 'operation' => 'preview in collabora', - 'status' => 1, - 'scope' => 'own', - ], - 'preview:unpublished:any::preview' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => ['preview group_media:document in collabora'], - 'operation' => 'preview in collabora', - 'status' => 0, - 'scope' => 'any', - ], - 'preview:unpublished:own::preview' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => ['preview group_media:document in collabora'], - 'operation' => 'preview in collabora', - 'status' => 0, - 'scope' => 'own', - ], - // The global preview unpublished doesn't affect to medias related - // to a group. - 'preview:unpublished:own:preview_own_unpublished:' => [ - 'result' => FALSE, - 'permissions' => ['preview own unpublished document in collabora'], - 'group_permissions' => [], - 'operation' => 'preview in collabora', - 'status' => 0, - 'scope' => 'own', - ], - // The group permission to preview own unpublished permission allows - // to see only entities with such properties. - 'preview:published:any::preview_own_unpublished' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => ['preview own unpublished group_media:document in collabora'], - 'operation' => 'preview in collabora', - 'status' => 1, - 'scope' => 'any', - ], - 'preview:published:own::preview_own_unpublished' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => ['preview own unpublished group_media:document in collabora'], - 'operation' => 'preview in collabora', - 'status' => 1, - 'scope' => 'own', - ], - 'preview:unpublished:own::preview_own_unpublished' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['preview own unpublished group_media:document in collabora'], - 'operation' => 'preview in collabora', - 'status' => 0, - 'scope' => 'own', - ], - 'preview:unpublished:any::preview_own_unpublished' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => ['preview own unpublished group_media:document in collabora'], - 'operation' => 'preview in collabora', - 'status' => 0, - 'scope' => 'any', - ], - // Edit no permissions cases. - 'edit:published:any::' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => [], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'any', - ], - 'edit:published:own::' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => [], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'own', - ], - // The global permission doesn't grant access to edit in a group. - 'edit:published:any:edit_any:' => [ - 'result' => FALSE, - 'permissions' => ['edit any document in collabora'], - 'group_permissions' => [], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'any', - ], - 'edit:published:own:edit_any:' => [ - 'result' => FALSE, - 'permissions' => ['edit any document in collabora'], - 'group_permissions' => [], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'own', - ], - 'edit:published:own:edit_own:' => [ - 'result' => FALSE, - 'permissions' => ['edit own document in collabora'], - 'group_permissions' => [], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'own', - ], - // Only users with edit any permission in a group can edit all. - 'edit:published:any::edit_any' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['edit any group_media:document in collabora'], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'any', - ], - 'edit:published:own::edit_any' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['edit any group_media:document in collabora'], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'own', - ], - 'edit:unpublished:any::edit_any' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['edit any group_media:document in collabora'], - 'operation' => 'edit in collabora', - 'status' => 0, - 'scope' => 'any', - ], - 'edit:unpublished:own::edit_any' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['edit any group_media:document in collabora'], - 'operation' => 'edit in collabora', - 'status' => 0, - 'scope' => 'own', - ], - // Or edit own permission for the entities the user owns. - 'edit:published:own::edit_own' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['edit own group_media:document in collabora'], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'own', - ], - 'edit:unpublished:own::edit_own' => [ - 'result' => TRUE, - 'permissions' => [], - 'group_permissions' => ['edit own group_media:document in collabora'], - 'operation' => 'edit in collabora', - 'status' => 0, - 'scope' => 'own', - ], - 'edit:published:any::edit_own' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => ['edit own group_media:document in collabora'], - 'operation' => 'edit in collabora', - 'status' => 1, - 'scope' => 'any', - ], - 'edit:unpublished:any::edit_own' => [ - 'result' => FALSE, - 'permissions' => [], - 'group_permissions' => ['edit own group_media:document in collabora'], - 'operation' => 'edit in collabora', - 'status' => 0, - 'scope' => 'any', - ], - ]; - } + /** + * Retrieves the scenarios to be tested. + * + * @return array + * An array of test scenarios. + */ + protected function getTestScenarios(): array { + // The scenario keys contains values used for each scenario: + // 'operation:status:scope:global_permission:group_permission'. + return [ + // Preview no permissions cases. + 'preview:published:any::' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => [], + 'operation' => 'preview in collabora', + 'status' => 1, + 'scope' => 'any', + ], + 'preview:published:own::' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => [], + 'operation' => 'preview in collabora', + 'status' => 1, + 'scope' => 'own', + ], + // The global permissions that would allow to preview, doesn't work + // in a media related to a group. + 'preview:published:any:preview:' => [ + 'result' => FALSE, + 'permissions' => ['preview document in collabora'], + 'group_permissions' => [], + 'operation' => 'preview in collabora', + 'status' => 1, + 'scope' => 'any', + ], + 'preview:published:own:preview:' => [ + 'result' => FALSE, + 'permissions' => ['preview document in collabora'], + 'group_permissions' => [], + 'operation' => 'preview in collabora', + 'status' => 1, + 'scope' => 'own', + ], + // User can only see published entities with the group preview + // permission. + 'preview:published:any::preview' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['preview group_media:document in collabora'], + 'operation' => 'preview in collabora', + 'status' => 1, + 'scope' => 'any', + ], + 'preview:published:own::preview' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['preview group_media:document in collabora'], + 'operation' => 'preview in collabora', + 'status' => 1, + 'scope' => 'own', + ], + 'preview:unpublished:any::preview' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => ['preview group_media:document in collabora'], + 'operation' => 'preview in collabora', + 'status' => 0, + 'scope' => 'any', + ], + 'preview:unpublished:own::preview' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => ['preview group_media:document in collabora'], + 'operation' => 'preview in collabora', + 'status' => 0, + 'scope' => 'own', + ], + // The global preview unpublished doesn't affect to medias related + // to a group. + 'preview:unpublished:own:preview_own_unpublished:' => [ + 'result' => FALSE, + 'permissions' => ['preview own unpublished document in collabora'], + 'group_permissions' => [], + 'operation' => 'preview in collabora', + 'status' => 0, + 'scope' => 'own', + ], + // The group permission to preview own unpublished permission allows + // to see only entities with such properties. + 'preview:published:any::preview_own_unpublished' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => ['preview own unpublished group_media:document in collabora'], + 'operation' => 'preview in collabora', + 'status' => 1, + 'scope' => 'any', + ], + 'preview:published:own::preview_own_unpublished' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => ['preview own unpublished group_media:document in collabora'], + 'operation' => 'preview in collabora', + 'status' => 1, + 'scope' => 'own', + ], + 'preview:unpublished:own::preview_own_unpublished' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['preview own unpublished group_media:document in collabora'], + 'operation' => 'preview in collabora', + 'status' => 0, + 'scope' => 'own', + ], + 'preview:unpublished:any::preview_own_unpublished' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => ['preview own unpublished group_media:document in collabora'], + 'operation' => 'preview in collabora', + 'status' => 0, + 'scope' => 'any', + ], + // Edit no permissions cases. + 'edit:published:any::' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => [], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'any', + ], + 'edit:published:own::' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => [], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'own', + ], + // The global permission doesn't grant access to edit in a group. + 'edit:published:any:edit_any:' => [ + 'result' => FALSE, + 'permissions' => ['edit any document in collabora'], + 'group_permissions' => [], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'any', + ], + 'edit:published:own:edit_any:' => [ + 'result' => FALSE, + 'permissions' => ['edit any document in collabora'], + 'group_permissions' => [], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'own', + ], + 'edit:published:own:edit_own:' => [ + 'result' => FALSE, + 'permissions' => ['edit own document in collabora'], + 'group_permissions' => [], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'own', + ], + // Only users with edit any permission in a group can edit all. + 'edit:published:any::edit_any' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['edit any group_media:document in collabora'], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'any', + ], + 'edit:published:own::edit_any' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['edit any group_media:document in collabora'], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'own', + ], + 'edit:unpublished:any::edit_any' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['edit any group_media:document in collabora'], + 'operation' => 'edit in collabora', + 'status' => 0, + 'scope' => 'any', + ], + 'edit:unpublished:own::edit_any' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['edit any group_media:document in collabora'], + 'operation' => 'edit in collabora', + 'status' => 0, + 'scope' => 'own', + ], + // Or edit own permission for the entities the user owns. + 'edit:published:own::edit_own' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['edit own group_media:document in collabora'], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'own', + ], + 'edit:unpublished:own::edit_own' => [ + 'result' => TRUE, + 'permissions' => [], + 'group_permissions' => ['edit own group_media:document in collabora'], + 'operation' => 'edit in collabora', + 'status' => 0, + 'scope' => 'own', + ], + 'edit:published:any::edit_own' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => ['edit own group_media:document in collabora'], + 'operation' => 'edit in collabora', + 'status' => 1, + 'scope' => 'any', + ], + 'edit:unpublished:any::edit_own' => [ + 'result' => FALSE, + 'permissions' => [], + 'group_permissions' => ['edit own group_media:document in collabora'], + 'operation' => 'edit in collabora', + 'status' => 0, + 'scope' => 'any', + ], + ]; + } } diff --git a/modules/collabora_online_group/tests/src/Kernel/PermissionTest.php b/modules/collabora_online_group/tests/src/Kernel/PermissionTest.php index 4938b508..d7daa4bd 100644 --- a/modules/collabora_online_group/tests/src/Kernel/PermissionTest.php +++ b/modules/collabora_online_group/tests/src/Kernel/PermissionTest.php @@ -13,112 +13,112 @@ */ class PermissionTest extends GroupKernelTestBase { - use MediaTypeCreationTrait; - use GroupRelationTrait; + use MediaTypeCreationTrait; + use GroupRelationTrait; - /** - * {@inheritdoc} - */ - protected static $modules = [ - 'file', - 'media', - 'group', - 'groupmedia', - ]; + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'file', + 'media', + 'group', + 'groupmedia', + ]; - /** - * Tests that group permissions are properly created. - */ - public function testGroupPermissions(): void { - // Generate types: groups and medias. - $group_type_1 = $this->createGroupType(); - $group_type_2 = $this->createGroupType(); - $group_type_3 = $this->createGroupType(); - $this->createMediaType('file', ['id' => 'document']); - $this->createMediaType('file', ['id' => 'spreadsheet']); + /** + * Tests that group permissions are properly created. + */ + public function testGroupPermissions(): void { + // Generate types: groups and medias. + $group_type_1 = $this->createGroupType(); + $group_type_2 = $this->createGroupType(); + $group_type_3 = $this->createGroupType(); + $this->createMediaType('file', ['id' => 'document']); + $this->createMediaType('file', ['id' => 'spreadsheet']); - // Enable relation plugins in groups. - $this->createPluginRelation( - $group_type_1, - 'group_media:document', - [ - 'group_cardinality' => 0, - 'entity_cardinality' => 1, - 'use_creation_wizard' => FALSE, - ]); - $this->createPluginRelation( - $group_type_2, - 'group_media:document', - [ - 'group_cardinality' => 0, - 'entity_cardinality' => 1, - 'use_creation_wizard' => FALSE, - ]); - $this->createPluginRelation( - $group_type_2, - 'group_media:spreadsheet', - [ - 'group_cardinality' => 0, - 'entity_cardinality' => 1, - 'use_creation_wizard' => FALSE, - ]); + // Enable relation plugins in groups. + $this->createPluginRelation( + $group_type_1, + 'group_media:document', + [ + 'group_cardinality' => 0, + 'entity_cardinality' => 1, + 'use_creation_wizard' => FALSE, + ]); + $this->createPluginRelation( + $group_type_2, + 'group_media:document', + [ + 'group_cardinality' => 0, + 'entity_cardinality' => 1, + 'use_creation_wizard' => FALSE, + ]); + $this->createPluginRelation( + $group_type_2, + 'group_media:spreadsheet', + [ + 'group_cardinality' => 0, + 'entity_cardinality' => 1, + 'use_creation_wizard' => FALSE, + ]); - // Check that permissions are generated for the groups. - // Save current permissions. - /** @var \Drupal\group\Access\GroupPermissionHandlerInterface $permission_handler */ - $permission_handler = \Drupal::service('group.permissions'); - $permissions_before_1 = $permission_handler->getPermissionsByGroupType($group_type_1); - $permissions_before_2 = $permission_handler->getPermissionsByGroupType($group_type_2); - $permissions_before_3 = $permission_handler->getPermissionsByGroupType($group_type_3); + // Check that permissions are generated for the groups. + // Save current permissions. + /** @var \Drupal\group\Access\GroupPermissionHandlerInterface $permission_handler */ + $permission_handler = \Drupal::service('group.permissions'); + $permissions_before_1 = $permission_handler->getPermissionsByGroupType($group_type_1); + $permissions_before_2 = $permission_handler->getPermissionsByGroupType($group_type_2); + $permissions_before_3 = $permission_handler->getPermissionsByGroupType($group_type_3); - // Get permissions difference after enabling the module. - $this->enableModules(['collabora_online_group']); - $permission_handler = \Drupal::service('group.permissions'); - $permissions_after_1 = $permission_handler->getPermissionsByGroupType($group_type_1); - $new_permissions_1 = array_diff_key($permissions_after_1, $permissions_before_1); - ksort($new_permissions_1); - $permissions_after_2 = $permission_handler->getPermissionsByGroupType($group_type_2); - $new_permissions_2 = array_diff_key($permissions_after_2, $permissions_before_2); - ksort($new_permissions_2); - $permissions_after_3 = $permission_handler->getPermissionsByGroupType($group_type_3); - $new_permissions_3 = array_diff_key($permissions_after_3, $permissions_before_3); - ksort($new_permissions_3); + // Get permissions difference after enabling the module. + $this->enableModules(['collabora_online_group']); + $permission_handler = \Drupal::service('group.permissions'); + $permissions_after_1 = $permission_handler->getPermissionsByGroupType($group_type_1); + $new_permissions_1 = array_diff_key($permissions_after_1, $permissions_before_1); + ksort($new_permissions_1); + $permissions_after_2 = $permission_handler->getPermissionsByGroupType($group_type_2); + $new_permissions_2 = array_diff_key($permissions_after_2, $permissions_before_2); + ksort($new_permissions_2); + $permissions_after_3 = $permission_handler->getPermissionsByGroupType($group_type_3); + $new_permissions_3 = array_diff_key($permissions_after_3, $permissions_before_3); + ksort($new_permissions_3); - // The 'group_1' has only 'document' permissions. - $this->assertSame( - [ - 'edit any group_media:document in collabora' => 'Entity: Edit any media item in collabora', - 'edit own group_media:document in collabora' => 'Entity: Edit own media item in collabora', - 'preview group_media:document in collabora' => 'Entity: Preview published media item in collabora', - 'preview own unpublished group_media:document in collabora' => 'Entity: Preview own unpublished media item in collabora', - ], - array_map( - fn($permission) => (string) $permission['title'], - $new_permissions_1, - )); - // The 'group_2' has 'document' and 'spreadsheet' permissions. - $this->assertSame( - [ - 'edit any group_media:document in collabora' => 'Entity: Edit any media item in collabora', - 'edit any group_media:spreadsheet in collabora' => 'Entity: Edit any media item in collabora', - 'edit own group_media:document in collabora' => 'Entity: Edit own media item in collabora', - 'edit own group_media:spreadsheet in collabora' => 'Entity: Edit own media item in collabora', - 'preview group_media:document in collabora' => 'Entity: Preview published media item in collabora', - 'preview group_media:spreadsheet in collabora' => 'Entity: Preview published media item in collabora', - 'preview own unpublished group_media:document in collabora' => 'Entity: Preview own unpublished media item in collabora', - 'preview own unpublished group_media:spreadsheet in collabora' => 'Entity: Preview own unpublished media item in collabora', - ], - array_map( - fn($permission) => (string) $permission['title'], - $new_permissions_2, - )); - // The 'group_3' doesn't have any new permissions. - $this->assertSame( - [], - array_map( - fn($permission) => (string) $permission['title'], - $new_permissions_3, - )); - } + // The 'group_1' has only 'document' permissions. + $this->assertSame( + [ + 'edit any group_media:document in collabora' => 'Entity: Edit any media item in collabora', + 'edit own group_media:document in collabora' => 'Entity: Edit own media item in collabora', + 'preview group_media:document in collabora' => 'Entity: Preview published media item in collabora', + 'preview own unpublished group_media:document in collabora' => 'Entity: Preview own unpublished media item in collabora', + ], + array_map( + fn($permission) => (string) $permission['title'], + $new_permissions_1, + )); + // The 'group_2' has 'document' and 'spreadsheet' permissions. + $this->assertSame( + [ + 'edit any group_media:document in collabora' => 'Entity: Edit any media item in collabora', + 'edit any group_media:spreadsheet in collabora' => 'Entity: Edit any media item in collabora', + 'edit own group_media:document in collabora' => 'Entity: Edit own media item in collabora', + 'edit own group_media:spreadsheet in collabora' => 'Entity: Edit own media item in collabora', + 'preview group_media:document in collabora' => 'Entity: Preview published media item in collabora', + 'preview group_media:spreadsheet in collabora' => 'Entity: Preview published media item in collabora', + 'preview own unpublished group_media:document in collabora' => 'Entity: Preview own unpublished media item in collabora', + 'preview own unpublished group_media:spreadsheet in collabora' => 'Entity: Preview own unpublished media item in collabora', + ], + array_map( + fn($permission) => (string) $permission['title'], + $new_permissions_2, + )); + // The 'group_3' doesn't have any new permissions. + $this->assertSame( + [], + array_map( + fn($permission) => (string) $permission['title'], + $new_permissions_3, + )); + } } diff --git a/src/CollaboraMediaPermissions.php b/src/CollaboraMediaPermissions.php index 2eecaf31..77541d85 100644 --- a/src/CollaboraMediaPermissions.php +++ b/src/CollaboraMediaPermissions.php @@ -27,68 +27,68 @@ */ class CollaboraMediaPermissions implements ContainerInjectionInterface { - use AutowireTrait; - use BundlePermissionHandlerTrait; - use StringTranslationTrait; + use AutowireTrait; + use BundlePermissionHandlerTrait; + use StringTranslationTrait; - /** - * Constructor. - * - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager - * The entity type manager service. - */ - public function __construct( - protected readonly EntityTypeManagerInterface $entityTypeManager, - ) {} + /** + * Constructor. + * + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager + * The entity type manager service. + */ + public function __construct( + protected readonly EntityTypeManagerInterface $entityTypeManager, + ) {} - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static($container->get('entity_type.manager')); - } + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('entity_type.manager')); + } - /** - * Returns an array of media type permissions. - * - * @return array - * The media type permissions. - * - * @see \Drupal\user\PermissionHandlerInterface::getPermissions() - */ - public function mediaTypePermissions(): array { - // Generate media permissions for all media types. - $media_types = $this->entityTypeManager->getStorage('media_type')->loadMultiple(); - return $this->generatePermissions($media_types, [$this, 'buildPermissions']); - } + /** + * Returns an array of media type permissions. + * + * @return array + * The media type permissions. + * + * @see \Drupal\user\PermissionHandlerInterface::getPermissions() + */ + public function mediaTypePermissions(): array { + // Generate media permissions for all media types. + $media_types = $this->entityTypeManager->getStorage('media_type')->loadMultiple(); + return $this->generatePermissions($media_types, [$this, 'buildPermissions']); + } - /** - * Returns a list of permissions for a given media type. - * - * @param \Drupal\media\MediaTypeInterface $type - * The media type. - * - * @return array - * An associative array of permission names and descriptions. - */ - protected function buildPermissions(MediaTypeInterface $type) { - $type_id = $type->id(); - $type_params = ['%type_name' => $type->label()]; + /** + * Returns a list of permissions for a given media type. + * + * @param \Drupal\media\MediaTypeInterface $type + * The media type. + * + * @return array + * An associative array of permission names and descriptions. + */ + protected function buildPermissions(MediaTypeInterface $type) { + $type_id = $type->id(); + $type_params = ['%type_name' => $type->label()]; - return [ - "preview $type_id in collabora" => [ - 'title' => $this->t('%type_name: Preview published media file in Collabora', $type_params), - ], - "preview own unpublished $type_id in collabora" => [ - 'title' => $this->t('%type_name: Preview own unpublished media file in Collabora', $type_params), - ], - "edit own $type_id in collabora" => [ - 'title' => $this->t('%type_name: Edit own media file in Collabora', $type_params), - ], - "edit any $type_id in collabora" => [ - 'title' => $this->t('%type_name: Edit any media file in Collabora', $type_params), - ], - ]; - } + return [ + "preview $type_id in collabora" => [ + 'title' => $this->t('%type_name: Preview published media file in Collabora', $type_params), + ], + "preview own unpublished $type_id in collabora" => [ + 'title' => $this->t('%type_name: Preview own unpublished media file in Collabora', $type_params), + ], + "edit own $type_id in collabora" => [ + 'title' => $this->t('%type_name: Edit own media file in Collabora', $type_params), + ], + "edit any $type_id in collabora" => [ + 'title' => $this->t('%type_name: Edit any media file in Collabora', $type_params), + ], + ]; + } } diff --git a/src/Controller/ViewerController.php b/src/Controller/ViewerController.php index 083b9166..951f0ffd 100644 --- a/src/Controller/ViewerController.php +++ b/src/Controller/ViewerController.php @@ -24,68 +24,68 @@ */ class ViewerController extends ControllerBase { - /** - * The renderer service. - * - * @var \Drupal\Core\Render\RendererInterface - */ - private $renderer; + /** + * The renderer service. + * + * @var \Drupal\Core\Render\RendererInterface + */ + private $renderer; - /** - * The controller constructor. - * - * @param \Drupal\Core\Render\RendererInterface $renderer - * The renderer service. - */ - public function __construct(RendererInterface $renderer) { - $this->renderer = $renderer; - } + /** + * The controller constructor. + * + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer service. + */ + public function __construct(RendererInterface $renderer) { + $this->renderer = $renderer; + } - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container): self { - return new self( - $container->get('renderer'), - ); - } + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container): self { + return new self( + $container->get('renderer'), + ); + } - /** - * Returns a raw page for the iframe embed. - * - * @param \Drupal\media\Entity\Media $media - * Media entity. - * @param bool $edit - * TRUE to open Collabora Online in edit mode. - * FALSE to open Collabora Online in readonly mode. - * - * @return \Symfony\Component\HttpFoundation\Response - * Response suitable for iframe, without the usual page decorations. - */ - public function editor(Media $media, $edit = FALSE) { - $options = [ - 'closebutton' => 'true', - ]; + /** + * Returns a raw page for the iframe embed. + * + * @param \Drupal\media\Entity\Media $media + * Media entity. + * @param bool $edit + * TRUE to open Collabora Online in edit mode. + * FALSE to open Collabora Online in readonly mode. + * + * @return \Symfony\Component\HttpFoundation\Response + * Response suitable for iframe, without the usual page decorations. + */ + public function editor(Media $media, $edit = FALSE) { + $options = [ + 'closebutton' => 'true', + ]; - $render_array = CoolUtils::getViewerRender($media, $edit, $options); + $render_array = CoolUtils::getViewerRender($media, $edit, $options); - if (!$render_array || array_key_exists('error', $render_array)) { - $error_msg = 'Viewer error: ' . ($render_array ? $render_array['error'] : 'NULL'); - \Drupal::logger('cool')->error($error_msg); - return new Response( - $error_msg, - Response::HTTP_BAD_REQUEST, - ['content-type' => 'text/plain'] - ); - } + if (!$render_array || array_key_exists('error', $render_array)) { + $error_msg = 'Viewer error: ' . ($render_array ? $render_array['error'] : 'NULL'); + \Drupal::logger('cool')->error($error_msg); + return new Response( + $error_msg, + Response::HTTP_BAD_REQUEST, + ['content-type' => 'text/plain'] + ); + } - $render_array['#theme'] = 'collabora_online_full'; - $render_array['#attached']['library'][] = 'collabora_online/cool.frame'; + $render_array['#theme'] = 'collabora_online_full'; + $render_array['#attached']['library'][] = 'collabora_online/cool.frame'; - $response = new Response(); - $response->setContent($this->renderer->renderRoot($render_array)); + $response = new Response(); + $response->setContent($this->renderer->renderRoot($render_array)); - return $response; - } + return $response; + } } diff --git a/src/Controller/WopiController.php b/src/Controller/WopiController.php index 103316c8..1fca5d7b 100644 --- a/src/Controller/WopiController.php +++ b/src/Controller/WopiController.php @@ -26,256 +26,256 @@ */ class WopiController extends ControllerBase { - /** - * Creates a failure response that is understood by Collabora. - * - * @return \Symfony\Component\HttpFoundation\Response - * Response object. - */ - public static function permissionDenied(): Response { - return new Response( - 'Authentication failed.', - Response::HTTP_FORBIDDEN, - ['content-type' => 'text/plain'], - ); + /** + * Creates a failure response that is understood by Collabora. + * + * @return \Symfony\Component\HttpFoundation\Response + * Response object. + */ + public static function permissionDenied(): Response { + return new Response( + 'Authentication failed.', + Response::HTTP_FORBIDDEN, + ['content-type' => 'text/plain'], + ); + } + + /** + * Handles the WOPI 'info' request for a media entity. + * + * @param string $id + * Media id from url. + * @param \Symfony\Component\HttpFoundation\Request $request + * Request object with query parameters. + * + * @return \Symfony\Component\HttpFoundation\Response + * The response with file contents. + */ + public function wopiCheckFileInfo(string $id, Request $request) { + $token = $request->query->get('access_token'); + + $jwt_payload = CoolUtils::verifyTokenForId($token, $id); + if ($jwt_payload == NULL) { + return static::permissionDenied(); } - /** - * Handles the WOPI 'info' request for a media entity. - * - * @param string $id - * Media id from url. - * @param \Symfony\Component\HttpFoundation\Request $request - * Request object with query parameters. - * - * @return \Symfony\Component\HttpFoundation\Response - * The response with file contents. - */ - public function wopiCheckFileInfo(string $id, Request $request) { - $token = $request->query->get('access_token'); - - $jwt_payload = CoolUtils::verifyTokenForId($token, $id); - if ($jwt_payload == NULL) { - return static::permissionDenied(); - } - - /** @var \Drupal\media\MediaInterface|null $media */ - $media = \Drupal::entityTypeManager()->getStorage('media')->load($id); - if (!$media) { - return static::permissionDenied(); - } - - $file = CoolUtils::getFileById($id); - $mtime = date_create_immutable_from_format('U', $file->getChangedTime()); - // @todo What if the uid in the payload is not set? - // @todo What if $user is NULL? - $user = User::load($jwt_payload->uid); - $can_write = $jwt_payload->wri; - - if ($can_write && !$media->access('edit in collabora', $user)) { - \Drupal::logger('cool')->error('Token and user permissions do not match.'); - return static::permissionDenied(); - } - - $payload = [ - 'BaseFileName' => $file->getFilename(), - 'Size' => $file->getSize(), - 'LastModifiedTime' => $mtime->format('c'), - 'UserId' => $jwt_payload->uid, - 'UserFriendlyName' => $user->getDisplayName(), - 'UserExtraInfo' => [ - 'mail' => $user->getEmail(), - ], - 'UserCanWrite' => $can_write, - 'IsAdminUser' => $user->hasPermission('administer collabora instance'), - 'IsAnonymousUser' => $user->isAnonymous(), - ]; - - $user_picture = $user->user_picture?->entity; - if ($user_picture) { - /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */ - $file_url_generator = \Drupal::service('file_url_generator'); - $payload['UserExtraInfo']['avatar'] = $file_url_generator->generateAbsoluteString($user_picture->getFileUri()); - } - - $jsonPayload = json_encode($payload); - - $response = new Response( - $jsonPayload, - Response::HTTP_OK, - ['content-type' => 'application/json'] - ); - return $response; + /** @var \Drupal\media\MediaInterface|null $media */ + $media = \Drupal::entityTypeManager()->getStorage('media')->load($id); + if (!$media) { + return static::permissionDenied(); } - /** - * Handles the wopi "content" request for a media entity. - * - * @param string $id - * Media id from url. - * @param \Symfony\Component\HttpFoundation\Request $request - * Request object with query parameters. - * - * @return \Symfony\Component\HttpFoundation\Response - * The response with file contents. - */ - public function wopiGetFile(string $id, Request $request) { - $token = $request->query->get('access_token'); - - $jwt_payload = CoolUtils::verifyTokenForId($token, $id); - if ($jwt_payload == NULL) { - return static::permissionDenied(); - } - - $user = User::load($jwt_payload->uid); - $accountSwitcher = \Drupal::service('account_switcher'); - $accountSwitcher->switchTo($user); - - $file = CoolUtils::getFileById($id); - $mimetype = $file->getMimeType(); - - $response = new BinaryFileResponse( - $file->getFileUri(), - Response::HTTP_OK, - ['content-type' => $mimetype] - ); - $accountSwitcher->switchBack(); - return $response; + $file = CoolUtils::getFileById($id); + $mtime = date_create_immutable_from_format('U', $file->getChangedTime()); + // @todo What if the uid in the payload is not set? + // @todo What if $user is NULL? + $user = User::load($jwt_payload->uid); + $can_write = $jwt_payload->wri; + + if ($can_write && !$media->access('edit in collabora', $user)) { + \Drupal::logger('cool')->error('Token and user permissions do not match.'); + return static::permissionDenied(); } - /** - * Handles the wopi "save" request for a media entity. - * - * @param string $id - * Media id from url. - * @param \Symfony\Component\HttpFoundation\Request $request - * Request object with headers, query parameters and payload. - * - * @return \Symfony\Component\HttpFoundation\Response - * The response. - */ - public function wopiPutFile(string $id, Request $request) { - $token = $request->query->get('access_token'); - $timestamp = $request->headers->get('x-cool-wopi-timestamp'); - $modified_by_user = $request->headers->get('x-cool-wopi-ismodifiedbyuser') == 'true'; - $autosave = $request->headers->get('x-cool-wopi-isautosave') == 'true'; - $exitsave = $request->headers->get('x-cool-wopi-isexitsave') == 'true'; - - $jwt_payload = CoolUtils::verifyTokenForId($token, $id); - if ($jwt_payload == NULL || !$jwt_payload->wri) { - return static::permissionDenied(); - } - - $fs = \Drupal::service('file_system'); - - $media = \Drupal::entityTypeManager()->getStorage('media')->load($id); - $user = User::load($jwt_payload->uid); - - $accountSwitcher = \Drupal::service('account_switcher'); - $accountSwitcher->switchTo($user); - - $file = CoolUtils::getFile($media); - - if ($timestamp) { - $wopi_stamp = date_create_immutable_from_format(\DateTimeInterface::ISO8601, $timestamp); - $file_stamp = date_create_immutable_from_format('U', $file->getChangedTime()); - - if ($wopi_stamp != $file_stamp) { - \Drupal::logger('cool')->error('Conflict saving file ' . $id . ' wopi: ' . $wopi_stamp->format('c') . ' differs from file: ' . $file_stamp->format('c')); - - return new Response( - json_encode(['COOLStatusCode' => 1010]), - Response::HTTP_CONFLICT, - ['content-type' => 'application/json'], - ); - } - } - - $dir = $fs->dirname($file->getFileUri()); - $dest = $dir . '/' . $file->getFilename(); - - $content = $request->getContent(); - $owner_id = $file->getOwnerId(); - $uri = $fs->saveData($content, $dest, FileSystemInterface::EXISTS_RENAME); - - $file = File::create(['uri' => $uri]); - $file->setOwnerId($owner_id); - if (is_file($dest)) { - $file->setFilename($fs->basename($dest)); - } - $file->setPermanent(); - $file->setSize(strlen($content)); - $file->save(); - $mtime = date_create_immutable_from_format('U', $file->getChangedTime()); - - CoolUtils::setMediaSource($media, $file); - $media->setRevisionUser($user); - $media->setRevisionCreationTime(\Drupal::service('datetime.time')->getRequestTime()); - - $save_reason = 'Saved by Collabora Online'; - $reasons = []; - if ($modified_by_user) { - $reasons[] = 'Modified by user'; - } - if ($autosave) { - $reasons[] = 'Autosaved'; - } - if ($exitsave) { - $reasons[] = 'Save on Exit'; - } - if (count($reasons) > 0) { - $save_reason .= ' (' . implode(', ', $reasons) . ')'; - } - \Drupal::logger('cool')->error('Save reason: ' . $save_reason); - $media->setRevisionLogMessage($save_reason); - $media->save(); - - $payload = json_encode([ - 'LastModifiedTime' => $mtime->format('c'), - ]); - - $response = new Response( - $payload, - Response::HTTP_OK, - ['content-type' => 'application/json'] - ); + $payload = [ + 'BaseFileName' => $file->getFilename(), + 'Size' => $file->getSize(), + 'LastModifiedTime' => $mtime->format('c'), + 'UserId' => $jwt_payload->uid, + 'UserFriendlyName' => $user->getDisplayName(), + 'UserExtraInfo' => [ + 'mail' => $user->getEmail(), + ], + 'UserCanWrite' => $can_write, + 'IsAdminUser' => $user->hasPermission('administer collabora instance'), + 'IsAnonymousUser' => $user->isAnonymous(), + ]; + + $user_picture = $user->user_picture?->entity; + if ($user_picture) { + /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */ + $file_url_generator = \Drupal::service('file_url_generator'); + $payload['UserExtraInfo']['avatar'] = $file_url_generator->generateAbsoluteString($user_picture->getFileUri()); + } - $accountSwitcher->switchBack(); - return $response; + $jsonPayload = json_encode($payload); + + $response = new Response( + $jsonPayload, + Response::HTTP_OK, + ['content-type' => 'application/json'] + ); + return $response; + } + + /** + * Handles the wopi "content" request for a media entity. + * + * @param string $id + * Media id from url. + * @param \Symfony\Component\HttpFoundation\Request $request + * Request object with query parameters. + * + * @return \Symfony\Component\HttpFoundation\Response + * The response with file contents. + */ + public function wopiGetFile(string $id, Request $request) { + $token = $request->query->get('access_token'); + + $jwt_payload = CoolUtils::verifyTokenForId($token, $id); + if ($jwt_payload == NULL) { + return static::permissionDenied(); } - /** - * The WOPI entry point. - * - * @param string $action - * One of 'info', 'content' or 'save', depending with path is visited. - * @param string $id - * Media id from url. - * @param \Symfony\Component\HttpFoundation\Request $request - * Request object for headers and query parameters. - * - * @return \Symfony\Component\HttpFoundation\Response - * Response to be consumed by Collabora Online. - */ - public function wopi(string $action, string $id, Request $request) { - $returnCode = Response::HTTP_BAD_REQUEST; - switch ($action) { - case 'info': - return $this->wopiCheckFileInfo($id, $request); - - case 'content': - return $this->wopiGetFile($id, $request); - - case 'save': - return $this->wopiPutFile($id, $request); - } - - $response = new Response( - 'Invalid WOPI action ' . $action, - $returnCode, - ['content-type' => 'text/plain'] + $user = User::load($jwt_payload->uid); + $accountSwitcher = \Drupal::service('account_switcher'); + $accountSwitcher->switchTo($user); + + $file = CoolUtils::getFileById($id); + $mimetype = $file->getMimeType(); + + $response = new BinaryFileResponse( + $file->getFileUri(), + Response::HTTP_OK, + ['content-type' => $mimetype] + ); + $accountSwitcher->switchBack(); + return $response; + } + + /** + * Handles the wopi "save" request for a media entity. + * + * @param string $id + * Media id from url. + * @param \Symfony\Component\HttpFoundation\Request $request + * Request object with headers, query parameters and payload. + * + * @return \Symfony\Component\HttpFoundation\Response + * The response. + */ + public function wopiPutFile(string $id, Request $request) { + $token = $request->query->get('access_token'); + $timestamp = $request->headers->get('x-cool-wopi-timestamp'); + $modified_by_user = $request->headers->get('x-cool-wopi-ismodifiedbyuser') == 'true'; + $autosave = $request->headers->get('x-cool-wopi-isautosave') == 'true'; + $exitsave = $request->headers->get('x-cool-wopi-isexitsave') == 'true'; + + $jwt_payload = CoolUtils::verifyTokenForId($token, $id); + if ($jwt_payload == NULL || !$jwt_payload->wri) { + return static::permissionDenied(); + } + + $fs = \Drupal::service('file_system'); + + $media = \Drupal::entityTypeManager()->getStorage('media')->load($id); + $user = User::load($jwt_payload->uid); + + $accountSwitcher = \Drupal::service('account_switcher'); + $accountSwitcher->switchTo($user); + + $file = CoolUtils::getFile($media); + + if ($timestamp) { + $wopi_stamp = date_create_immutable_from_format(\DateTimeInterface::ISO8601, $timestamp); + $file_stamp = date_create_immutable_from_format('U', $file->getChangedTime()); + + if ($wopi_stamp != $file_stamp) { + \Drupal::logger('cool')->error('Conflict saving file ' . $id . ' wopi: ' . $wopi_stamp->format('c') . ' differs from file: ' . $file_stamp->format('c')); + + return new Response( + json_encode(['COOLStatusCode' => 1010]), + Response::HTTP_CONFLICT, + ['content-type' => 'application/json'], ); - return $response; + } + } + + $dir = $fs->dirname($file->getFileUri()); + $dest = $dir . '/' . $file->getFilename(); + + $content = $request->getContent(); + $owner_id = $file->getOwnerId(); + $uri = $fs->saveData($content, $dest, FileSystemInterface::EXISTS_RENAME); + + $file = File::create(['uri' => $uri]); + $file->setOwnerId($owner_id); + if (is_file($dest)) { + $file->setFilename($fs->basename($dest)); } + $file->setPermanent(); + $file->setSize(strlen($content)); + $file->save(); + $mtime = date_create_immutable_from_format('U', $file->getChangedTime()); + + CoolUtils::setMediaSource($media, $file); + $media->setRevisionUser($user); + $media->setRevisionCreationTime(\Drupal::service('datetime.time')->getRequestTime()); + + $save_reason = 'Saved by Collabora Online'; + $reasons = []; + if ($modified_by_user) { + $reasons[] = 'Modified by user'; + } + if ($autosave) { + $reasons[] = 'Autosaved'; + } + if ($exitsave) { + $reasons[] = 'Save on Exit'; + } + if (count($reasons) > 0) { + $save_reason .= ' (' . implode(', ', $reasons) . ')'; + } + \Drupal::logger('cool')->error('Save reason: ' . $save_reason); + $media->setRevisionLogMessage($save_reason); + $media->save(); + + $payload = json_encode([ + 'LastModifiedTime' => $mtime->format('c'), + ]); + + $response = new Response( + $payload, + Response::HTTP_OK, + ['content-type' => 'application/json'] + ); + + $accountSwitcher->switchBack(); + return $response; + } + + /** + * The WOPI entry point. + * + * @param string $action + * One of 'info', 'content' or 'save', depending with path is visited. + * @param string $id + * Media id from url. + * @param \Symfony\Component\HttpFoundation\Request $request + * Request object for headers and query parameters. + * + * @return \Symfony\Component\HttpFoundation\Response + * Response to be consumed by Collabora Online. + */ + public function wopi(string $action, string $id, Request $request) { + $returnCode = Response::HTTP_BAD_REQUEST; + switch ($action) { + case 'info': + return $this->wopiCheckFileInfo($id, $request); + + case 'content': + return $this->wopiGetFile($id, $request); + + case 'save': + return $this->wopiPutFile($id, $request); + } + + $response = new Response( + 'Invalid WOPI action ' . $action, + $returnCode, + ['content-type' => 'text/plain'] + ); + return $response; + } } diff --git a/src/Cool/CoolRequest.php b/src/Cool/CoolRequest.php index 631457aa..c93f906d 100644 --- a/src/Cool/CoolRequest.php +++ b/src/Cool/CoolRequest.php @@ -22,22 +22,22 @@ * The full contents of discovery.xml, or FALSE on failure. */ function getDiscovery($server) { - $discovery_url = $server . '/hosting/discovery'; - - $default_config = \Drupal::config('collabora_online.settings'); - if ($default_config === NULL) { - return FALSE; - } - $disable_checks = (bool) $default_config->get('cool')['disable_cert_check']; - - $stream_context = stream_context_create([ - 'ssl' => [ - 'verify_peer' => !$disable_checks, - 'verify_peer_name' => !$disable_checks, - ], - ]); - $res = file_get_contents($discovery_url, FALSE, $stream_context); - return $res; + $discovery_url = $server . '/hosting/discovery'; + + $default_config = \Drupal::config('collabora_online.settings'); + if ($default_config === NULL) { + return FALSE; + } + $disable_checks = (bool) $default_config->get('cool')['disable_cert_check']; + + $stream_context = stream_context_create([ + 'ssl' => [ + 'verify_peer' => !$disable_checks, + 'verify_peer_name' => !$disable_checks, + ], + ]); + $res = file_get_contents($discovery_url, FALSE, $stream_context); + return $res; } /** @@ -54,14 +54,14 @@ function getDiscovery($server) { * was found for the given MIME type. */ function getWopiSrcUrl($discovery_parsed, $mimetype) { - if ($discovery_parsed === NULL || $discovery_parsed == FALSE) { - return NULL; - } - $result = $discovery_parsed->xpath(sprintf('/wopi-discovery/net-zone/app[@name=\'%s\']/action', $mimetype)); - if ($result && count($result) > 0) { - return $result[0]['urlsrc']; - } + if ($discovery_parsed === NULL || $discovery_parsed == FALSE) { return NULL; + } + $result = $discovery_parsed->xpath(sprintf('/wopi-discovery/net-zone/app[@name=\'%s\']/action', $mimetype)); + if ($result && count($result) > 0) { + return $result[0]['urlsrc']; + } + return NULL; } /** @@ -78,8 +78,8 @@ function getWopiSrcUrl($discovery_parsed, $mimetype) { * @see str_starts_with() */ function strStartsWith($s, $ss) { - $res = strrpos($s, $ss); - return !is_bool($res) && $res == 0; + $res = strrpos($s, $ss); + return !is_bool($res) && $res == 0; } /** @@ -87,94 +87,94 @@ function strStartsWith($s, $ss) { */ class CoolRequest { - /** - * Error code from last attempt to fetch the client WOPI url. - * - * @var int - */ - private $error_code; - - const ERROR_MSG = [ - 0 => 'Success', - 101 => 'GET Request not found.', - 201 => 'Collabora Online server address is not valid.', - 202 => 'Collabora Online server address scheme does not match the current page url scheme.', - 203 => 'Not able to retrieve the discovery.xml file from the Collabora Online server.', - 102 => 'The retrieved discovery.xml file is not a valid XML file.', - 103 => 'The requested mime type is not handled.', - 204 => 'Warning! You have to specify the scheme protocol too (http|https) for the server address.', - ]; - - /** - * The WOPI url that was last fetched, or '' as initial value. - * - * @var int - */ - private $wopi_src; - - /** - * Constructor. - */ - public function __construct() { - $this->error_code = 0; - $this->wopi_src = ''; + /** + * Error code from last attempt to fetch the client WOPI url. + * + * @var int + */ + private $error_code; + + const ERROR_MSG = [ + 0 => 'Success', + 101 => 'GET Request not found.', + 201 => 'Collabora Online server address is not valid.', + 202 => 'Collabora Online server address scheme does not match the current page url scheme.', + 203 => 'Not able to retrieve the discovery.xml file from the Collabora Online server.', + 102 => 'The retrieved discovery.xml file is not a valid XML file.', + 103 => 'The requested mime type is not handled.', + 204 => 'Warning! You have to specify the scheme protocol too (http|https) for the server address.', + ]; + + /** + * The WOPI url that was last fetched, or '' as initial value. + * + * @var int + */ + private $wopi_src; + + /** + * Constructor. + */ + public function __construct() { + $this->error_code = 0; + $this->wopi_src = ''; + } + + /** + * Gets an error string from the last attempt to fetch the WOPI url. + * + * @return string + * Error string containing int error code and a message. + */ + public function errorString() { + return $this->error_code . ': ' . static::ERROR_MSG[$this->error_code]; + } + + /** + * Gets the URL for the WOPI client. + * + * @return string|null + * The WOPI client url, or NULL on failure. + */ + public function getWopiClientURL() { + $_HOST_SCHEME = isset($_SERVER['HTTPS']) ? 'https' : 'http'; + $default_config = \Drupal::config('collabora_online.settings'); + $wopi_client_server = $default_config->get('cool')['server']; + if (!$wopi_client_server) { + $this->error_code = 201; + return NULL; } + $wopi_client_server = trim($wopi_client_server); - /** - * Gets an error string from the last attempt to fetch the WOPI url. - * - * @return string - * Error string containing int error code and a message. - */ - public function errorString() { - return $this->error_code . ': ' . static::ERROR_MSG[$this->error_code]; + if (!strStartsWith($wopi_client_server, 'http')) { + $this->error_code = 204; + return NULL; } - /** - * Gets the URL for the WOPI client. - * - * @return string|null - * The WOPI client url, or NULL on failure. - */ - public function getWopiClientURL() { - $_HOST_SCHEME = isset($_SERVER['HTTPS']) ? 'https' : 'http'; - $default_config = \Drupal::config('collabora_online.settings'); - $wopi_client_server = $default_config->get('cool')['server']; - if (!$wopi_client_server) { - $this->error_code = 201; - return NULL; - } - $wopi_client_server = trim($wopi_client_server); - - if (!strStartsWith($wopi_client_server, 'http')) { - $this->error_code = 204; - return NULL; - } - - if (!strStartsWith($wopi_client_server, $_HOST_SCHEME . '://')) { - $this->error_code = 202; - return NULL; - } - - $discovery = getDiscovery($wopi_client_server); - if ($discovery === FALSE) { - $this->error_code = 203; - return NULL; - } - - $discovery_parsed = simplexml_load_string($discovery); - if (!$discovery_parsed) { - $this->error_code = 102; - return NULL; - } - - $this->wopi_src = strval(getWopiSrcUrl($discovery_parsed, 'text/plain')[0]); - if (!$this->wopi_src) { - $this->error_code = 103; - return NULL; - } - - return $this->wopi_src; + if (!strStartsWith($wopi_client_server, $_HOST_SCHEME . '://')) { + $this->error_code = 202; + return NULL; } + $discovery = getDiscovery($wopi_client_server); + if ($discovery === FALSE) { + $this->error_code = 203; + return NULL; + } + + $discovery_parsed = simplexml_load_string($discovery); + if (!$discovery_parsed) { + $this->error_code = 102; + return NULL; + } + + $this->wopi_src = strval(getWopiSrcUrl($discovery_parsed, 'text/plain')[0]); + if (!$this->wopi_src) { + $this->error_code = 103; + return NULL; + } + + return $this->wopi_src; + } + } diff --git a/src/Cool/CoolUtils.php b/src/Cool/CoolUtils.php index f8c4b8e3..f1a548b0 100644 --- a/src/Cool/CoolUtils.php +++ b/src/Cool/CoolUtils.php @@ -23,261 +23,261 @@ */ class CoolUtils { - /** - * Gets the file referenced by a media entity. - * - * @param \Drupal\media\Entity\Media $media - * The media entity. - * - * @return \Drupal\file\FileInterface|null - * The file entity, or NULL if not found. - */ - public static function getFile(Media $media) { - $fid = $media->getSource()->getSourceFieldValue($media); - $file = File::load($fid); + /** + * Gets the file referenced by a media entity. + * + * @param \Drupal\media\Entity\Media $media + * The media entity. + * + * @return \Drupal\file\FileInterface|null + * The file entity, or NULL if not found. + */ + public static function getFile(Media $media) { + $fid = $media->getSource()->getSourceFieldValue($media); + $file = File::load($fid); - return $file; - } + return $file; + } - /** - * Gets a file based on the media id. - * - * @param int|string $id - * Media id which might be in strong form like '123'. - * - * @return \Drupal\file\FileInterface|null - * File referenced by the media entity, or NULL if not found. - */ - public static function getFileById($id) { - /** @var \Drupal\media\MediaInterface|null $media */ - $media = \Drupal::entityTypeManager()->getStorage('media')->load($id); - return CoolUtils::getFile($media); - } + /** + * Gets a file based on the media id. + * + * @param int|string $id + * Media id which might be in strong form like '123'. + * + * @return \Drupal\file\FileInterface|null + * File referenced by the media entity, or NULL if not found. + */ + public static function getFileById($id) { + /** @var \Drupal\media\MediaInterface|null $media */ + $media = \Drupal::entityTypeManager()->getStorage('media')->load($id); + return CoolUtils::getFile($media); + } - /** - * Sets the file entity reference for a media entity. - * - * @param \Drupal\media\Entity\Media $media - * Media entity to be modified. - * @param \Drupal\file\Entity\File $source - * File entity to reference. - */ - public static function setMediaSource(Media $media, File $source) { - $name = $media->getSource()->getSourceFieldDefinition($media->bundle->entity)->getName(); - $media->set($name, $source); - } + /** + * Sets the file entity reference for a media entity. + * + * @param \Drupal\media\Entity\Media $media + * Media entity to be modified. + * @param \Drupal\file\Entity\File $source + * File entity to reference. + */ + public static function setMediaSource(Media $media, File $source) { + $name = $media->getSource()->getSourceFieldDefinition($media->bundle->entity)->getName(); + $media->set($name, $source); + } - /** - * Obtains the signing key from the key storage. - * - * @return string - * The key value. - */ - public static function getKey() { - $default_config = \Drupal::config('collabora_online.settings'); - $key_id = $default_config->get('cool')['key_id']; + /** + * Obtains the signing key from the key storage. + * + * @return string + * The key value. + */ + public static function getKey() { + $default_config = \Drupal::config('collabora_online.settings'); + $key_id = $default_config->get('cool')['key_id']; - $key = \Drupal::service('key.repository')->getKey($key_id)->getKeyValue(); - return $key; - } + $key = \Drupal::service('key.repository')->getKey($key_id)->getKeyValue(); + return $key; + } - /** - * Decodes and verifies a JWT token. - * - * Verification include: - * - matching $id with fid in the payload - * - verifying the expiration. - * - * @param string $token - * The token to verify. - * @param int|string $id - * Media id for which the token was created. - * This could be in string form like '123'. - * - * @return \stdClass|null - * Data decoded from the token, or NULL on failure or if the token has - * expired. - */ - public static function verifyTokenForId( - #[\SensitiveParameter] - string $token, - $id, - ) { - $key = static::getKey(); - try { - $payload = JWT::decode($token, new Key($key, 'HS256')); + /** + * Decodes and verifies a JWT token. + * + * Verification include: + * - matching $id with fid in the payload + * - verifying the expiration. + * + * @param string $token + * The token to verify. + * @param int|string $id + * Media id for which the token was created. + * This could be in string form like '123'. + * + * @return \stdClass|null + * Data decoded from the token, or NULL on failure or if the token has + * expired. + */ + public static function verifyTokenForId( + #[\SensitiveParameter] + string $token, + $id, + ) { + $key = static::getKey(); + try { + $payload = JWT::decode($token, new Key($key, 'HS256')); - if ($payload && ($payload->fid == $id) && ($payload->exp >= gettimeofday(TRUE))) { - return $payload; - } - } - catch (\Exception $e) { - \Drupal::logger('cool')->error($e->getMessage()); - } - return NULL; + if ($payload && ($payload->fid == $id) && ($payload->exp >= gettimeofday(TRUE))) { + return $payload; + } } - - /** - * Gets the TTL of the token in seconds, from the EPOCH. - * - * @return int - * Token TTL in seconds. - */ - public static function getAccessTokenTtl() { - $default_config = \Drupal::config('collabora_online.settings'); - $ttl = $default_config->get('cool')['access_token_ttl']; - - return gettimeofday(TRUE) + $ttl; + catch (\Exception $e) { + \Drupal::logger('cool')->error($e->getMessage()); } + return NULL; + } - /** - * Creates a JWT token for a media entity. - * - * The token will carry the following: - * - * - fid: the Media id in Drupal. - * - uid: the User id for the token. Permissions should be checked - * whenever. - * - exp: the expiration time of the token. - * - wri: if true, then this token has write permissions. - * - * The signing key is stored in Drupal key management. - * - * @param int|string $id - * Media id, which could be in string form like '123'. - * @param int $ttl - * Access token TTL in seconds. - * @param bool $can_write - * TRUE if the token is for an editor in write/edit mode. - * - * @return string - * The access token. - */ - public static function tokenForFileId($id, $ttl, $can_write = FALSE) { - $payload = [ - "fid" => $id, - "uid" => \Drupal::currentUser()->id(), - "exp" => $ttl, - "wri" => $can_write, - ]; - $key = static::getKey(); - $jwt = JWT::encode($payload, $key, 'HS256'); + /** + * Gets the TTL of the token in seconds, from the EPOCH. + * + * @return int + * Token TTL in seconds. + */ + public static function getAccessTokenTtl() { + $default_config = \Drupal::config('collabora_online.settings'); + $ttl = $default_config->get('cool')['access_token_ttl']; - return $jwt; - } + return gettimeofday(TRUE) + $ttl; + } - /** - * List of read only formats. Currently limited to the one Drupal accept. - */ - const READ_ONLY = [ - 'application/x-iwork-keynote-sffkey' => TRUE, - 'application/x-iwork-pages-sffpages' => TRUE, - 'application/x-iwork-numbers-sffnumbers' => TRUE, + /** + * Creates a JWT token for a media entity. + * + * The token will carry the following: + * + * - fid: the Media id in Drupal. + * - uid: the User id for the token. Permissions should be checked + * whenever. + * - exp: the expiration time of the token. + * - wri: if true, then this token has write permissions. + * + * The signing key is stored in Drupal key management. + * + * @param int|string $id + * Media id, which could be in string form like '123'. + * @param int $ttl + * Access token TTL in seconds. + * @param bool $can_write + * TRUE if the token is for an editor in write/edit mode. + * + * @return string + * The access token. + */ + public static function tokenForFileId($id, $ttl, $can_write = FALSE) { + $payload = [ + "fid" => $id, + "uid" => \Drupal::currentUser()->id(), + "exp" => $ttl, + "wri" => $can_write, ]; + $key = static::getKey(); + $jwt = JWT::encode($payload, $key, 'HS256'); - /** - * Determines if we can edit that media file. - * - * There are few types that Collabora Online only views. - * - * @param \Drupal\file\Entity\File $file - * File entity. - * - * @return bool - * TRUE if the file has a file type that is supported for editing. - * FALSE if the file can only be opened as read-only. - */ - public static function canEdit(File $file) { - $mimetype = $file->getMimeType(); - return !array_key_exists($mimetype, static::READ_ONLY); - } + return $jwt; + } - /** - * Gets the mime type for the document. - * - * Drupal will figure it out for us. - * - * @param \Drupal\file\Entity\File $file - * File entity. - * - * @return string|null - * The mime type, or NULL if it cannot be determined. - */ - public static function getDocumentType(File $file) { - return $file->getMimeType(); - } + /** + * List of read only formats. Currently limited to the one Drupal accept. + */ + const READ_ONLY = [ + 'application/x-iwork-keynote-sffkey' => TRUE, + 'application/x-iwork-pages-sffpages' => TRUE, + 'application/x-iwork-numbers-sffnumbers' => TRUE, + ]; - /** - * Gets the editor / viewer Drupal URL from the routes configured. - * - * @param \Drupal\media\Entity\Media $media - * Media entity that holds the file to open in the editor. - * @param bool $can_write - * TRUE for an edit url, FALSE for a read-only preview url. - * - * @return \Drupal\Core\Url - * Editor url to visit as full-page, or to embed in an iframe. - */ - public static function getEditorUrl(Media $media, $can_write = FALSE) { - if ($can_write) { - return Url::fromRoute('collabora-online.edit', ['media' => $media->id()]); - } - else { - return Url::fromRoute('collabora-online.view', ['media' => $media->id()]); - } - } + /** + * Determines if we can edit that media file. + * + * There are few types that Collabora Online only views. + * + * @param \Drupal\file\Entity\File $file + * File entity. + * + * @return bool + * TRUE if the file has a file type that is supported for editing. + * FALSE if the file can only be opened as read-only. + */ + public static function canEdit(File $file) { + $mimetype = $file->getMimeType(); + return !array_key_exists($mimetype, static::READ_ONLY); + } + + /** + * Gets the mime type for the document. + * + * Drupal will figure it out for us. + * + * @param \Drupal\file\Entity\File $file + * File entity. + * + * @return string|null + * The mime type, or NULL if it cannot be determined. + */ + public static function getDocumentType(File $file) { + return $file->getMimeType(); + } - /** - * Gets a render array for a cool viewer. - * - * @param \Drupal\media\Entity\Media $media - * The media entity to view / edit. - * @param bool $can_write - * Whether this is a viewer (false) or an edit (true). Permissions will - * also be checked. - * @param array{closebutton: bool} $options - * Options for the renderer. Current values: - * - "closebutton" if "true" will add a close box. (see COOL SDK) - * - * @return array|array{error: string} - * A stub render element array, or an array with an error on failure. - */ - public static function getViewerRender(Media $media, bool $can_write, $options = NULL) { - $default_config = \Drupal::config('collabora_online.settings'); - $wopi_base = $default_config->get('cool')['wopi_base']; - $allowfullscreen = $default_config->get('cool')['allowfullscreen'] ?? FALSE; + /** + * Gets the editor / viewer Drupal URL from the routes configured. + * + * @param \Drupal\media\Entity\Media $media + * Media entity that holds the file to open in the editor. + * @param bool $can_write + * TRUE for an edit url, FALSE for a read-only preview url. + * + * @return \Drupal\Core\Url + * Editor url to visit as full-page, or to embed in an iframe. + */ + public static function getEditorUrl(Media $media, $can_write = FALSE) { + if ($can_write) { + return Url::fromRoute('collabora-online.edit', ['media' => $media->id()]); + } + else { + return Url::fromRoute('collabora-online.view', ['media' => $media->id()]); + } + } - $req = new CoolRequest(); - $wopi_client = $req->getWopiClientURL(); - if ($wopi_client === NULL) { - return [ - 'error' => t('The Collabora Online server is not available: ') . $req->errorString(), - ]; - } + /** + * Gets a render array for a cool viewer. + * + * @param \Drupal\media\Entity\Media $media + * The media entity to view / edit. + * @param bool $can_write + * Whether this is a viewer (false) or an edit (true). Permissions will + * also be checked. + * @param array{closebutton: bool} $options + * Options for the renderer. Current values: + * - "closebutton" if "true" will add a close box. (see COOL SDK) + * + * @return array|array{error: string} + * A stub render element array, or an array with an error on failure. + */ + public static function getViewerRender(Media $media, bool $can_write, $options = NULL) { + $default_config = \Drupal::config('collabora_online.settings'); + $wopi_base = $default_config->get('cool')['wopi_base']; + $allowfullscreen = $default_config->get('cool')['allowfullscreen'] ?? FALSE; - $id = $media->id(); + $req = new CoolRequest(); + $wopi_client = $req->getWopiClientURL(); + if ($wopi_client === NULL) { + return [ + 'error' => t('The Collabora Online server is not available: ') . $req->errorString(), + ]; + } - $ttl = static::getAccessTokenTtl(); - if ($ttl == 0) { - $ttl = 86400; - } - $access_token = static::tokenForFileId($id, $ttl, $can_write); + $id = $media->id(); - $render_array = [ - '#wopiClient' => $wopi_client, - '#wopiSrc' => urlencode($wopi_base . '/cool/wopi/files/' . $id), - '#accessToken' => $access_token, - // It's in usec. The JWT is in sec. - '#accessTokenTtl' => $ttl * 1000, - '#allowfullscreen' => $allowfullscreen ? 'allowfullscreen' : '', - ]; - if ($options) { - if (isset($options['closebutton']) && $options['closebutton'] == 'true') { - $render_array['#closebutton'] = 'true'; - } - } + $ttl = static::getAccessTokenTtl(); + if ($ttl == 0) { + $ttl = 86400; + } + $access_token = static::tokenForFileId($id, $ttl, $can_write); - return $render_array; + $render_array = [ + '#wopiClient' => $wopi_client, + '#wopiSrc' => urlencode($wopi_base . '/cool/wopi/files/' . $id), + '#accessToken' => $access_token, + // It's in usec. The JWT is in sec. + '#accessTokenTtl' => $ttl * 1000, + '#allowfullscreen' => $allowfullscreen ? 'allowfullscreen' : '', + ]; + if ($options) { + if (isset($options['closebutton']) && $options['closebutton'] == 'true') { + $render_array['#closebutton'] = 'true'; + } } + return $render_array; + } + } diff --git a/src/Form/ConfigForm.php b/src/Form/ConfigForm.php index 9d559fd9..284e54d9 100644 --- a/src/Form/ConfigForm.php +++ b/src/Form/ConfigForm.php @@ -20,89 +20,89 @@ */ class ConfigForm extends ConfigFormBase { - const SETTINGS = 'collabora_online.settings'; - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'collabora_configform'; - } - - /** - * {@inheritdoc} - */ - public function getEditableConfigNames() { - return [ - static::SETTINGS, - ]; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state) { - $config = $this->config(static::SETTINGS); - - $form['server'] = [ - '#type' => 'textfield', - '#title' => $this->t('Collabora Online server URL'), - '#default_value' => $config->get('cool')['server'], - '#required' => TRUE, - ]; - - $form['wopi_base'] = [ - '#type' => 'textfield', - '#title' => $this->t('WOPI host URL. Likely https://<drupal_server>'), - '#default_value' => $config->get('cool')['wopi_base'], - '#required' => TRUE, - ]; - - $form['key_id'] = [ - '#type' => 'textfield', - '#title' => $this->t('JWT private key ID'), - '#default_value' => $config->get('cool')['key_id'], - '#required' => TRUE, - ]; - - $form['access_token_ttl'] = [ - '#type' => 'textfield', - '#title' => $this->t('Access Token Expiration (in seconds)'), - '#default_value' => $config->get('cool')['access_token_ttl'], - '#required' => TRUE, - ]; - - $form['disable_cert_check'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Disable TLS certificate check for COOL.'), - '#default_value' => $config->get('cool')['disable_cert_check'], - ]; - - $form['allowfullscreen'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Allow COOL to use fullscreen mode.'), - '#default_value' => $config->get('cool')['allowfullscreen'] ?? FALSE, - ]; - - return parent::buildForm($form, $form_state); - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $wopi_base = rtrim($form_state->getValue('wopi_base'), '/'); - - $this->config(static::SETTINGS) - ->set('cool.server', $form_state->getValue('server')) - ->set('cool.wopi_base', $wopi_base) - ->set('cool.key_id', $form_state->getValue('key_id')) - ->set('cool.access_token_ttl', $form_state->getValue('access_token_ttl')) - ->set('cool.disable_cert_check', $form_state->getValue('disable_cert_check')) - ->set('cool.allowfullscreen', $form_state->getValue('allowfullscreen')) - ->save(); - - parent::submitForm($form, $form_state); - } + const SETTINGS = 'collabora_online.settings'; + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'collabora_configform'; + } + + /** + * {@inheritdoc} + */ + public function getEditableConfigNames() { + return [ + static::SETTINGS, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $config = $this->config(static::SETTINGS); + + $form['server'] = [ + '#type' => 'textfield', + '#title' => $this->t('Collabora Online server URL'), + '#default_value' => $config->get('cool')['server'], + '#required' => TRUE, + ]; + + $form['wopi_base'] = [ + '#type' => 'textfield', + '#title' => $this->t('WOPI host URL. Likely https://<drupal_server>'), + '#default_value' => $config->get('cool')['wopi_base'], + '#required' => TRUE, + ]; + + $form['key_id'] = [ + '#type' => 'textfield', + '#title' => $this->t('JWT private key ID'), + '#default_value' => $config->get('cool')['key_id'], + '#required' => TRUE, + ]; + + $form['access_token_ttl'] = [ + '#type' => 'textfield', + '#title' => $this->t('Access Token Expiration (in seconds)'), + '#default_value' => $config->get('cool')['access_token_ttl'], + '#required' => TRUE, + ]; + + $form['disable_cert_check'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Disable TLS certificate check for COOL.'), + '#default_value' => $config->get('cool')['disable_cert_check'], + ]; + + $form['allowfullscreen'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Allow COOL to use fullscreen mode.'), + '#default_value' => $config->get('cool')['allowfullscreen'] ?? FALSE, + ]; + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $wopi_base = rtrim($form_state->getValue('wopi_base'), '/'); + + $this->config(static::SETTINGS) + ->set('cool.server', $form_state->getValue('server')) + ->set('cool.wopi_base', $wopi_base) + ->set('cool.key_id', $form_state->getValue('key_id')) + ->set('cool.access_token_ttl', $form_state->getValue('access_token_ttl')) + ->set('cool.disable_cert_check', $form_state->getValue('disable_cert_check')) + ->set('cool.allowfullscreen', $form_state->getValue('allowfullscreen')) + ->save(); + + parent::submitForm($form, $form_state); + } } diff --git a/src/Plugin/Field/FieldFormatter/CoolPreview.php b/src/Plugin/Field/FieldFormatter/CoolPreview.php index bbdf382b..766ab314 100644 --- a/src/Plugin/Field/FieldFormatter/CoolPreview.php +++ b/src/Plugin/Field/FieldFormatter/CoolPreview.php @@ -31,49 +31,49 @@ */ class CoolPreview extends EntityReferenceFormatterBase { - /** - * {@inheritdoc} - */ - public function settingsSummary() { - $summary = []; - $summary[] = $this->t('Preview Collabora Online documents.'); - return $summary; - } + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = []; + $summary[] = $this->t('Preview Collabora Online documents.'); + return $summary; + } - /** - * {@inheritdoc} - */ - public function viewElements(FieldItemListInterface $items, $langcode) { - /** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items */ - $elements = []; - $media = $items->getEntity(); - if (!$media instanceof MediaInterface) { - // Entity types other than 'media' are not supported. - return []; - } + /** + * {@inheritdoc} + */ + public function viewElements(FieldItemListInterface $items, $langcode) { + /** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items */ + $elements = []; + $media = $items->getEntity(); + if (!$media instanceof MediaInterface) { + // Entity types other than 'media' are not supported. + return []; + } - $access_result = $media->access('preview in collabora', NULL, TRUE); - (new CacheableMetadata()) - ->addCacheableDependency($access_result) - ->applyTo($elements); + $access_result = $media->access('preview in collabora', NULL, TRUE); + (new CacheableMetadata()) + ->addCacheableDependency($access_result) + ->applyTo($elements); - if (!$access_result->isAllowed()) { - return $elements; - } + if (!$access_result->isAllowed()) { + return $elements; + } - foreach ($this->getEntitiesToView($items, $langcode) as $delta => $file) { - $url = CoolUtils::getEditorUrl($media, FALSE); + foreach ($this->getEntitiesToView($items, $langcode) as $delta => $file) { + $url = CoolUtils::getEditorUrl($media, FALSE); - $render_array = [ - '#editorUrl' => $url, - '#fileName' => $media->getName(), - ]; - $render_array['#theme'] = 'collabora_online_preview'; - $render_array['#attached']['library'][] = 'collabora_online/cool.previewer'; - // Render each element as markup. - $elements[$delta] = $render_array; - } - return $elements; + $render_array = [ + '#editorUrl' => $url, + '#fileName' => $media->getName(), + ]; + $render_array['#theme'] = 'collabora_online_preview'; + $render_array['#attached']['library'][] = 'collabora_online/cool.previewer'; + // Render each element as markup. + $elements[$delta] = $render_array; } + return $elements; + } } diff --git a/src/Plugin/views/field/CollaboraEdit.php b/src/Plugin/views/field/CollaboraEdit.php index 328ebc76..e1698a4d 100644 --- a/src/Plugin/views/field/CollaboraEdit.php +++ b/src/Plugin/views/field/CollaboraEdit.php @@ -19,25 +19,25 @@ #[ViewsField('media_collabora_edit')] class CollaboraEdit extends LinkBase { - /** - * {@inheritdoc} - */ - protected function getUrlInfo(ResultRow $row): Url|null { - /** @var \Drupal\media\MediaInterface $entity */ - $entity = $this->getEntity($row); - - if ($entity === NULL) { - return NULL; - } - - return CoolUtils::getEditorUrl($entity, TRUE); + /** + * {@inheritdoc} + */ + protected function getUrlInfo(ResultRow $row): Url|null { + /** @var \Drupal\media\MediaInterface $entity */ + $entity = $this->getEntity($row); + + if ($entity === NULL) { + return NULL; } - /** - * {@inheritdoc} - */ - protected function getDefaultLabel(): TranslatableMarkup { - return $this->t('Edit in Collabora Online'); - } + return CoolUtils::getEditorUrl($entity, TRUE); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultLabel(): TranslatableMarkup { + return $this->t('Edit in Collabora Online'); + } } diff --git a/src/Plugin/views/field/CollaboraPreview.php b/src/Plugin/views/field/CollaboraPreview.php index f636b359..a4b2d65a 100644 --- a/src/Plugin/views/field/CollaboraPreview.php +++ b/src/Plugin/views/field/CollaboraPreview.php @@ -19,25 +19,25 @@ #[ViewsField('media_collabora_preview')] class CollaboraPreview extends LinkBase { - /** - * {@inheritdoc} - */ - protected function getUrlInfo(ResultRow $row): Url|null { - /** @var \Drupal\media\MediaInterface $entity */ - $entity = $this->getEntity($row); - - if ($entity === NULL) { - return NULL; - } - - return CoolUtils::getEditorUrl($entity, FALSE); + /** + * {@inheritdoc} + */ + protected function getUrlInfo(ResultRow $row): Url|null { + /** @var \Drupal\media\MediaInterface $entity */ + $entity = $this->getEntity($row); + + if ($entity === NULL) { + return NULL; } - /** - * {@inheritdoc} - */ - protected function getDefaultLabel(): TranslatableMarkup { - return $this->t('View in Collabora Online'); - } + return CoolUtils::getEditorUrl($entity, FALSE); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultLabel(): TranslatableMarkup { + return $this->t('View in Collabora Online'); + } } diff --git a/tests/src/ExistingSiteJavascript/CollaboraIntegrationTest.php b/tests/src/ExistingSiteJavascript/CollaboraIntegrationTest.php index 2ae06bdb..afde4eb0 100644 --- a/tests/src/ExistingSiteJavascript/CollaboraIntegrationTest.php +++ b/tests/src/ExistingSiteJavascript/CollaboraIntegrationTest.php @@ -21,71 +21,71 @@ */ class CollaboraIntegrationTest extends ExistingSiteSelenium2DriverTestBase { - /** - * Tests the Collabora editor in readonly mode. - */ - public function testCollaboraPreview(): void { - $user = $this->createUser([ - 'preview document in collabora', - ]); - $this->drupalLogin($user); - $media = $this->createDocumentMedia('Shopping list', 'shopping-list', 'Chocolate, pickles'); - $this->drupalGet('/cool/view/' . $media->id()); - $this->getSession()->switchToIFrame('collabora-online-viewer'); + /** + * Tests the Collabora editor in readonly mode. + */ + public function testCollaboraPreview(): void { + $user = $this->createUser([ + 'preview document in collabora', + ]); + $this->drupalLogin($user); + $media = $this->createDocumentMedia('Shopping list', 'shopping-list', 'Chocolate, pickles'); + $this->drupalGet('/cool/view/' . $media->id()); + $this->getSession()->switchToIFrame('collabora-online-viewer'); - $assert_session = $this->assertSession(); - $canvas = $assert_session->waitForElement('css', 'canvas#document-canvas'); - $this->assertNotNull($canvas, 'The canvas element was not found after 10 seconds.'); + $assert_session = $this->assertSession(); + $canvas = $assert_session->waitForElement('css', 'canvas#document-canvas'); + $this->assertNotNull($canvas, 'The canvas element was not found after 10 seconds.'); - $document_field = $assert_session->waitForElement('css', 'input#document-name-input'); - $this->assertNotNull($document_field, 'The document name input was not found after 10 seconds.'); - $this->getCurrentPage()->waitFor(10, function () use ($document_field) { - return $document_field->getValue() === 'shopping-list.txt'; - }); - $this->assertEquals('shopping-list.txt', $document_field->getValue(), 'The document name input did not contain the correct value after 10 seconds.'); + $document_field = $assert_session->waitForElement('css', 'input#document-name-input'); + $this->assertNotNull($document_field, 'The document name input was not found after 10 seconds.'); + $this->getCurrentPage()->waitFor(10, function () use ($document_field) { + return $document_field->getValue() === 'shopping-list.txt'; + }); + $this->assertEquals('shopping-list.txt', $document_field->getValue(), 'The document name input did not contain the correct value after 10 seconds.'); - $word_count_element = $assert_session->waitForElement('css', 'div#StateWordCount'); - $this->assertNotNull($word_count_element, 'The word count element was not found after 10 seconds.'); - $this->getCurrentPage()->waitFor(10, function () use ($word_count_element) { - return $word_count_element->getText() === '2 words, 18 characters'; - }); - $this->assertEquals('2 words, 18 characters', $word_count_element->getText(), 'The word count element did not contain the correct text after 10 seconds.'); - } + $word_count_element = $assert_session->waitForElement('css', 'div#StateWordCount'); + $this->assertNotNull($word_count_element, 'The word count element was not found after 10 seconds.'); + $this->getCurrentPage()->waitFor(10, function () use ($word_count_element) { + return $word_count_element->getText() === '2 words, 18 characters'; + }); + $this->assertEquals('2 words, 18 characters', $word_count_element->getText(), 'The word count element did not contain the correct text after 10 seconds.'); + } - /** - * Creates a media entity with an attached *.txt file. - * - * The *.txt format is enough to test the basic functionality. - * - * @param string $media_name - * Media label. - * @param string $file_basename - * File name without the extension. - * @param string $text_content - * Content for the attached *.txt file. - * - * @return \Drupal\media\MediaInterface - * New media entity. - */ - protected function createDocumentMedia(string $media_name, string $file_basename, string $text_content): MediaInterface { - $file_uri = 'public://' . $file_basename . '.txt'; - file_put_contents($file_uri, $text_content); - $file = File::create([ - 'uri' => $file_uri, - ]); - $file->save(); - $this->markEntityForCleanup($file); - $values = [ - 'bundle' => 'document', - 'name' => $media_name, - 'title' => $media_name, - 'label' => $media_name, - 'field_media_file' => $file->id(), - ]; - $media = Media::create($values); - $media->save(); - $this->markEntityForCleanup($media); - return $media; - } + /** + * Creates a media entity with an attached *.txt file. + * + * The *.txt format is enough to test the basic functionality. + * + * @param string $media_name + * Media label. + * @param string $file_basename + * File name without the extension. + * @param string $text_content + * Content for the attached *.txt file. + * + * @return \Drupal\media\MediaInterface + * New media entity. + */ + protected function createDocumentMedia(string $media_name, string $file_basename, string $text_content): MediaInterface { + $file_uri = 'public://' . $file_basename . '.txt'; + file_put_contents($file_uri, $text_content); + $file = File::create([ + 'uri' => $file_uri, + ]); + $file->save(); + $this->markEntityForCleanup($file); + $values = [ + 'bundle' => 'document', + 'name' => $media_name, + 'title' => $media_name, + 'label' => $media_name, + 'field_media_file' => $file->id(), + ]; + $media = Media::create($values); + $media->save(); + $this->markEntityForCleanup($media); + return $media; + } } diff --git a/tests/src/Kernel/CollaboraMediaAccessTest.php b/tests/src/Kernel/CollaboraMediaAccessTest.php index b9b6d814..91c9aaac 100644 --- a/tests/src/Kernel/CollaboraMediaAccessTest.php +++ b/tests/src/Kernel/CollaboraMediaAccessTest.php @@ -19,276 +19,276 @@ */ class CollaboraMediaAccessTest extends KernelTestBase { - use MediaTypeCreationTrait; - use UserCreationTrait; - use MediaCreationTrait; + use MediaTypeCreationTrait; + use UserCreationTrait; + use MediaCreationTrait; - /** - * {@inheritdoc} - */ - protected static $modules = [ - 'collabora_online', - 'user', - 'media', - 'field', - 'image', - 'system', - 'file', - ]; + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'collabora_online', + 'user', + 'media', + 'field', + 'image', + 'system', + 'file', + ]; - /** - * {@inheritdoc} - */ - protected function setUp(): void { - parent::setUp(); + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); - $this->installEntitySchema('user'); - $this->installEntitySchema('file'); - $this->installSchema('file', 'file_usage'); - $this->installEntitySchema('media'); - $this->installConfig([ - 'field', - 'system', - 'user', - 'image', - 'file', - 'media', - ]); + $this->installEntitySchema('user'); + $this->installEntitySchema('file'); + $this->installSchema('file', 'file_usage'); + $this->installEntitySchema('media'); + $this->installConfig([ + 'field', + 'system', + 'user', + 'image', + 'file', + 'media', + ]); - $this->createMediaType('file', ['id' => 'document']); - $this->createMediaType('file', ['id' => 'book']); + $this->createMediaType('file', ['id' => 'document']); + $this->createMediaType('file', ['id' => 'book']); - // Consume the user id 1. - $this->createUser(); - } + // Consume the user id 1. + $this->createUser(); + } - /** - * Tests media access for Collabora routes and operations. - */ - public function testCollaboraMediaAccess(): void { - $this->assertCollaboraMediaAccess( - [], - new AnonymousUserSession(), - 'No access for anonymous without permissions', - ); + /** + * Tests media access for Collabora routes and operations. + */ + public function testCollaboraMediaAccess(): void { + $this->assertCollaboraMediaAccess( + [], + new AnonymousUserSession(), + 'No access for anonymous without permissions', + ); - // Check authenticated with irrelevant permissions. - // This also covers the "no permissions" case. - $this->assertCollaboraMediaAccess( - [], - $this->createUser([ - // Add general 'media' permissions. - 'administer media types', - 'view media', - 'update any media', - 'view own unpublished media', - // Add Collabora permissions for a different media type. - 'preview book in collabora', - 'preview own unpublished book in collabora', - 'edit any book in collabora', - 'edit own book in collabora', - ]), - 'No access with irrelevant permissions', - ); + // Check authenticated with irrelevant permissions. + // This also covers the "no permissions" case. + $this->assertCollaboraMediaAccess( + [], + $this->createUser([ + // Add general 'media' permissions. + 'administer media types', + 'view media', + 'update any media', + 'view own unpublished media', + // Add Collabora permissions for a different media type. + 'preview book in collabora', + 'preview own unpublished book in collabora', + 'edit any book in collabora', + 'edit own book in collabora', + ]), + 'No access with irrelevant permissions', + ); - $this->assertCollaboraMediaAccessForPermission( - [ - 'published document' => ['preview'], - 'own published document' => ['preview'], - ], - 'preview document in collabora', - ); + $this->assertCollaboraMediaAccessForPermission( + [ + 'published document' => ['preview'], + 'own published document' => ['preview'], + ], + 'preview document in collabora', + ); - $this->assertCollaboraMediaAccessForPermission( - [ - 'own unpublished document' => ['preview'], - ], - 'preview own unpublished document in collabora', - ); + $this->assertCollaboraMediaAccessForPermission( + [ + 'own unpublished document' => ['preview'], + ], + 'preview own unpublished document in collabora', + ); - $this->assertCollaboraMediaAccessForPermission( - [ - 'published document' => ['edit'], - 'unpublished document' => ['edit'], - 'own published document' => ['edit'], - 'own unpublished document' => ['edit'], - ], - 'edit any document in collabora', - ); + $this->assertCollaboraMediaAccessForPermission( + [ + 'published document' => ['edit'], + 'unpublished document' => ['edit'], + 'own published document' => ['edit'], + 'own unpublished document' => ['edit'], + ], + 'edit any document in collabora', + ); - $this->assertCollaboraMediaAccessForPermission( - [ - 'own published document' => ['edit'], - 'own unpublished document' => ['edit'], - ], - 'edit own document in collabora', - ); + $this->assertCollaboraMediaAccessForPermission( + [ + 'own published document' => ['edit'], + 'own unpublished document' => ['edit'], + ], + 'edit own document in collabora', + ); - // The 'administer media' permission grants access to everything. - $this->assertCollaboraMediaAccessForPermission( - [ - 'published document' => ['preview', 'edit'], - 'unpublished document' => ['preview', 'edit'], - 'own published document' => ['preview', 'edit'], - 'own unpublished document' => ['preview', 'edit'], - ], - 'administer media', - ); + // The 'administer media' permission grants access to everything. + $this->assertCollaboraMediaAccessForPermission( + [ + 'published document' => ['preview', 'edit'], + 'unpublished document' => ['preview', 'edit'], + 'own published document' => ['preview', 'edit'], + 'own unpublished document' => ['preview', 'edit'], + ], + 'administer media', + ); - $this->assertCollaboraMediaAccess( - [ - 'published document' => ['preview', 'edit'], - 'unpublished document' => ['preview', 'edit'], - 'own published document' => ['preview', 'edit'], - 'own unpublished document' => ['preview', 'edit'], - ], - $this->createUser(admin: TRUE), - "Access for admin user", - ); - } + $this->assertCollaboraMediaAccess( + [ + 'published document' => ['preview', 'edit'], + 'unpublished document' => ['preview', 'edit'], + 'own published document' => ['preview', 'edit'], + 'own unpublished document' => ['preview', 'edit'], + ], + $this->createUser(admin: TRUE), + "Access for admin user", + ); + } - /** - * Tests scenarios where the anonymous user has more permissions. - * - * This verifies the special treatment of uid 0 to determine the owner of a - * media entity. - */ - public function testAnonymousOwnAccess(): void { - user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, [ - 'preview own unpublished document in collabora', - 'edit own document in collabora', - ]); - $this->assertCollaboraMediaAccess( - [], - new AnonymousUserSession(), - "Anonymous user with '... own ...' permissions.", - ); + /** + * Tests scenarios where the anonymous user has more permissions. + * + * This verifies the special treatment of uid 0 to determine the owner of a + * media entity. + */ + public function testAnonymousOwnAccess(): void { + user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, [ + 'preview own unpublished document in collabora', + 'edit own document in collabora', + ]); + $this->assertCollaboraMediaAccess( + [], + new AnonymousUserSession(), + "Anonymous user with '... own ...' permissions.", + ); - user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, [ - 'preview document in collabora', - 'edit any document in collabora', - ]); - $this->assertCollaboraMediaAccess( - [ - 'published document' => ['preview', 'edit'], - 'unpublished document' => ['edit'], - 'own published document' => ['preview', 'edit'], - 'own unpublished document' => ['edit'], - ], - new AnonymousUserSession(), - "Anonymous user with all Collabora media permissions.", - ); - } + user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, [ + 'preview document in collabora', + 'edit any document in collabora', + ]); + $this->assertCollaboraMediaAccess( + [ + 'published document' => ['preview', 'edit'], + 'unpublished document' => ['edit'], + 'own published document' => ['preview', 'edit'], + 'own unpublished document' => ['edit'], + ], + new AnonymousUserSession(), + "Anonymous user with all Collabora media permissions.", + ); + } - /** - * Creates a user with one permission, and asserts access to media entities. - * - * @param array> $expected - * Expected access. - * @param string $permission - * Permission machine name. - */ - protected function assertCollaboraMediaAccessForPermission(array $expected, string $permission): void { - $account = $this->createUser([$permission]); - $message = "User with '$permission' permission."; - $this->assertCollaboraMediaAccess($expected, $account, $message); - } + /** + * Creates a user with one permission, and asserts access to media entities. + * + * @param array> $expected + * Expected access. + * @param string $permission + * Permission machine name. + */ + protected function assertCollaboraMediaAccessForPermission(array $expected, string $permission): void { + $account = $this->createUser([$permission]); + $message = "User with '$permission' permission."; + $this->assertCollaboraMediaAccess($expected, $account, $message); + } - /** - * Asserts Collabora media access for a user account. - * - * @param array $expected - * Expected access matrix. - * The keys identify media entities that are created in this test. - * The values identify operations. - * @param \Drupal\Core\Session\AccountInterface $account - * Account to check access for. - * @param string $message - * Message for the assertion. - */ - protected function assertCollaboraMediaAccess(array $expected, AccountInterface $account, string $message): void { - $other_user = $this->createUser(); - $entities = [ - "published document" => $this->createMediaEntity('document', [ - 'uid' => $other_user->id(), - ]), - "unpublished document" => $this->createMediaEntity('document', [ - 'uid' => $other_user->id(), - 'status' => 0, - ]), - "own published document" => $this->createMediaEntity('document', [ - 'uid' => $account->id(), - ]), - "own unpublished document" => $this->createMediaEntity('document', [ - 'uid' => $account->id(), - 'status' => 0, - ]), - ]; + /** + * Asserts Collabora media access for a user account. + * + * @param array $expected + * Expected access matrix. + * The keys identify media entities that are created in this test. + * The values identify operations. + * @param \Drupal\Core\Session\AccountInterface $account + * Account to check access for. + * @param string $message + * Message for the assertion. + */ + protected function assertCollaboraMediaAccess(array $expected, AccountInterface $account, string $message): void { + $other_user = $this->createUser(); + $entities = [ + "published document" => $this->createMediaEntity('document', [ + 'uid' => $other_user->id(), + ]), + "unpublished document" => $this->createMediaEntity('document', [ + 'uid' => $other_user->id(), + 'status' => 0, + ]), + "own published document" => $this->createMediaEntity('document', [ + 'uid' => $account->id(), + ]), + "own unpublished document" => $this->createMediaEntity('document', [ + 'uid' => $account->id(), + 'status' => 0, + ]), + ]; - // Test $entity->access() with different operations on all entities. - $operations = [ - 'preview' => 'preview in collabora', - 'edit' => 'edit in collabora', - ]; - $actual_entity_access = []; - foreach ($entities as $entity_key => $entity) { - foreach ($operations as $operation_key => $operation) { - $has_entity_access = $entity->access($operation, $account); - if ($has_entity_access) { - $actual_entity_access[$entity_key][] = $operation_key; - } - } + // Test $entity->access() with different operations on all entities. + $operations = [ + 'preview' => 'preview in collabora', + 'edit' => 'edit in collabora', + ]; + $actual_entity_access = []; + foreach ($entities as $entity_key => $entity) { + foreach ($operations as $operation_key => $operation) { + $has_entity_access = $entity->access($operation, $account); + if ($has_entity_access) { + $actual_entity_access[$entity_key][] = $operation_key; } - $this->assertSameYaml( - $expected, - $actual_entity_access, - 'Entity access: ' . $message, - ); + } + } + $this->assertSameYaml( + $expected, + $actual_entity_access, + 'Entity access: ' . $message, + ); - // Test path access. - // The result is expected to be exactly the same, due to how the route - // access is configured. - // Testing the paths like this introduces some level of redundancy or - // duplication, but it is cheap and easy, so for now this is what we do. - $sprintf_path_patterns = [ - 'preview' => '/cool/view/%s', - 'edit' => '/cool/edit/%s', - ]; - $actual_path_access = []; - foreach ($entities as $entity_key => $entity) { - foreach ($sprintf_path_patterns as $pattern_key => $sprintf_path_pattern) { - $path = sprintf($sprintf_path_pattern, $entity->id()); - $has_path_access = Url::fromUserInput($path)->access($account); - if ($has_path_access) { - $actual_path_access[$entity_key][] = $pattern_key; - } - } + // Test path access. + // The result is expected to be exactly the same, due to how the route + // access is configured. + // Testing the paths like this introduces some level of redundancy or + // duplication, but it is cheap and easy, so for now this is what we do. + $sprintf_path_patterns = [ + 'preview' => '/cool/view/%s', + 'edit' => '/cool/edit/%s', + ]; + $actual_path_access = []; + foreach ($entities as $entity_key => $entity) { + foreach ($sprintf_path_patterns as $pattern_key => $sprintf_path_pattern) { + $path = sprintf($sprintf_path_pattern, $entity->id()); + $has_path_access = Url::fromUserInput($path)->access($account); + if ($has_path_access) { + $actual_path_access[$entity_key][] = $pattern_key; } - $this->assertSameYaml( - $expected, - $actual_path_access, - 'Path access: ' . $message, - ); + } } + $this->assertSameYaml( + $expected, + $actual_path_access, + 'Path access: ' . $message, + ); + } - /** - * Asserts that two values are the same when exported to yaml. - * - * This provides a nicer diff output, without numeric array keys. - * - * @param mixed $expected - * Expected value. - * @param mixed $actual - * Actual value. - * @param string $message - * Message. - */ - protected function assertSameYaml(mixed $expected, mixed $actual, string $message = ''): void { - $this->assertSame( - "\n" . Yaml::encode($expected), - "\n" . Yaml::encode($actual), - $message, - ); - } + /** + * Asserts that two values are the same when exported to yaml. + * + * This provides a nicer diff output, without numeric array keys. + * + * @param mixed $expected + * Expected value. + * @param mixed $actual + * Actual value. + * @param string $message + * Message. + */ + protected function assertSameYaml(mixed $expected, mixed $actual, string $message = ''): void { + $this->assertSame( + "\n" . Yaml::encode($expected), + "\n" . Yaml::encode($actual), + $message, + ); + } } diff --git a/tests/src/Kernel/PermissionTest.php b/tests/src/Kernel/PermissionTest.php index 20f9739a..aa0a8be1 100644 --- a/tests/src/Kernel/PermissionTest.php +++ b/tests/src/Kernel/PermissionTest.php @@ -24,87 +24,87 @@ */ class PermissionTest extends KernelTestBase { - use MediaTypeCreationTrait; - use UserCreationTrait; + use MediaTypeCreationTrait; + use UserCreationTrait; - /** - * {@inheritdoc} - */ - protected static $modules = [ - 'collabora_online', - 'media', - 'user', - 'field', - 'system', - 'file', - 'image', - ]; + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'collabora_online', + 'media', + 'user', + 'field', + 'system', + 'file', + 'image', + ]; - /** - * {@inheritdoc} - */ - protected function setUp(): void { - parent::setUp(); + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); - $this->installEntitySchema('user'); - $this->installEntitySchema('file'); - $this->installSchema('file', 'file_usage'); - $this->installEntitySchema('media'); - $this->installConfig(['field', 'system', 'user', 'file', 'media']); - } + $this->installEntitySchema('user'); + $this->installEntitySchema('file'); + $this->installSchema('file', 'file_usage'); + $this->installEntitySchema('media'); + $this->installConfig(['field', 'system', 'user', 'file', 'media']); + } - /** - * Tests that dynamic permissions are properly created. - */ - public function testDynamicPermissions(): void { - $this->createMediaType('file', [ - 'id' => 'public_wiki', - 'label' => 'Public wiki', - ]); - /** @var \Drupal\user\PermissionHandlerInterface $permission_handler */ - $permission_handler = \Drupal::service(PermissionHandlerInterface::class); - $permissions = $permission_handler->getPermissions(); - $permissions = array_filter( - $permissions, - fn (array $permission) => $permission['provider'] === 'collabora_online', - ); - // Remove noise that is hard to diff. - $permissions = array_map( - static function (array $permission) { - $permission['title'] = (string) $permission['title']; - if ($permission['description'] === NULL) { - unset($permission['description']); - } - if ($permission['provider'] === 'collabora_online') { - unset($permission['provider']); - } - return $permission; - }, - $permissions, - ); - ksort($permissions); - $this->assertSame([ - 'administer collabora instance' => [ - 'title' => 'Administer the Collabora instance', - 'restrict access' => TRUE, - ], - 'edit any public_wiki in collabora' => [ - 'title' => 'Public wiki: Edit any media file in Collabora', - 'dependencies' => ['config' => ['media.type.public_wiki']], - ], - 'edit own public_wiki in collabora' => [ - 'title' => 'Public wiki: Edit own media file in Collabora', - 'dependencies' => ['config' => ['media.type.public_wiki']], - ], - 'preview own unpublished public_wiki in collabora' => [ - 'title' => 'Public wiki: Preview own unpublished media file in Collabora', - 'dependencies' => ['config' => ['media.type.public_wiki']], - ], - 'preview public_wiki in collabora' => [ - 'title' => 'Public wiki: Preview published media file in Collabora', - 'dependencies' => ['config' => ['media.type.public_wiki']], - ], - ], $permissions); - } + /** + * Tests that dynamic permissions are properly created. + */ + public function testDynamicPermissions(): void { + $this->createMediaType('file', [ + 'id' => 'public_wiki', + 'label' => 'Public wiki', + ]); + /** @var \Drupal\user\PermissionHandlerInterface $permission_handler */ + $permission_handler = \Drupal::service(PermissionHandlerInterface::class); + $permissions = $permission_handler->getPermissions(); + $permissions = array_filter( + $permissions, + fn (array $permission) => $permission['provider'] === 'collabora_online', + ); + // Remove noise that is hard to diff. + $permissions = array_map( + static function (array $permission) { + $permission['title'] = (string) $permission['title']; + if ($permission['description'] === NULL) { + unset($permission['description']); + } + if ($permission['provider'] === 'collabora_online') { + unset($permission['provider']); + } + return $permission; + }, + $permissions, + ); + ksort($permissions); + $this->assertSame([ + 'administer collabora instance' => [ + 'title' => 'Administer the Collabora instance', + 'restrict access' => TRUE, + ], + 'edit any public_wiki in collabora' => [ + 'title' => 'Public wiki: Edit any media file in Collabora', + 'dependencies' => ['config' => ['media.type.public_wiki']], + ], + 'edit own public_wiki in collabora' => [ + 'title' => 'Public wiki: Edit own media file in Collabora', + 'dependencies' => ['config' => ['media.type.public_wiki']], + ], + 'preview own unpublished public_wiki in collabora' => [ + 'title' => 'Public wiki: Preview own unpublished media file in Collabora', + 'dependencies' => ['config' => ['media.type.public_wiki']], + ], + 'preview public_wiki in collabora' => [ + 'title' => 'Public wiki: Preview published media file in Collabora', + 'dependencies' => ['config' => ['media.type.public_wiki']], + ], + ], $permissions); + } } diff --git a/tests/src/Kernel/ViewsLinkFieldsTest.php b/tests/src/Kernel/ViewsLinkFieldsTest.php index 10c4cbc2..a5db12a3 100644 --- a/tests/src/Kernel/ViewsLinkFieldsTest.php +++ b/tests/src/Kernel/ViewsLinkFieldsTest.php @@ -18,157 +18,157 @@ */ class ViewsLinkFieldsTest extends KernelTestBase { - use UserCreationTrait; - use MediaCreationTrait; - use MediaTypeCreationTrait; + use UserCreationTrait; + use MediaCreationTrait; + use MediaTypeCreationTrait; - /** - * {@inheritdoc} - */ - protected static $modules = [ - 'collabora_online', - 'collabora_online_test', - 'field', - 'file', - 'image', - 'media', - 'system', - 'user', - 'views', - ]; + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'collabora_online', + 'collabora_online_test', + 'field', + 'file', + 'image', + 'media', + 'system', + 'user', + 'views', + ]; - /** - * {@inheritdoc} - */ - protected function setUp(): void { - parent::setUp(); + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); - $this->installEntitySchema('file'); - $this->installEntitySchema('media'); - $this->installEntitySchema('user'); - $this->installConfig(['user', 'views', 'collabora_online_test']); - $this->installSchema('file', ['file_usage']); - // Install user module to avoid user 1 permissions bypass. - \Drupal::moduleHandler()->loadInclude('user', 'install'); - user_install(); - } + $this->installEntitySchema('file'); + $this->installEntitySchema('media'); + $this->installEntitySchema('user'); + $this->installConfig(['user', 'views', 'collabora_online_test']); + $this->installSchema('file', ['file_usage']); + // Install user module to avoid user 1 permissions bypass. + \Drupal::moduleHandler()->loadInclude('user', 'install'); + user_install(); + } - /** - * Tests link fields. - */ - public function testLinks(): void { - // User without permissions can't see links. - $this->doTestLinks( - [ - 'preview' => [FALSE, FALSE, FALSE, FALSE], - 'edit' => [FALSE, FALSE, FALSE, FALSE], - ], - $this->createUser([]) - ); - // User with 'Preview' permission can see preview link. - $this->doTestLinks( - [ - 'preview' => [TRUE, FALSE, TRUE, FALSE], - 'edit' => [FALSE, FALSE, FALSE, FALSE], - ], - $this->createUser([ - 'preview document in collabora', - ]) - ); - // User with 'Preview own unpublished' permission can see preview link - // for unpublished entity they own. - $this->doTestLinks( - [ - 'preview' => [FALSE, FALSE, FALSE, TRUE], - 'edit' => [FALSE, FALSE, FALSE, FALSE], - ], - $this->createUser([ - 'preview own unpublished document in collabora', - ]) - ); - // User with 'Edit any' permission can see edit link. - $this->doTestLinks( - [ - 'preview' => [FALSE, FALSE, FALSE, FALSE], - 'edit' => [TRUE, TRUE, TRUE, TRUE], - ], - $this->createUser([ - 'edit any document in collabora', - ]) - ); - // User with 'Edit own' permission can see edit link for entities they - // own. - $this->doTestLinks( - [ - 'preview' => [FALSE, FALSE, FALSE, FALSE], - 'edit' => [FALSE, FALSE, TRUE, TRUE], - ], - $this->createUser([ - 'edit own document in collabora', - ]) - ); - } + /** + * Tests link fields. + */ + public function testLinks(): void { + // User without permissions can't see links. + $this->doTestLinks( + [ + 'preview' => [FALSE, FALSE, FALSE, FALSE], + 'edit' => [FALSE, FALSE, FALSE, FALSE], + ], + $this->createUser([]) + ); + // User with 'Preview' permission can see preview link. + $this->doTestLinks( + [ + 'preview' => [TRUE, FALSE, TRUE, FALSE], + 'edit' => [FALSE, FALSE, FALSE, FALSE], + ], + $this->createUser([ + 'preview document in collabora', + ]) + ); + // User with 'Preview own unpublished' permission can see preview link + // for unpublished entity they own. + $this->doTestLinks( + [ + 'preview' => [FALSE, FALSE, FALSE, TRUE], + 'edit' => [FALSE, FALSE, FALSE, FALSE], + ], + $this->createUser([ + 'preview own unpublished document in collabora', + ]) + ); + // User with 'Edit any' permission can see edit link. + $this->doTestLinks( + [ + 'preview' => [FALSE, FALSE, FALSE, FALSE], + 'edit' => [TRUE, TRUE, TRUE, TRUE], + ], + $this->createUser([ + 'edit any document in collabora', + ]) + ); + // User with 'Edit own' permission can see edit link for entities they + // own. + $this->doTestLinks( + [ + 'preview' => [FALSE, FALSE, FALSE, FALSE], + 'edit' => [FALSE, FALSE, TRUE, TRUE], + ], + $this->createUser([ + 'edit own document in collabora', + ]) + ); + } - /** - * Tests that links behave as expected. - * - * @param array $expected_results - * An associative array of expected results keyed by operation. - * @param \Drupal\Core\Session\AccountInterface $account - * The user account to be used to run the test. - */ - protected function doTestLinks(array $expected_results, AccountInterface $account): void { - $this->setCurrentUser($account); - // Create medias: cover all combinations of status and ownership. - $this->createMediaEntity('document', [ - 'uid' => $this->createUser(), - ]); - $this->createMediaEntity('document', [ - 'uid' => $this->createUser(), - 'status' => 0, - ]); - $this->createMediaEntity('document', [ - 'uid' => $account->id(), - ]); - $this->createMediaEntity('document', [ - 'uid' => $account->id(), - 'status' => 0, - ]); + /** + * Tests that links behave as expected. + * + * @param array $expected_results + * An associative array of expected results keyed by operation. + * @param \Drupal\Core\Session\AccountInterface $account + * The user account to be used to run the test. + */ + protected function doTestLinks(array $expected_results, AccountInterface $account): void { + $this->setCurrentUser($account); + // Create medias: cover all combinations of status and ownership. + $this->createMediaEntity('document', [ + 'uid' => $this->createUser(), + ]); + $this->createMediaEntity('document', [ + 'uid' => $this->createUser(), + 'status' => 0, + ]); + $this->createMediaEntity('document', [ + 'uid' => $account->id(), + ]); + $this->createMediaEntity('document', [ + 'uid' => $account->id(), + 'status' => 0, + ]); - $view = Views::getView('test_collabora_links'); - $view->preview(); + $view = Views::getView('test_collabora_links'); + $view->preview(); - $info = [ - 'preview' => [ - 'label' => 'View in Collabora Online', - 'field_id' => 'collabora_preview', - 'route' => 'collabora-online.view', - ], - 'edit' => [ - 'label' => 'Edit in Collabora Online', - 'field_id' => 'collabora_edit', - 'route' => 'collabora-online.edit', - ], - ]; + $info = [ + 'preview' => [ + 'label' => 'View in Collabora Online', + 'field_id' => 'collabora_preview', + 'route' => 'collabora-online.view', + ], + 'edit' => [ + 'label' => 'Edit in Collabora Online', + 'field_id' => 'collabora_edit', + 'route' => 'collabora-online.edit', + ], + ]; - // Check each expected results for every media. - $i = 0; - foreach (Media::loadMultiple() as $media) { - foreach ($expected_results as $operation => $expected_result) { - $expected_link = ''; - // The operations array contains results for each entity. - if ($expected_result[$i]) { - $path = Url::fromRoute($info[$operation]['route'], ['media' => $media->id()])->toString(); - $expected_link = '' . $info[$operation]['label'] . ''; - } - // We check the output: link HTML or empty (access denied). - $link = $view->style_plugin->getField($i, $info[$operation]['field_id']); - $this->assertEquals($expected_link, (string) $link); - } - $i++; - // Clean medias as we check results. - $media->delete(); + // Check each expected results for every media. + $i = 0; + foreach (Media::loadMultiple() as $media) { + foreach ($expected_results as $operation => $expected_result) { + $expected_link = ''; + // The operations array contains results for each entity. + if ($expected_result[$i]) { + $path = Url::fromRoute($info[$operation]['route'], ['media' => $media->id()])->toString(); + $expected_link = '' . $info[$operation]['label'] . ''; } + // We check the output: link HTML or empty (access denied). + $link = $view->style_plugin->getField($i, $info[$operation]['field_id']); + $this->assertEquals($expected_link, (string) $link); + } + $i++; + // Clean medias as we check results. + $media->delete(); } + } } diff --git a/tests/src/Traits/MediaCreationTrait.php b/tests/src/Traits/MediaCreationTrait.php index 32b59310..f4ae1317 100644 --- a/tests/src/Traits/MediaCreationTrait.php +++ b/tests/src/Traits/MediaCreationTrait.php @@ -27,12 +27,12 @@ trait MediaCreationTrait { protected function createMediaEntity(string $type, array $values = []): MediaInterface { file_put_contents('public://test.txt', 'Hello test'); $file = File::create([ - 'uri' => 'public://test.txt', + 'uri' => 'public://test.txt', ]); $file->save(); $values += [ - 'bundle' => $type, - 'field_media_file' => $file->id(), + 'bundle' => $type, + 'field_media_file' => $file->id(), ]; $media = Media::create($values); $media->save(); From 8b5da2e6d6aaa2573c8f46ef075fe265cde7429c Mon Sep 17 00:00:00 2001 From: Andreas Hennings Date: Wed, 27 Nov 2024 13:00:29 +0100 Subject: [PATCH 3/3] Issue #61: Fix indentation in js and css. --- css/cool.css | 34 +++++++++++----------- js/cool.js | 76 ++++++++++++++++++++++++------------------------- js/previewer.js | 42 +++++++++++++-------------- 3 files changed, 76 insertions(+), 76 deletions(-) diff --git a/css/cool.css b/css/cool.css index 5b5c6395..1b829c4c 100644 --- a/css/cool.css +++ b/css/cool.css @@ -14,30 +14,30 @@ } .cool-frame__preview { - width: 100%; - height: 100%; - border: 0; + width: 100%; + height: 100%; + border: 0; } .cool-frame__iframe { - border: 1px solid; - width: 100%; - height: 100%; - position: absolute; - box-sizing: border-box; + border: 1px solid; + width: 100%; + height: 100%; + position: absolute; + box-sizing: border-box; } .cool-editor__dialog { - resize: both; - position: fixed; - top: 250px; - width: 100%; - height: calc(100% - 250px); - /* seems to be a reasonable value to be above of it all */ - z-index: 501; - box-sizing: border-box; + resize: both; + position: fixed; + top: 250px; + width: 100%; + height: calc(100% - 250px); + /* seems to be a reasonable value to be above of it all */ + z-index: 501; + box-sizing: border-box; } .cool-editor__body { - margin: 0; + margin: 0; } diff --git a/js/cool.js b/js/cool.js index 95dd249d..80a49dd7 100644 --- a/js/cool.js +++ b/js/cool.js @@ -10,60 +10,60 @@ */ function loadDocument(wopiClient, wopiSrc, options = null) { - let hasCloseButton = false; - let wopiUrl = `${wopiClient}WOPISrc=${wopiSrc}`; - if (options && options.closebutton == true) { - wopiUrl += '&closebutton=true'; - hasCloseButton = true; - } + let hasCloseButton = false; + let wopiUrl = `${wopiClient}WOPISrc=${wopiSrc}`; + if (options && options.closebutton == true) { + wopiUrl += '&closebutton=true'; + hasCloseButton = true; + } - window.addEventListener("message", receiveMessage.bind(null, hasCloseButton), false); + window.addEventListener("message", receiveMessage.bind(null, hasCloseButton), false); - let formElem = document.getElementById("collabora-submit-form"); + let formElem = document.getElementById("collabora-submit-form"); - if (!formElem) { - console.log("error: submit form not found"); - return; - } - formElem.action = wopiUrl; - formElem.submit(); + if (!formElem) { + console.log("error: submit form not found"); + return; + } + formElem.action = wopiUrl; + formElem.submit(); } function postMessage(msg) { - document.getElementById("collabora-online-viewer").contentWindow.postMessage(JSON.stringify(msg), '*'); + document.getElementById("collabora-online-viewer").contentWindow.postMessage(JSON.stringify(msg), '*'); } function postReady() { - postMessage({ MessageId: "Host_PostmessageReady" }); + postMessage({ MessageId: "Host_PostmessageReady" }); } function receiveMessage(hasCloseButton, event) { - let msg = JSON.parse(event.data); - if (!msg) { - return; - } + let msg = JSON.parse(event.data); + if (!msg) { + return; + } - switch (msg.MessageId) { + switch (msg.MessageId) { case "App_LoadingStatus": - if (msg.Values && msg.Values.Status == "Document_Loaded") { - postReady(); - } - break; + if (msg.Values && msg.Values.Status == "Document_Loaded") { + postReady(); + } + break; case "UI_Close": - if (hasCloseButton) { - if (msg.Values && msg.Values.EverModified) { - let reply = { MessageId: "Action_Close" }; - postMessage(reply); - } - if (window.parent.location == window.location) { - history.back(); - } else { - /* we send back the UI_Close message to the parent frame. */ - window.parent.postMessage(event.data); - } + if (hasCloseButton) { + if (msg.Values && msg.Values.EverModified) { + let reply = { MessageId: "Action_Close" }; + postMessage(reply); + } + if (window.parent.location == window.location) { + history.back(); + } else { + /* we send back the UI_Close message to the parent frame. */ + window.parent.postMessage(event.data); } - break; - } + } + break; + } } diff --git a/js/previewer.js b/js/previewer.js index fdf072c5..eebdb781 100644 --- a/js/previewer.js +++ b/js/previewer.js @@ -10,35 +10,35 @@ */ function previewField(coolUrl) { - let iframe = document.querySelector("#cool-editor__dialog > .cool-frame__preview"); - iframe.src = coolUrl; - document.querySelector("#cool-editor__dialog").show(); + let iframe = document.querySelector("#cool-editor__dialog > .cool-frame__preview"); + iframe.src = coolUrl; + document.querySelector("#cool-editor__dialog").show(); } function closePreview() { - let iframe = document.querySelector("#cool-editor__dialog > .cool-frame__preview"); - iframe.src = "about:blank"; - document.querySelector('#cool-editor__dialog').close(); + let iframe = document.querySelector("#cool-editor__dialog > .cool-frame__preview"); + iframe.src = "about:blank"; + document.querySelector('#cool-editor__dialog').close(); } (function () { - function receiveMessage(event) { - let msg = JSON.parse(event.data); - if (!msg) { - return; - } + function receiveMessage(event) { + let msg = JSON.parse(event.data); + if (!msg) { + return; + } - switch (msg.MessageId) { - case "App_LoadingStatus": - if (msg.Values && msg.Values.Status == "Document_Loaded") { - postReady(); - } - break; - case "UI_Close": - closePreview(); - break; + switch (msg.MessageId) { + case "App_LoadingStatus": + if (msg.Values && msg.Values.Status == "Document_Loaded") { + postReady(); } + break; + case "UI_Close": + closePreview(); + break; } + } - window.addEventListener("message", receiveMessage, false); + window.addEventListener("message", receiveMessage, false); })()