Skip to content
This repository was archived by the owner on Aug 18, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 87 commits
Commits
Show all changes
112 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
a65d814
Merge remote-tracking branch 'origin/8.x-1.x' into 242
pfrenssen Sep 15, 2016
5e0f89b
Simplify the way we will resolve groups to determine group context.
pfrenssen Sep 15, 2016
a447c99
Use the standard naming scheme for plugin managers.
pfrenssen Sep 15, 2016
9081809
Provide an updated interface for group resolver plugins.
pfrenssen Sep 15, 2016
09c1797
Rename the GroupResolver plugin type to OgGroupResolver.
pfrenssen Sep 15, 2016
31ba598
Use correct name for the property containing a reference to the group…
pfrenssen Sep 15, 2016
b265bf5
Initial untested implementation of OgContext.
pfrenssen Sep 16, 2016
8be29c7
OgGroupResolver plugins need to declare which cache contexts are affe…
pfrenssen Sep 16, 2016
885996d
Update plugin name, this has been renamed to OgGroupResolver.
pfrenssen Sep 16, 2016
8cff83a
Document how the discovery of the group context works.
pfrenssen Sep 19, 2016
277eb20
Exploring the first OgResolver plugin.
pfrenssen Sep 19, 2016
999820f
Fix bugs popping up after actually trying to run the code :)
pfrenssen Sep 19, 2016
23d3024
Add schema for the group resolvers configuration entry.
pfrenssen Sep 19, 2016
72ce165
Initial version of the RouteGroupContentResolver.
pfrenssen Sep 20, 2016
7d10f44
Initial version of the RequestQueryArgumentResolver.
pfrenssen Sep 20, 2016
dc4e7e7
Use the correct method name.
pfrenssen Sep 20, 2016
eb363ee
Don't let plugins decide which is the best candidate. This is the res…
pfrenssen Sep 21, 2016
8ca2cba
Merge remote-tracking branch 'origin/8.x-1.x' into 242-simplified
pfrenssen Sep 21, 2016
cf137c9
Remove the tests for the initial approach. They are no longer valid.
pfrenssen Sep 21, 2016
d5cae35
The original Entity plugin has been replaced by RouteGroupResolver an…
pfrenssen Sep 21, 2016
46143f8
Fix coding standards.
pfrenssen Sep 22, 2016
80f5d88
Account for the difference between plugins that provide actual groups…
pfrenssen Sep 22, 2016
e5c28de
Start on unit tests for the OgGroupResolver plugins.
pfrenssen Sep 22, 2016
57afdad
Test RouteGroupResolver::getGroups().
pfrenssen Sep 27, 2016
84c0b9d
Fix PHP_CodeSniffer warnings.
pfrenssen Sep 27, 2016
3847ecd
Moar PHP_CodeSniffer warning fixes.
pfrenssen Sep 27, 2016
736f110
Remove the notion of a OgResolver plugin type.
pfrenssen Sep 28, 2016
5b625a0
Initial implementation of the OgResolvedGroupCollection.
pfrenssen Oct 3, 2016
0ff6952
Use an OgResolvedGroupCollection to gather the resolved groups, inste…
pfrenssen Oct 3, 2016
d130c00
Convert RouteGroupResolver to use OgResolvedGroupCollection.
pfrenssen Oct 3, 2016
af8f2e1
Partially start using the OgResolvedGroupCollection in the context pr…
pfrenssen Oct 3, 2016
2a5615a
Fix PHP_CodeSniffer warnings.
pfrenssen Oct 3, 2016
b0815e1
Refactor group resolver test so its functionality can be reused in ot…
pfrenssen Oct 4, 2016
e72348a
Convert RouteGroupContentResolver to use the group collection.
pfrenssen Oct 4, 2016
cf3a2a8
Start on a test for the RouteGroupContentResolver.
pfrenssen Oct 4, 2016
a48da8e
Convert RequestQueryArgumentResolver to use the resolved group collec…
pfrenssen Oct 4, 2016
d3ef86f
Update documentation.
pfrenssen Oct 4, 2016
c37f100
Merge remote-tracking branch 'gizra/8.x-1.x' into 242-simplified
pfrenssen Oct 4, 2016
13330de
Fix coding standards violation.
pfrenssen Oct 4, 2016
58412ab
Merge branch 'convert-group-audience-helper-to-service' into 242-simp…
pfrenssen Oct 4, 2016
c36670f
Merge remote-tracking branch 'gizra/8.x-1.x' into 242-simplified
pfrenssen Oct 5, 2016
aa06968
Finish the unit test of the RouteGroupContentResolver now that GroupA…
pfrenssen Oct 5, 2016
5e3cdac
Store all properties in a single array.
pfrenssen Oct 5, 2016
355cd70
Store cache context information in the resolved group collection.
pfrenssen Oct 5, 2016
ffe8bed
Remove empty method.
pfrenssen Oct 5, 2016
bb4a95c
Refactor shared code for the route based plugin tests to the base class.
pfrenssen Oct 5, 2016
d0be60c
Do not forget to call your parent.
pfrenssen Oct 5, 2016
5bffde3
Maintain identical ordering for the dependencies.
pfrenssen Oct 5, 2016
8038951
Let the base test class handle dependency injection.
pfrenssen Oct 5, 2016
7cdfe5c
Refactor code to make it reusable in tests.
pfrenssen Oct 5, 2016
154a090
Add a test for the RequestQueryArgumentResolver plugin.
pfrenssen Oct 5, 2016
4c895b5
Change method name to better reflect what it actually returns.
pfrenssen Oct 6, 2016
9c9346e
Move the hardcoded filtering by user access to a dedicated plugin.
pfrenssen Oct 6, 2016
b1f244a
Refactor the sorting of the resolved groups into the collection class.
pfrenssen Oct 6, 2016
bb193ee
Simplify the handling of cache contexts now that the heavy lifting is…
pfrenssen Oct 6, 2016
b2cbdbf
Update documentation.
pfrenssen Oct 6, 2016
bad78ed
Fix coding standards.
pfrenssen Oct 6, 2016
646fa0c
Rename property so that it is clearer what it contains.
pfrenssen Oct 7, 2016
68edfe2
Make it possible to override the way mocked test group entities are c…
pfrenssen Oct 7, 2016
57ff754
Mark the method that returns test entity properties as abstract so th…
pfrenssen Oct 7, 2016
3da581b
Update documentation.
pfrenssen Oct 7, 2016
f9cb560
Add a test for the UserGroupAccessResolver plugin.
pfrenssen Oct 7, 2016
2cc4844
Fix coding standards.
pfrenssen Oct 7, 2016
9a24ff7
Start on a test for OgResolvedGroupCollection.
pfrenssen Oct 10, 2016
4d4983d
Add more test coverage for OgResolvedGroupCollection.
pfrenssen Oct 11, 2016
78cde1e
Test the sorting of groups in a collection and fix bugs discovered by…
pfrenssen Oct 11, 2016
6d0b4e3
Sorting items with the same value is undefined, we can't test this.
pfrenssen Oct 13, 2016
1077495
Merge remote-tracking branch 'origin/8.x-1.x' into 242-simplified
pfrenssen Oct 14, 2016
1f72be2
Start on a test for OgContext.
pfrenssen Oct 17, 2016
43284d4
Merge remote-tracking branch 'upstream/8.x-1.x' into 242-simplified
pfrenssen Nov 3, 2016
b79ce83
Start testing the logic in OgContext::getBestCandidate().
pfrenssen Nov 3, 2016
5897952
Add a standard test case, a single group resolver returning a single …
pfrenssen Nov 5, 2016
d962300
Fix PHP_CodeSniffer warnings.
pfrenssen Nov 5, 2016
0c5185d
Test if cache contexts are correctly provided in the cacheability met…
pfrenssen Nov 5, 2016
1cd8d87
Add a test case for two resolver plugins that each return a different…
pfrenssen Nov 5, 2016
1dc75d8
Add a complex test case, and test if propagation can be stopped.
pfrenssen Nov 5, 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
4 changes: 4 additions & 0 deletions config/install/og.settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ node_access_strict: true
delete_orphans: false
delete_orphans_plugin_id: simple
deny_subscribe_without_approval: true
group_resolvers:
- route_group
- route_group_content
- request_query_argument
6 changes: 6 additions & 0 deletions config/schema/og.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ og.settings:
deny_subscribe_without_approval:
type: boolean
label: 'When enabled, a user that ask to join to a private group their membership status will be pending regardless of the group permissions'
group_resolvers:
type: sequence
label: 'List of OgGroupResolver plugins that are used to discover the group context, in order of priority.'
sequence:
type: string
label: 'OgGroupResolver plugin ID.'

