Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 2.0.x
  • 2.1.x
  • 2.2.x
  • 2.3.x
  • 3.0.x
  • 3.1.x
  • 3.2.x
  • 3.3.x
  • 4.0.x
  • 7.x-1.x
  • 8.x-1.x
  • feature/gc-entity-routes
  • feature/translate_group_content
  • feature/ui_handler
  • 2.0.0
  • 2.0.0-alpha1
  • 2.0.0-alpha2
  • 2.0.0-alpha3
  • 2.0.0-alpha4
  • 2.0.0-beta1
  • 2.0.0-beta2
  • 2.0.0-beta3
  • 2.0.0-beta4
  • 2.0.0-beta5
  • 2.0.0-beta6
  • 2.0.0-rc1
  • 2.0.0-rc2
  • 2.0.1
  • 2.1.0
  • 2.2.0
  • 2.2.1
  • 2.2.2
  • 2.3.0
  • 2.3.1
  • 3.0.0
  • 3.0.0-beta1
  • 3.0.0-beta2
  • 3.0.0-beta3
  • 3.0.0-beta4
  • 3.0.0-beta5
  • 3.0.0-beta6
  • 3.0.0-rc1
  • 3.0.0-rc2
  • 3.0.1
  • 3.1.0
  • 3.2.0
  • 3.2.1
  • 3.2.2
  • 3.3.0
  • 3.3.1
  • 3.3.2
  • 3.3.3
  • 3.3.4
  • 7.x-1.0-alpha1
  • 7.x-1.0-alpha2
  • 7.x-1.0-alpha3
  • 7.x-1.0-alpha4
  • 7.x-1.0-alpha5
  • 7.x-1.0-alpha6
  • 7.x-1.0-beta1
  • 7.x-1.0-beta2
  • 7.x-1.0-beta3
  • 7.x-1.0-beta4
  • 7.x-1.0-beta5
  • 7.x-1.0-beta6
  • 7.x-1.0-unstable1
  • 7.x-1.0-unstable2
  • 7.x-1.0-unstable3
  • 7.x-1.0-unstable4
  • 7.x-1.0-unstable5
  • 8.x-1.0
  • 8.x-1.0-alpha1
  • 8.x-1.0-alpha2
  • 8.x-1.0-alpha3
  • 8.x-1.0-alpha4
  • 8.x-1.0-alpha5
  • 8.x-1.0-alpha6
  • 8.x-1.0-alpha7
  • 8.x-1.0-beta1
  • 8.x-1.0-beta2
  • 8.x-1.0-beta3
  • 8.x-1.0-beta4
  • 8.x-1.0-beta5
  • 8.x-1.0-rc1
  • 8.x-1.0-rc2
  • 8.x-1.0-rc3
  • 8.x-1.0-rc4
  • 8.x-1.0-rc5
  • 8.x-1.1
  • 8.x-1.2
  • 8.x-1.3
  • 8.x-1.4
  • 8.x-1.5
  • 8.x-1.6
  • previous/3016197-group-creator-must/2023-10-13
  • previous/3332963-hookquerytagalter-implementations-get/2023-02-13
  • previous/3397063-drupal-10.1-revisions/2024-03-15
  • previous/3426098-add-cspell-integration/2024-03-12
  • previous/3426408-drop-ismethodcacheable-check/2024-03-12
  • previous/3427552-switch-to-php/2024-03-14
100 results

Target

