Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: First sketch of the OG context module. #252

Merged
merged 36 commits into from
Nov 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
03c0969
First sketch of the OG context module.
Jul 17, 2016
b267014
Adding files.
Jul 19, 2016
bd4bbcd
Moving OG context to OG core.
Jul 22, 2016
b17e50f
Commit more work.
Jul 22, 2016
ed2f4a2
Iterate over plugins easily.
Jul 22, 2016
f4439c6
Get list of plugins by a logic you desire.
Jul 22, 2016
2ca93c8
Minor fixes.
Jul 22, 2016
273dd20
Get the plugins in the right order.
Jul 22, 2016
9f5d251
Get the group from the url.
Jul 22, 2016
55e06bb
Pushing work.
Jul 22, 2016
bc82a45
Get the group from a group content.
Jul 22, 2016
1666774
Get all the groups.
Jul 22, 2016
27c3fac
Remove a line in the end of the file.
Jul 23, 2016
b263d46
My own CR fixes.
Jul 23, 2016
a2f5eb7
Adding some tests.
Jul 24, 2016
70da07b
Merge master
Jul 24, 2016
6e0b1e5
CS fixes(hope)
Jul 24, 2016
c00439b
More CS.
Jul 24, 2016
245cc8a
she's into a typical riff.
Jul 24, 2016
ddb9d7f
Hmm...
Jul 24, 2016
ec3f732
Return the first group for now.
Jul 26, 2016
cb4bf5a
Keep entity as the only plugin.
Jul 27, 2016
3a6d73e
Testing the context it self.
Jul 27, 2016
1753887
Rename the plugin to GroupResolver.
Aug 6, 2016
603fffe
Refactor the entity name [skip ci]
Aug 6, 2016
113b402
More renaming [skip ci]
Aug 6, 2016
f3c98a8
More renaming [skip ci]
Aug 6, 2016
0a0f5e4
Down with renaming.
Aug 6, 2016
5aea9ee
Fix code sniffer.
Aug 6, 2016
becdcf7
Merge branch '8.x-1.x' into 242
Aug 6, 2016
9fce221
Fix tests.
Aug 6, 2016
5ee68e9
Fix conflicts
Aug 26, 2016
f59e521
Extend from the correct class and fix a service.
Aug 26, 2016
23a59c7
Make CS happy.
Aug 26, 2016
e9c7f0a
Adding test.
Aug 27, 2016
e7cad2d
Merge remote-tracking branch 'origin/8.x-1.x' into 242
pfrenssen Sep 14, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions config/schema/group_resolver_negotiation.schema.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
og.group_resolver_negotiation.*:
type: config_entity
label: 'OG group resolver negotiation config'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
description:
type: string
label: 'Description'
status:
type: boolean
label: 'Status'
weight:
type: integer
label: 'Weight'
Copy link
Collaborator

@pfrenssen pfrenssen Sep 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the intention to save a separate config instance for each plugin? Do you foresee that we will need to save multiple instances of the same plugin? I can imagine for example the query argument plugin to have a configurable keyword, and you might want to save multiple instances of it, so you can derive the group if the URL has the query argument ?og_group_ref=groupid as well as ?group=groupid.

To me this sound overkill though. I think it would be simpler to have every plugin available once, and manage them solely with their on/off status and their weight. That means we can save the entire negotiation config in simple config. You would ignore the disabled plugins, and simply list the enabled ones, ordered by weight.

Example config file: config/install/og.group_resolvers.yml

# Most important plugin: check if we are on a canonical entity route, like node/{node}.
route_canonical_entity
# Second, check the query argument, this is configurable.
query_argument:
  keyword: "group"
# A custom plugin provided by a project. E.g. checking if a group exists in an entity reference.
my_custom_plugin:
  reference_field: "myentity_myfield"
# Fallback plugin, iterate over all route parameters. This will provide group context on routes like edit forms etc.
route_parameters

This would make it a lot easier to manage.

7 changes: 7 additions & 0 deletions og.install
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,10 @@ function og_schema() {

return $schema;
}