og.settings.group.*:
type: sequence
Expand Down
15 changes: 11 additions & 4 deletions og.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ 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.group_audience_helper']
og.context:
class: Drupal\og\ContextProvider\OgContext
arguments: ['@plugin.manager.og.group_resolver', '@config.factory']
tags:
- { name: 'context_provider' }
og.membership_manager:
class: Drupal\og\MembershipManager
arguments: ['@entity_type.manager', '@og.group_audience_helper']
Expand All @@ -29,18 +34,20 @@ services:
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.delete_orphans:
class: Drupal\og\OgDeleteOrphansPluginManager
parent: default_plugin_manager
plugin.manager.og.fields:
class: Drupal\og\OgFieldsPluginManager
parent: default_plugin_manager

plugin.manager.og.group_resolver:
class: Drupal\og\OgGroupResolverPluginManager
parent: default_plugin_manager
33 changes: 33 additions & 0 deletions src/Annotation/OgGroupResolver.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 the OgGroupResolver annotation object.
*
* @see \Drupal\og\OgGroupResolverPluginManager
* @see plugin_api
*
* @Annotation
*/
class OgGroupResolver extends Plugin {

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

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

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

namespace Drupal\og\ContextProvider;

use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\og\OgResolvedGroupCollection;

/**
* Provides the group that best matches the current context.
*/
class OgContext implements ContextProviderInterface {

use StringTranslationTrait;

/**
* The OgGroupResolver plugin manager.
*
* @var \Drupal\Component\Plugin\PluginManagerInterface
*/
protected $pluginManager;

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

/**
* IDs of cache contexts that are relevant for the current group context.
*
* @var string[]
*/
protected $cacheContextIds = [];

/**
* Constructs a new OgContext.
*
* @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager
* The OgGroupResolver plugin manager.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
*/
public function __construct(PluginManagerInterface $plugin_manager, ConfigFactoryInterface $config_factory) {
$this->pluginManager = $plugin_manager;
$this->configFactory = $config_factory;
}

/**
* {@inheritdoc}
*/
public function getRuntimeContexts(array $unqualified_context_ids) {
// Don't bother to resolve the group context if it is not requested.
if (!in_array('og', $unqualified_context_ids)) {
return [];
}

return ['og' => $this->getOgContext()];
}

/**
* {@inheritdoc}
*/
public function getAvailableContexts() {
$context = new Context(new ContextDefinition('entity', $this->t('Active group')));
return ['og' => $context];
}

/**
* Returns the context object containing the relevant group.
*
* @return \Drupal\Core\Plugin\Context\Context
* A context object containing the group which is relevant in the current
* context as a value. If there is no relevant group in the current context
* then the value will be empty.
*/
protected function getOgContext() {
$context_definition = new ContextDefinition('entity', $this->t('Active group'), FALSE);
$context = new Context($context_definition, $this->getBestCandidate());

$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts($this->cacheContextIds);

$context->addCacheableDependency($cacheability);

return $context;
}

/**
* Returns the group which best matches the current context.
*
* There might be several groups that are relevant in the current context, and
* this method tries to determine which is the best possible candidate.
*
* For example, if we are on a page that displays a group content entity that
* belongs to two groups, then both groups are relevant in the current
* context. We might then decide which group is the better candidate by
* looking at a URL query argument or inspecting the user's browsing history
* to see if they are coming from a group page.
*
* This discovery of groups is handled by OgGroupResolver plugins. Each plugin
* is responsible for discovering groups in a specific domain (e.g. find all
* groups belonging to the current route). The plugins that will be used for
* the discovery are configurable; they are listed under the 'group_resolvers'
* key in the 'og.settings' config. The plugins are ordered by priority,
* meaning that if two groups are relevant to the current context, then the
* plugin with the highest priority will decide which group is going to 'win'.
*
* Developers can customize the group context result by providing their own
* plugins and by activating, disabling or reordering the default ones.
*
* @return \Drupal\Core\Entity\EntityInterface|null
* The group entity which is most relevant in the current context, or NULL
* if no relevant group was found.
*
* @see \Drupal\og\OgGroupResolverInterface
*/
protected function getBestCandidate() {
$collection = new OgResolvedGroupCollection();
$plugins = [];

// Retrieve the list of group resolvers. These are stored in config, and are
// ordered by priority.
$group_resolvers = $this->configFactory->get('og.settings')->get('group_resolvers');
$priority = 0;
foreach ($group_resolvers as $plugin_id) {
/** @var \Drupal\og\OgGroupResolverInterface $plugin */
if ($plugin = $this->pluginManager->createInstance($plugin_id)) {
$plugins[$plugin_id] = $plugin;

// Set the default vote weight according to the plugin's priority.
$collection->setVoteWeight($priority);

// Let the plugin do its magic.
$plugin->resolve($collection);

// If the plugin is certain that the candidate belongs to the current
// context, it can declare the search to be over.
if ($plugin->isPropagationStopped()) {
break;
}

// The next plugin we try will have a lower priority.
$priority--;
}
}

// @todo This should be moved to a plugin.
// Filter out inappropriate results.
array_filter($collection, function (array $candidate) {
// Filter out results that are not accessible by the current user.
return $candidate['entity']->access('view');
});

// @todo This should be handled in the collection.
// Since we have filtered the results by the current user, we need to add
// the user as a cache context.
$this->addCacheContextIds(['user']);

// @todo Move this in the collection? Seems to make sense, since the
// collection has knowledge about the groups and votes.
// Find the best matching group by iterating over the candidates and return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

so, is votes like +1 that each plugin may give?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, with a small difference, their vote will be depending on their position in the list, so the first plugin's vote will count for more than the second plugin's vote. This will allow us to easily choose the correct "winner" if we have two groups with the same number of votes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note that the OgContext code still needs to be cleaned up and tested, it still contains parts from the original implementation. But the basic idea will remain the same.

// the one that has the most "votes". If there are multiple candidates with
// the same number of votes then the candidate that was resolved by the
// plugin(s) with the highest priority will be returned.
uasort($collection, function ($a, $b) {
if (count($a['votes']) == count($b['votes'])) {
return array_sum($a['votes']) < array_sum($b['votes']) ? -1 : 1;
}
return count($a['votes']) < count($b['votes']) ? -1 : 1;
});

// We found the best candidate.
$candidate = reset($collection);

if (empty($candidate)) {
return NULL;
}

// Compile the cache contexts that were used by the plugins that voted for
// our chosen candidate.
foreach (array_keys($candidate['votes']) as $plugin_id) {
$this->addCacheContextIds($plugins[$plugin_id]->getCacheContextIds());
};

return $candidate['entity'];
}

/**
* Adds a list of cache context IDs to be included in the context object.
*
* @param string[] $contexts
* An array of cache context IDs.
*/
protected function addCacheContextIds($contexts) {
foreach ($contexts as $context) {
if (!in_array($context, $this->cacheContextIds)) {
$this->cacheContextIds[] = $context;
}
}
}

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

namespace Drupal\og;

use Drupal\Core\Plugin\PluginBase;

/**
* Base class for OgGroupResolver plugins.
*/
abstract class OgGroupResolverBase extends PluginBase implements OgGroupResolverInterface {

/**
* Whether the group resolving process can be stopped.
*
* @var bool
*/
protected $propagationStopped = FALSE;

/**
* {@inheritdoc}
*/
public function stopPropagation() {
$this->propagationStopped = TRUE;
}

/**
* {@inheritdoc}
*/
public function isPropagationStopped() {
return $this->propagationStopped;
}

}
Loading