Select target project
  • project/group
  • issue/group-3157478
  • issue/group-3084140
  • issue/group-3159277
  • issue/group-3163334
  • issue/group-3166252
  • issue/group-3167443
  • issue/group-3169377
  • issue/group-2797793
  • issue/group-2919982
  • issue/group-3177042
  • issue/group-3177542
  • issue/group-2801603
  • issue/group-3179857
  • issue/group-3052620
  • issue/group-2872645
  • issue/group-3007472
  • issue/group-3162259
  • issue/group-3183791
  • issue/group-3150877
  • issue/group-3185179
  • issue/group-3067811
  • issue/group-3189659
  • issue/group-3190190
  • issue/group-3190306
  • issue/group-2876696
  • issue/group-3118383
  • issue/group-3026735
  • issue/group-3194750
  • issue/group-3197122
  • issue/group-2746839
  • issue/group-3163683
  • issue/group-3201941
  • issue/group-3202091
  • issue/group-3202966
  • issue/group-3202249
  • issue/group-3045373
  • issue/group-3278740
  • issue/group-3209096
  • issue/group-3207064
  • issue/group-3214009
  • issue/group-3166107
  • issue/group-3214895
  • issue/group-2916907
  • issue/group-3220758
  • issue/group-3221785
  • issue/group-3222359
  • issue/group-3222747
  • issue/group-3223957
  • issue/group-2824018
  • issue/group-3226713
  • issue/group-3227793
  • issue/group-3228454
  • issue/group-3229567
  • issue/group-3232851
  • issue/group-3137312
  • issue/group-2295249
  • issue/group-3236626
  • issue/group-3281530
  • issue/group-3202908
  • issue/group-3278778
  • issue/group-3245095
  • issue/group-2972933
  • issue/group-3074933
  • issue/group-3162511
  • issue/group-3253368
  • issue/group-2767155
  • issue/group-2877493
  • issue/group-3254038
  • issue/group-3257227
  • issue/group-3257756
  • issue/group-2717981
  • issue/group-3259178
  • issue/group-3256998
  • issue/group-3204083
  • issue/group-3082125
  • issue/group-3265962
  • issue/group-3266146
  • issue/group-3262923
  • issue/group-3276613
  • issue/group-3005047
  • issue/group-3284402
  • issue/group-3071489
  • issue/group-3061881
  • issue/group-3305051
  • issue/group-2842630
  • issue/group-3301785
  • issue/group-3310499
  • issue/group-3311119
  • issue/group-3307282
  • issue/group-3307291
  • issue/group-2933819
  • issue/group-3310605
  • issue/group-2774729
  • issue/group-3311949
  • issue/group-3313220
  • issue/group-3317459
  • issue/group-3313515
  • issue/group-3315779
  • issue/group-2817109
  • issue/group-3319274
  • issue/group-3325789
  • issue/group-3320513
  • issue/group-3328660
  • issue/group-3210808
  • issue/group-3317396
  • issue/group-3325994
  • issue/group-3322623
  • issue/group-3331923
  • issue/group-3332963
  • issue/group-3112501
  • issue/group-3324506
  • issue/group-3344283
  • issue/group-3180860
  • issue/group-3104345
  • issue/group-3332640
  • issue/group-3354135
  • issue/group-3322761
  • issue/group-3367755
  • issue/group-3406043
  • issue/group-3398778
  • issue/group-3399601
  • issue/group-3406373
  • issue/group-3371637
  • issue/group-3371641
  • issue/group-3371651
  • issue/group-3374041
  • issue/group-3378653
  • issue/group-3364226
  • issue/group-3378977
  • issue/group-3399569
  • issue/group-3336186
  • issue/group-3382559
  • issue/group-3382831
  • issue/group-3016197
  • issue/group-2878723
  • issue/group-3390016
  • issue/group-3268711
  • issue/group-3394724
  • issue/group-3379174
  • issue/group-3397015
  • issue/group-3397021
  • issue/group-3397114
  • issue/group-3399085
  • issue/group-3406840
  • issue/group-3336280
  • issue/group-3409735
  • issue/group-3411381
  • issue/group-3413722
  • issue/group-3414592
  • issue/group-3415695
  • issue/group-3416324
  • issue/group-3421138
  • issue/group-3422253
  • issue/group-3422445
  • issue/group-3423341
  • issue/group-2895988
  • issue/group-3420605
  • issue/group-3424972
  • issue/group-3132084
  • issue/group-3101314
  • issue/group-3425601
  • issue/group-3426007
  • issue/group-3426098
  • issue/group-3426239
  • issue/group-3426408
  • issue/group-3427343
  • issue/group-3397063
  • issue/group-3427547
  • issue/group-3427552
  • issue/group-3428392
  • issue/group-3419405
  • issue/group-3441929
  • issue/group-2774827
  • issue/group-3414708
  • issue/group-2815971
  • issue/group-3441076
  • issue/group-3438400
  • issue/group-3440222
  • issue/group-3409374
  • issue/group-3430826
  • issue/group-3446192
  • issue/group-3137221
  • issue/group-3448221
  • issue/group-3449620
  • issue/group-3449640
  • issue/group-3346626
  • issue/group-3451560
  • issue/group-3437029
  • issue/group-3457095
  • issue/group-3462425
  • issue/group-2929788
  • issue/group-3352235
  • issue/group-3455789
  • issue/group-3467754
  • issue/group-3469835
  • issue/group-3470663
  • issue/group-3036272
  • issue/group-3469890
  • issue/group-3475807
  • issue/group-2907838
  • issue/group-3476539
  • issue/group-3478088
  • issue/group-3475816
  • issue/group-3399452
  • issue/group-3480110
  • issue/group-2754399
  • issue/group-3278936
  • issue/group-3482975
  • issue/group-3421756
  • issue/group-3485785
  • issue/group-3482418
  • issue/group-3490229
  • issue/group-3490670
  • issue/group-3489243
  • issue/group-3493593
  • issue/group-3493597
  • issue/group-3493741
  • issue/group-3493807
  • issue/group-3494447
  • issue/group-3494780
  • issue/group-3494986
  • issue/group-3497467
  • issue/group-3499639
  • issue/group-3494298
  • issue/group-3501018
  • issue/group-3502994
  • issue/group-3504133
  • issue/group-3504805
  • issue/group-3505771
  • issue/group-3506061
  • issue/group-3506097
  • issue/group-3506163
  • issue/group-3507425
  • issue/group-3508876
  • issue/group-3509416
  • issue/group-3509657
  • issue/group-3511625
  • issue/group-3512941
  • issue/group-3511454
  • issue/group-3484817
  • issue/group-3519311