/**
* Implements hook_install().
*/
function og_install() {
\Drupal\og\Og::groupResolverHandler()->updateConfigStorage();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed. If I look at other modules that provide configurable plugins (e.g. block and views) then they also don't initialize their plugins on hook_install(). This can probably be replaced by default config, or simply by initializing on first use.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't be needed, as the plugins should not be configurable. The only configuration is their weight and status (enabled/ disabled)

7 changes: 7 additions & 0 deletions og.module
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ function og_entity_create_access(AccountInterface $account, array $context, $bun
return $required ? AccessResult::forbiddenIf($node_access_strict) : AccessResult::neutral();
}

/**
* Implements hook_cache_flush().
*/
function og_cache_flush() {
Og::groupResolverHandler()->updateConfigStorage();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we need to update our config on cache clear?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think probably the only case when we would need to update our config is when a new module that provides a plugin is enabled. So probably we can better do this on hook_modules_installed() instead of on every cache clear.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think shouldn't be needed at at all, since plugins won't have own config


/**
* Implements hook_entity_bundle_field_info().
*
Expand Down
19 changes: 12 additions & 7 deletions og.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,40 @@ services:
og.access:
class: Drupal\og\OgAccess
arguments: ['@config.factory', '@current_user', '@module_handler', '@og.group_type_manager', '@og.permission_manager', '@og.membership_manager']
og.membership_manager:
class: Drupal\og\MembershipManager
arguments: ['@entity_type.manager']
og.event_subscriber:
class: Drupal\og\EventSubscriber\OgEventSubscriber
arguments: ['@og.permission_manager', '@entity_type.manager', '@entity_type.bundle.info']
tags:
- { name: 'event_subscriber' }
og.group_resolver:
class: Drupal\og\GroupResolverHandler
arguments: ['@config.factory', '@plugin.manager.og.context', '@entity_type.manager']
og.group_type_manager:
class: Drupal\og\GroupTypeManager
arguments: ['@config.factory', '@entity_type.manager', '@entity_type.bundle.info', '@event_dispatcher', '@state', '@og.permission_manager', '@og.role_manager', '@router.builder']
og.membership_manager:
class: Drupal\og\MembershipManager
arguments: ['@entity_type.manager']
og.permissions:
class: Drupal\og\OgPermissionHandler
arguments: ['@module_handler', '@string_translation', '@controller_resolver']
og.permission_manager:
class: Drupal\og\PermissionManager
arguments: ['@event_dispatcher']
og.role_manager:
class: Drupal\og\OgRoleManager
arguments: ['@entity_type.manager', '@event_dispatcher', '@og.permission_manager']
og.route_subscriber:
class: Drupal\og\Routing\RouteSubscriber
arguments: ['@entity_type.manager', '@router.route_provider', '@event_dispatcher']
tags:
- { name: event_subscriber }
og.role_manager:
class: Drupal\og\OgRoleManager
arguments: ['@entity_type.manager', '@event_dispatcher', '@og.permission_manager']
plugin.manager.og.context:
class: Drupal\og\GroupResolverManager
parent: default_plugin_manager
plugin.manager.og.delete_orphans:
class: Drupal\og\OgDeleteOrphansPluginManager
parent: default_plugin_manager
plugin.manager.og.fields:
class: Drupal\og\OgFieldsPluginManager
parent: default_plugin_manager

33 changes: 33 additions & 0 deletions src/Annotation/GroupResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Drupal\og\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
* Defines a OG group resolver item annotation object.
*
* @see \Drupal\og\Plugin\GroupResolverManager
* @see plugin_api
*
* @Annotation
*/
class GroupResolver extends Plugin {

/**
* The plugin ID.
*
* @var string
*/
public $id;

/**
* The label of the plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;

}
65 changes: 65 additions & 0 deletions src/Entity/GroupResolverNegotiation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Drupal\og\Entity;

/**
* @file
* Contains \Drupal\og\Entity\GroupResolverNegotiation.
*/

use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\og\GroupResolverNegotiationInterface;

/**
* Defines the OG group resolver negotiation entity.
*
* @ConfigEntityType(
* id = "group_resolver_negotiation",
* label = @Translation("OG group resolver negotiation"),
* config_prefix = "group_resolver_negotiation",
* admin_permission = "administer site configuration",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* }
* )
*/
class GroupResolverNegotiation extends ConfigEntityBase implements GroupResolverNegotiationInterface {

/**
* The OG group resolver negotiation ID.
*
* @var string
*/
protected $id;

/**
* The OG group resolver negotiation label.
*
* @var string
*/
protected $label;

/**
* The status of the entity.
*
* @var boolean
*/
protected $status;

/**
* The description of the plugin.
*
* @var string
*/
protected $description;

/**
* The weight of the plugin.
*
* @var integer
*/
protected $weight;

}
161 changes: 161 additions & 0 deletions src/GroupResolverHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

namespace Drupal\og;

/**
* @file
* Contains \Drupal\og\GroupResolverHandler.
*/

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;

/**
* Class GroupResolverHandler.
*
* @package Drupal\og
*/
class GroupResolverHandler implements GroupResolverHandlerInterface {

/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;

/**
* The OG group resolver manager.
*
* @var \Drupal\og\GroupResolverManager
*/
protected $pluginManager;

/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;

/**
* Constructs an group resolver service.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\og\GroupResolverManager $context_manager
* The OG context manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager
* The entity type manager.
*/
public function __construct(ConfigFactoryInterface $config_factory, GroupResolverManager $context_manager, EntityTypeManagerInterface $entity_manager) {
$this->configFactory = $config_factory;
$this->pluginManager = $context_manager;
$this->storage = $entity_manager->getStorage('group_resolver_negotiation');
}

/**
* {@inheritdoc}
*/
public function getGroup() {
$plugins = $this->getPlugins();

$groups = [];

foreach ($plugins as $plugin) {
if ($group = $this->getPlugin($plugin['id'])->getGroup()) {
$groups = array_merge($groups, [$group]);
}
}

// Return the first group for now. handle in a follow up PR to find the best
// matching group.
return reset($groups);
}

/**
* {@inheritdoc}
*/
public function getPlugins($return_mode = GroupResolverHandlerInterface::RETURN_ONLY_ACTIVE) {

/** @var \Drupal\og\Entity\GroupResolverNegotiation[] $group_resolver_config */
$group_resolver_config = $this->storage->loadMultiple();

$plugins = $this->pluginManager->getDefinitions();

if ($return_mode != GroupResolverHandlerInterface::RETURN_ALL) {

foreach ($group_resolver_config as $context) {
if ($return_mode == GroupResolverHandlerInterface::RETURN_ONLY_ACTIVE) {
$condition = $context->get('status') == FALSE;
}
else {
$condition = !in_array($context->id(), array_keys($plugins));
}

if ($condition) {
unset($plugins[$context->id()]);
}
}
}

if (!empty($group_resolver_config)) {
uasort($plugins, function ($a, $b) use ($group_resolver_config) {
return $group_resolver_config[$a['id']]->get('weight') > $group_resolver_config[$b['id']]->get('weight') ? 1 : -1;
});
}

return $plugins;
}

/**
* {@inheritdoc}
*/
public function getPlugin($plugin_id) {
return $this->pluginManager->createInstance($plugin_id);
}

/**
* {@inheritdoc}
*/
public function updatePlugin($plugin_id, $config = []) {
/** @var GroupResolverNegotiation $contex */
$context = $this->storage->load($plugin_id);

foreach ($config as $key => $value) {
$context->set($key, $value);
}

$context->save();
}

/**
* {@inheritdoc}
*/
public function updateConfigStorage() {
$plugins = $this->getPlugins(GroupResolverHandlerInterface::RETURN_ALL);

$group_resolver_storage = $this->storage;
$group_resolver_config = $group_resolver_storage->loadMultiple();

$weight = 0;
foreach ($plugins as $plugin) {
if (in_array($plugin['id'], array_keys($group_resolver_config))) {
// The negotiation plugin already registered.
continue;
}

// Registering a new negotiation plugin.
$group_resolver_storage->create([
'id' => $plugin['id'],
'label' => $plugin['label'],
'description' => $plugin['description'],
'status' => FALSE,
'weight' => $weight,
])->save();

$weight++;
}
}

}
Loading