242 results
Select Git revision
  • 2.0.x
  • 2.1.x
  • 2.2.x
  • 2.3.x
  • 3.0.x
  • 3.1.x
  • 3.2.x
  • 3.3.x
  • 4.0.x
  • 7.x-1.x
  • 8.x-1.x
  • feature/gc-entity-routes
  • feature/translate_group_content
  • feature/ui_handler
  • 2.0.0
  • 2.0.0-alpha1
  • 2.0.0-alpha2
  • 2.0.0-alpha3
  • 2.0.0-alpha4
  • 2.0.0-beta1
  • 2.0.0-beta2
  • 2.0.0-beta3
  • 2.0.0-beta4
  • 2.0.0-beta5
  • 2.0.0-beta6
  • 2.0.0-rc1
  • 2.0.0-rc2
  • 2.0.1
  • 2.1.0
  • 2.2.0
  • 2.2.1
  • 2.2.2
  • 2.3.0
  • 2.3.1
  • 3.0.0
  • 3.0.0-beta1
  • 3.0.0-beta2
  • 3.0.0-beta3
  • 3.0.0-beta4
  • 3.0.0-beta5
  • 3.0.0-beta6
  • 3.0.0-rc1
  • 3.0.0-rc2
  • 3.0.1
  • 3.1.0
  • 3.2.0
  • 3.2.1
  • 3.2.2
  • 3.3.0
  • 3.3.1
  • 3.3.2
  • 3.3.3
  • 3.3.4
  • 7.x-1.0-alpha1
  • 7.x-1.0-alpha2
  • 7.x-1.0-alpha3
  • 7.x-1.0-alpha4
  • 7.x-1.0-alpha5
  • 7.x-1.0-alpha6
  • 7.x-1.0-beta1
  • 7.x-1.0-beta2
  • 7.x-1.0-beta3
  • 7.x-1.0-beta4
  • 7.x-1.0-beta5
  • 7.x-1.0-beta6
  • 7.x-1.0-unstable1
  • 7.x-1.0-unstable2
  • 7.x-1.0-unstable3
  • 7.x-1.0-unstable4
  • 7.x-1.0-unstable5
  • 8.x-1.0
  • 8.x-1.0-alpha1
  • 8.x-1.0-alpha2
  • 8.x-1.0-alpha3
  • 8.x-1.0-alpha4
  • 8.x-1.0-alpha5
  • 8.x-1.0-alpha6
  • 8.x-1.0-alpha7
  • 8.x-1.0-beta1
  • 8.x-1.0-beta2
  • 8.x-1.0-beta3
  • 8.x-1.0-beta4
  • 8.x-1.0-beta5
  • 8.x-1.0-rc1
  • 8.x-1.0-rc2
  • 8.x-1.0-rc3
  • 8.x-1.0-rc4
  • 8.x-1.0-rc5
  • 8.x-1.1
  • 8.x-1.2
  • 8.x-1.3
  • 8.x-1.4
  • 8.x-1.5
  • 8.x-1.6
  • previous/3016197-group-creator-must/2023-10-13
  • previous/3332963-hookquerytagalter-implementations-get/2023-02-13
  • previous/3397063-drupal-10.1-revisions/2024-03-15
  • previous/3426098-add-cspell-integration/2024-03-12
  • previous/3426408-drop-ismethodcacheable-check/2024-03-12
  • previous/3427552-switch-to-php/2024-03-14
100 results
Show changes
Showing
with 792 additions and 300 deletions
...@@ -34,16 +34,16 @@ ...@@ -34,16 +34,16 @@
* A string with the summary. * A string with the summary.
*/ */
function checkboxesSummary(context) { function checkboxesSummary(context) {
var vals = []; var values = [];
var $checkboxes = $(context).find('input[type="checkbox"]:checked + label'); var $checkboxes = $(context).find('input[type="checkbox"]:checked + label');
var il = $checkboxes.length; var il = $checkboxes.length;
for (var i = 0; i < il; i++) { for (var i = 0; i < il; i++) {
vals.push($($checkboxes[i]).html()); values.push($($checkboxes[i]).html());
} }
if (!vals.length) { if (!values.length) {
vals.push(Drupal.t('Not restricted')); values.push(Drupal.t('Not restricted'));
} }
return vals.join(', '); return values.join(', ');
} }
$('[data-drupal-selector="edit-visibility-group-type"]').drupalSetSummary(checkboxesSummary); $('[data-drupal-selector="edit-visibility-group-type"]').drupalSetSummary(checkboxesSummary);
......
...@@ -2,7 +2,7 @@ name: 'Group Node' ...@@ -2,7 +2,7 @@ name: 'Group Node'
description: 'Enables Group functionality for the Node module' description: 'Enables Group functionality for the Node module'
package: 'Group' package: 'Group'
type: 'module' type: 'module'
core_version_requirement: ^8.8 || ^9 core_version_requirement: ^10.3 || ^11
dependencies: dependencies:
- 'drupal:node' - 'drupal:node'
- 'group:group' - 'group:group'
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
use Drupal\Core\Config\ExtensionInstallStorage; use Drupal\Core\Config\ExtensionInstallStorage;
use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageInterface;
/** /**
* Update the group nodes view to use the argument provided title. * Update the group nodes view to use the argument provided title.
...@@ -68,7 +69,10 @@ function gnode_update_8003() { ...@@ -68,7 +69,10 @@ function gnode_update_8003() {
// Get the storage for optional extension configuration. // Get the storage for optional extension configuration.
$optional_storage = new ExtensionInstallStorage( $optional_storage = new ExtensionInstallStorage(
\Drupal::service('config.storage'), \Drupal::service('config.storage'),
InstallStorage::CONFIG_OPTIONAL_DIRECTORY InstallStorage::CONFIG_OPTIONAL_DIRECTORY,
StorageInterface::DEFAULT_COLLECTION,
FALSE,
\Drupal::installProfile()
); );
// Read the data from the YAML file and save it to the view. // Read the data from the YAML file and save it to the view.
...@@ -124,3 +128,16 @@ function gnode_update_8005() { ...@@ -124,3 +128,16 @@ function gnode_update_8005() {
function gnode_update_8006() { function gnode_update_8006() {
// Empty update. See group_post_update_view_cache_contexts(); // Empty update. See group_post_update_view_cache_contexts();
} }
/**
* Updates views to drop obsolete key.
*/
function gnode_update_8007(&$sandbox) {
if (\Drupal::moduleHandler()->moduleExists('views')) {
$view = \Drupal::configFactory()->getEditable('views.view.group_nodes');
if (!$view->isNew()) {
$view->clear('display.default.display_options.arguments.gid.default_argument_skip_url');
$view->save(TRUE);
}
}
}
group_content.group_node_add_page: group_relationship.group_node_add_page:
route_name: 'entity.group_content.group_node_add_page' route_name: 'entity.group_relationship.group_node_add_page'
title: 'Add existing content' title: 'Add existing content'
appears_on: appears_on:
- 'view.group_nodes.page_1' - 'view.group_nodes.page_1'
group_content.group_node_create_page: group_relationship.group_node_create_page:
route_name: 'entity.group_content.group_node_create_page' route_name: 'entity.group_relationship.group_node_create_page'
title: 'Add new content' title: 'Add new content'
appears_on: appears_on:
- 'view.group_nodes.page_1' - 'view.group_nodes.page_1'
...@@ -8,4 +8,3 @@ services: ...@@ -8,4 +8,3 @@ services:
group.relation_handler.permission_provider.group_node: group.relation_handler.permission_provider.group_node:
class: 'Drupal\gnode\Plugin\Group\RelationHandler\GroupNodePermissionProvider' class: 'Drupal\gnode\Plugin\Group\RelationHandler\GroupNodePermissionProvider'
arguments: ['@group.relation_handler.permission_provider'] arguments: ['@group.relation_handler.permission_provider']
shared: false
...@@ -2,23 +2,24 @@ ...@@ -2,23 +2,24 @@
namespace Drupal\gnode\Plugin\Group\Relation; namespace Drupal\gnode\Plugin\Group\Relation;
use Drupal\group\Plugin\Group\Relation\GroupRelationBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\group\Plugin\Attribute\GroupRelationType;
use Drupal\group\Plugin\Group\Relation\GroupRelationBase;
/** /**
* Provides a group relation type for nodes. * Provides a group relation type for nodes.
*
* @GroupRelationType(
* id = "group_node",
* label = @Translation("Group node"),
* description = @Translation("Adds nodes to groups both publicly and privately."),
* entity_type_id = "node",
* entity_access = TRUE,
* reference_label = @Translation("Title"),
* reference_description = @Translation("The title of the node to add to the group"),
* deriver = "Drupal\gnode\Plugin\Group\Relation\GroupNodeDeriver",
* )
*/ */
#[GroupRelationType(
id: 'group_node',
entity_type_id: 'node',
label: new TranslatableMarkup('Group node'),
description: new TranslatableMarkup('Adds nodes to groups both publicly and privately.'),
reference_label: new TranslatableMarkup('Title'),
reference_description: new TranslatableMarkup('The title of the node to add to the group'),
entity_access: TRUE,
deriver: 'Drupal\gnode\Plugin\Group\Relation\GroupNodeDeriver'
)]
class GroupNode extends GroupRelationBase { class GroupNode extends GroupRelationBase {
/** /**
......
...@@ -2,14 +2,17 @@ ...@@ -2,14 +2,17 @@
namespace Drupal\gnode\Plugin\Group\Relation; namespace Drupal\gnode\Plugin\Group\Relation;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\group\Plugin\Group\Relation\GroupRelationTypeInterface; use Drupal\group\Plugin\Group\Relation\GroupRelationTypeInterface;
use Drupal\node\Entity\NodeType; use Drupal\node\Entity\NodeType;
use Drupal\Component\Plugin\Derivative\DeriverBase;
/**
* Derives plugins based on node type.
*/
class GroupNodeDeriver extends DeriverBase { class GroupNodeDeriver extends DeriverBase {
/** /**
* {@inheritdoc}. * {@inheritdoc}
*/ */
public function getDerivativeDefinitions($base_plugin_definition) { public function getDerivativeDefinitions($base_plugin_definition) {
assert($base_plugin_definition instanceof GroupRelationTypeInterface); assert($base_plugin_definition instanceof GroupRelationTypeInterface);
......
...@@ -4,7 +4,6 @@ namespace Drupal\gnode\Plugin\Group\RelationHandler; ...@@ -4,7 +4,6 @@ namespace Drupal\gnode\Plugin\Group\RelationHandler;
use Drupal\group\Plugin\Group\RelationHandler\PermissionProviderInterface; use Drupal\group\Plugin\Group\RelationHandler\PermissionProviderInterface;
use Drupal\group\Plugin\Group\RelationHandler\PermissionProviderTrait; use Drupal\group\Plugin\Group\RelationHandler\PermissionProviderTrait;
use Drupal\group\Plugin\Group\RelationHandler\RelationHandlerTrait;
/** /**
* Provides group permissions for the group_node relation plugin. * Provides group permissions for the group_node relation plugin.
......
...@@ -14,18 +14,18 @@ class RouteSubscriber extends RouteSubscriberBase { ...@@ -14,18 +14,18 @@ class RouteSubscriber extends RouteSubscriberBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function alterRoutes(RouteCollection $collection) { protected function alterRoutes(RouteCollection $collection) {
if ($route = $collection->get('entity.group_content.create_page')) { if ($route = $collection->get('entity.group_relationship.create_page')) {
$copy = clone $route; $copy = clone $route;
$copy->setPath('group/{group}/node/create'); $copy->setPath('group/{group}/node/create');
$copy->setDefault('base_plugin_id', 'group_node'); $copy->setDefault('base_plugin_id', 'group_node');
$collection->add('entity.group_content.group_node_create_page', $copy); $collection->add('entity.group_relationship.group_node_create_page', $copy);
} }
if ($route = $collection->get('entity.group_content.add_page')) { if ($route = $collection->get('entity.group_relationship.add_page')) {
$copy = clone $route; $copy = clone $route;
$copy->setPath('group/{group}/node/add'); $copy->setPath('group/{group}/node/add');
$copy->setDefault('base_plugin_id', 'group_node'); $copy->setDefault('base_plugin_id', 'group_node');
$collection->add('entity.group_content.group_node_add_page', $copy); $collection->add('entity.group_relationship.group_node_add_page', $copy);
} }
} }
......
...@@ -16,7 +16,7 @@ class EntityOperationsTest extends GroupEntityOperationsTest { ...@@ -16,7 +16,7 @@ class EntityOperationsTest extends GroupEntityOperationsTest {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static $modules = ['gnode']; protected static $modules = ['gnode'];
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -32,7 +32,7 @@ class EntityOperationsTest extends GroupEntityOperationsTest { ...@@ -32,7 +32,7 @@ class EntityOperationsTest extends GroupEntityOperationsTest {
['group/1/nodes' => 'Nodes'], ['group/1/nodes' => 'Nodes'],
[ [
'view group', 'view group',
'access group_node overview' 'access group_node overview',
], ],
]; ];
...@@ -41,7 +41,7 @@ class EntityOperationsTest extends GroupEntityOperationsTest { ...@@ -41,7 +41,7 @@ class EntityOperationsTest extends GroupEntityOperationsTest {
[], [],
[ [
'view group', 'view group',
'access group_node overview' 'access group_node overview',
], ],
['views'], ['views'],
]; ];
......
...@@ -16,14 +16,13 @@ class GroupNodeConfigTest extends EntityKernelTestBase { ...@@ -16,14 +16,13 @@ class GroupNodeConfigTest extends EntityKernelTestBase {
* *
* @var array * @var array
*/ */
public static $modules = [ protected static $modules = [
'entity', 'entity',
'flexible_permissions', 'flexible_permissions',
'gnode', 'gnode',
'group', 'group',
'node', 'node',
'options', 'options',
'variationcache',
'views', 'views',
]; ];
......
name: 'Group Support: Revisions'
description: 'Enables revision access over grouped entities to be determined by the group'
package: 'Group'
type: 'module'
core_version_requirement: ^10.3 || ^11
dependencies:
- 'group:group'
services:
# Decorating group relation handlers.
group.relation_handler_decorator.permission_provider.support_revisions:
class: 'Drupal\group_support_revisions\Plugin\Group\RelationHandler\SupportRevisionsPermissionProvider'
decorates: 'group.relation_handler.permission_provider'
decoration_priority: 50
arguments: ['@group.relation_handler_decorator.permission_provider.support_revisions.inner']
<?php
namespace Drupal\group_support_revisions\Plugin\Group\RelationHandler;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\group\Plugin\Group\Relation\GroupRelationTypeInterface;
use Drupal\group\Plugin\Group\RelationHandler\PermissionProviderInterface;
use Drupal\group\Plugin\Group\RelationHandler\PermissionProviderTrait;
/**
* Alters all permission providers to add revision support.
*/
class SupportRevisionsPermissionProvider implements PermissionProviderInterface {
use PermissionProviderTrait {
init as defaultInit;
}
/**
* Whether the target entity type implements the RevisionableInterface.
*
* @var bool
*/
protected bool $implementsRevisionableInterface;
/**
* Constructs a new SupportRevisionsPermissionProvider.
*
* @param \Drupal\group\Plugin\Group\RelationHandler\PermissionProviderInterface $parent
* The parent permission provider.
*/
public function __construct(PermissionProviderInterface $parent) {
$this->parent = $parent;
}
/**
* {@inheritdoc}
*/
public function init($plugin_id, GroupRelationTypeInterface $group_relation_type) {
$this->defaultInit($plugin_id, $group_relation_type);
$this->implementsRevisionableInterface = $this->entityType->entityClassImplements(RevisionableInterface::class);
}
/**
* {@inheritdoc}
*/
public function getPermission($operation, $target, $scope = 'any') {
if ($target === 'entity') {
switch ($operation) {
case 'view all revisions':
return $this->getEntityViewAllRevisionsPermission();
case 'view revision':
return $this->getEntityViewRevisionPermission();
case 'revert revision':
return $this->getEntityRevertRevisionPermission();
case 'delete revision':
return $this->getEntityDeleteRevisionPermission();
}
}
return $this->parent->getPermission($operation, $target, $scope);
}
/**
* {@inheritdoc}
*/
public function buildPermissions() {
$permissions = $this->parent->buildPermissions();
// Instead of checking whether this specific permission provider allows for
// a permission to exist, we check the entire decorator chain. This avoids a
// lot of copy-pasted code to turn off or rename a permission in a decorator
// further down the chain.
$provider_chain = $this->groupRelationTypeManager()->getPermissionProvider($this->pluginId);
$prefix = 'Revisions:';
if ($name = $provider_chain->getPermission('view all revisions', 'entity')) {
$permissions[$name] = $this->buildPermission("$prefix View full version history");
}
if ($name = $provider_chain->getPermission('view revision', 'entity')) {
$permissions[$name] = $this->buildPermission("$prefix View specific entity revisions");
}
if ($name = $provider_chain->getPermission('revert revision', 'entity')) {
$permissions[$name] = $this->buildPermission("$prefix Revert specific entity revisions");
}
if ($name = $provider_chain->getPermission('delete revision', 'entity')) {
$permissions[$name] = $this->buildPermission("$prefix Delete specific entity revisions");
}
return $permissions;
}
/**
* Gets the name of the view all revisions permission for the entity.
*
* @return string|false
* The permission name or FALSE if it does not apply.
*/
protected function getEntityViewAllRevisionsPermission() {
if ($this->definesEntityPermissions && $this->implementsRevisionableInterface) {
return "view all $this->pluginId entity revisions";
}
return FALSE;
}
/**
* Gets the name of the view all revisions permission for the entity.
*
* @return string|false
* The permission name or FALSE if it does not apply.
*/
protected function getEntityViewRevisionPermission() {
if ($this->definesEntityPermissions && $this->implementsRevisionableInterface) {
return "view $this->pluginId entity revisions";
}
return FALSE;
}
/**
* Gets the name of the view all revisions permission for the entity.
*
* @return string|false
* The permission name or FALSE if it does not apply.
*/
protected function getEntityRevertRevisionPermission() {
if ($this->definesEntityPermissions && $this->implementsRevisionableInterface) {
return "revert $this->pluginId entity revisions";
}
return FALSE;
}
/**
* Gets the name of the view all revisions permission for the entity.
*
* @return string|false
* The permission name or FALSE if it does not apply.
*/
protected function getEntityDeleteRevisionPermission() {
if ($this->definesEntityPermissions && $this->implementsRevisionableInterface) {
return "delete $this->pluginId entity revisions";
}
return FALSE;
}
}
<?php
namespace Drupal\Tests\group_support_revisions\Functional;
use Drupal\Tests\group\Functional\GroupBrowserTestBase;
use Drupal\group\Entity\Storage\GroupRelationshipTypeStorageInterface;
use Drupal\group\PermissionScopeInterface;
use Drupal\node\NodeInterface;
use Drupal\user\RoleInterface;
/**
* Tests that revision operations (do not) show up on a grouped entity.
*
* @group group_support_revisions
*/
class GroupSupportRevisionsTest extends GroupBrowserTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['block', 'gnode'];
/**
* Gets the global (site) permissions for the group creator.
*
* @return string[]
* The permissions.
*/
protected function getGlobalPermissions() {
return [
'access content',
'edit any page content',
'delete any page content',
'view all revisions',
'revert all revisions',
'delete all revisions',
'access administration pages',
] + parent::getGlobalPermissions();
}
/**
* The group type to run the tests with.
*
* @var \Drupal\group\Entity\GroupTypeInterface
*/
protected $groupType;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Page', 'new_revision' => TRUE]);
$this->drupalPlaceBlock('local_tasks_block');
$this->setUpAccount();
$this->groupType = $this->createGroupType();
$storage = $this->entityTypeManager->getStorage('group_relationship_type');
assert($storage instanceof GroupRelationshipTypeStorageInterface);
$storage->save($storage->createFromPlugin($this->groupType, 'group_node:page'));
$group_role_storage = $this->entityTypeManager->getStorage('group_role');
$group_role_storage->save($group_role_storage->create([
'id' => 'foo',
'group_type' => $this->groupType->id(),
'scope' => PermissionScopeInterface::INSIDER_ID,
'global_role' => RoleInterface::AUTHENTICATED_ID,
'permissions' => [],
]));
}
/**
* Tests the revisions tab on an entity's canonical route.
*/
public function testRevisionsTab(): void {
$group_role_storage = $this->entityTypeManager->getStorage('group_role');
$node = $this->drupalCreateNode(['type' => 'page', 'title' => $this->randomString()]);
$path = '/node/1';
$href = '/node/1/revisions';
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists($href, 0, 'Control; "Revisions" tab shows up.');
$group_role = $group_role_storage->load('foo');
$group_role_storage->save($group_role->grantPermission('view group_node:page entity'));
$group = $this->createGroup(['type' => $this->groupType->id()]);
$group->addRelationship($node, 'group_node:page');
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists($href, 0, 'Grouping the node without any special support still shows the "Revisions" tab.');
\Drupal::getContainer()->get('module_installer')->install(['group_support_revisions'], TRUE);
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefNotExists($href, 'Now hat Group knows about revision operations, the grouped node no longer shows the "Revisions" tab.');
$group_role = $group_role_storage->load('foo');
$group_role_storage->save($group_role->grantPermission('view all group_node:page entity revisions'));
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists($href, 0, 'Assigning the right Group permissions once again shows the "Revisions" tab.');
}
/**
* Tests the viewing of individual revisions for an entity.
*
* @depends testRevisionsTab
*/
public function testViewRevision(): void {
$group_role_storage = $this->entityTypeManager->getStorage('group_role');
$node_storage = $this->entityTypeManager->getStorage('node');
$path = '/node/1/revisions/1/view';
$node = $this->drupalCreateNode(['type' => 'page', 'title' => 'First title']);
$node = $node_storage->load($node->id());
assert($node instanceof NodeInterface);
$node->setNewRevision();
$node_storage->save($node->setTitle('Second title'));
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$group_role = $group_role_storage->load('foo');
$group_role_storage->save($group_role->grantPermission('view group_node:page entity'));
$group = $this->createGroup(['type' => $this->groupType->id()]);
$group->addRelationship($node, 'group_node:page');
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
\Drupal::getContainer()->get('module_installer')->install(['group_support_revisions'], TRUE);
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(403);
$group_role = $group_role_storage->load('foo');
$group_role_storage->save($group_role->grantPermission('view group_node:page entity revisions'));
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
}
/**
* Tests the revision operations on an entity's version history route.
*
* @param string $name
* The name of the operation we expect to see.
* @param string $href
* The href of the operation we expect to see.
* @param string $crud_permission
* The permission of the same CRUD operation, required by core.
* @param string $group_permission
* The group permission that should grant access when grouped and supported.
*
* @depends testRevisionsTab
* @dataProvider revisionsOperationsProvider
*/
public function testRevisionOperations(string $name, string $href, string $crud_permission, string $group_permission): void {
$group_role_storage = $this->entityTypeManager->getStorage('group_role');
$node_storage = $this->entityTypeManager->getStorage('node');
$path = '/node/1/revisions';
$node = $this->drupalCreateNode(['type' => 'page', 'title' => 'First title']);
$node = $node_storage->load($node->id());
assert($node instanceof NodeInterface);
$node->setNewRevision();
$node_storage->save($node->setTitle('Second title'));
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists($href, 0, 'Control; "' . $name . '" operation shows up.');
$group_role = $group_role_storage->load('foo');
$group_role_storage->save($group_role->grantPermissions([
'view group_node:page entity',
'view all group_node:page entity revisions',
// Standard revision access checks rely on update or delete access.
$crud_permission,
]));
$group = $this->createGroup(['type' => $this->groupType->id()]);
$group->addRelationship($node, 'group_node:page');
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists($href, 0, 'Grouping the node without any special support still shows the "' . $name . '" operation.');
\Drupal::getContainer()->get('module_installer')->install(['group_support_revisions'], TRUE);
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefNotExists($href, 'Now hat Group knows about revision operations, the grouped node no longer shows the "' . $name . '" operation.');
$group_role = $group_role_storage->load('foo');
$group_role_storage->save($group_role->grantPermission($group_permission));
$this->drupalGet($path);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists($href, 0, 'Assigning the right Group permissions once again shows the "' . $name . '" operation.');
}
/**
* Data provider for ::testRevisionOperations().
*
* @return array
* A list of test scenarios.
*/
public function revisionsOperationsProvider(): array {
$cases['revert'] = [
'name' => 'Revert',
'href' => 'node/1/revisions/1/revert',
'crud_permission' => 'update any group_node:page entity',
'group_permission' => 'revert group_node:page entity revisions',
];
$cases['delete'] = [
'name' => 'Delete',
'href' => 'node/1/revisions/1/delete',
'crud_permission' => 'delete any group_node:page entity',
'group_permission' => 'delete group_node:page entity revisions',
];
return $cases;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="drupal-project">
<description>Default PHP CodeSniffer configuration for Drupal project.</description>
<rule ref="vendor/drupal/coder/coder_sniffer/Drupal/ruleset.xml">
<exclude name="Drupal.Semantics.FunctionT.NotLiteralString"/>
<exclude-pattern>./src/ProxyClass/UninstallValidator/GroupRelationshipUninstallValidator</exclude-pattern>
</rule>
<rule ref="Drupal.NamingConventions.ValidVariableName.LowerCamelName">
<exclude-pattern>./src/Plugin/Group/Relation/GroupRelationType</exclude-pattern>
</rule>
<!-- https://www.drupal.org/drupalorg/docs/drupal-ci/using-coderphpcs-in-drupalci -->
<arg name="extensions" value="php,inc,module,install,info,test,profile,theme"/>
</ruleset>
parameters:
ignoreErrors:
# These errors are a bug in phpstan, see https://github.com/phpstan/phpstan/issues/6830.
-
message: "#^Variable \\$any_unpublished_permission might not be defined\\.$#"
count: 1
path: src/QueryAccess/EntityQueryAlter.php
-
message: "#^Variable \\$data_table might not be defined\\.$#"
count: 3
path: src/QueryAccess/EntityQueryAlter.php
-
message: "#^Variable \\$own_unpublished_permission might not be defined\\.$#"
count: 1
path: src/QueryAccess/EntityQueryAlter.php
-
message: "#^Variable \\$owner_conditions might not be defined\\.$#"
count: 3
path: tests/src/Kernel/QueryAlter/QueryAlterTestBase.php
-
message: "#^Variable \\$unpublished_permissions might not be defined\\.$#"
count: 8
path: tests/src/Kernel/QueryAlter/QueryAlterTestBase.php
-
message: "#^Variable \\$old_table might not be defined\\.$#"
count: 1
path: group.install
includes:
- phpstan-baseline.neon
parameters:
level: 2
ignoreErrors:
# new static() is a best practice in Drupal, so we cannot fix that.
- "#^Unsafe usage of new static#"
# Entity property $original is common in Drupal.
- "#^Access to an undefined property [a-zA-Z0-9\\\\]+\\:\\:\\$original.#"
# Ignore common errors for now.
- "#Drupal calls should be avoided in classes, use dependency injection instead#"
# Can only remove use of membership loader in 4.0.0
- "#^Fetching class constant class of deprecated class Drupal\\\\group\\\\GroupMembership\\:#"
- "#^Fetching class constant class of deprecated interface Drupal\\\\group\\\\GroupMembershipLoaderInterface\\:#"
- "#has typehint with deprecated class Drupal\\\\group\\\\GroupMembership\\:#"
- "#has typehint with deprecated interface Drupal\\\\group\\\\GroupMembershipLoaderInterface\\:#"
- "#^Constructor of class Drupal\\\\group\\\\GroupMembershipLoader has an unused parameter#"
drupal:
entityMapping:
group:
class: Drupal\group\Entity\Group
storage: Drupal\group\Entity\Storage\GroupStorage
group_config_wrapper:
class: Drupal\group\Entity\ConfigWrapper
storage: Drupal\group\Entity\Storage\ConfigWrapperStorage
group_relationship:
class: Drupal\group\Entity\GroupRelationship
storage: Drupal\group\Entity\Storage\GroupRelationshipStorage
group_relationship_type:
class: Drupal\group\Entity\GroupRelationshipType
storage: Drupal\group\Entity\Storage\GroupRelationshipTypeStorage
group_role:
class: Drupal\group\Entity\GroupRole
storage: Drupal\group\Entity\Storage\GroupRoleStorage
group_type:
class: Drupal\group\Entity\GroupType
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
namespace Drupal\group\Access; namespace Drupal\group\Access;
use Drupal\group\Entity\GroupInterface;
use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\group\Entity\GroupInterface;
/** /**
* Extends the AccessResult class with group permission checks. * Extends the AccessResult class with group permission checks.
......