Problem/Motivation

Block plugins have the API plumbing to be contextual, but they don't have the UI exposed to allow for that sort of mapping.

Proposed resolution

Leverage the abstracted UI component built (and already in core) for Condition plugins for Blocks as well.

Remaining tasks

  1. Get consensus on existing test coverage.
  2. RTBC
  3. Get someone to commit it!

User interface changes

For blocks that accept context, it will show a select box to map that context from one of the available context values. If only one possible value is available, it'll just select that one.

However, no block in core demonstrates the changes. #2550199: Add a ContextProvider for Feeds will introduce appropriate changes for aggregator feed block, but that has been descoped from this issue.

API changes

None.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug because every block has the plumbing to accept contexts, but (before this patch) each and every block would need to implement the UI and config storage themselves. It would not be possible to implement something in CTools that would affect every block, because we'd need the block to make sure their configuration schema can accept the mapping, and use the same configuration keys. Since core blocks are 99% of the way there, it seems only a bug that they wouldn't go the last 1% and save us from having to make a new BlockBase to use in contrib.
Issue priority Major because the amount of work and tension from having a split block eco-system (ie. contrib using a new BlockBase class from CTools, rather than core's BlockBase) or a completely non-standardized eco-system is much greater than simply connecting the last of the dots in the core Block API. Also, if we merged this patch later (ie. in 8.1.x), making an upgrade path would be problematic because how contrib handles config storage of context mapping without this patch is essentially undefined.
Prioritized changes The main goal of this issue is nail down the block_settings configuration (so it's standardized in how context mappings are stored) and provide UI for the otherwise complete block context handling already in core. This needs to be done before 8.0.0, because doing it later would be difficult to upgrade the configuration.
Disruption Not disruptive for core and contributed modules because it's only adding new UI and config for blocks. It doesn't make any API changes at all - the internals are already pretty much already set.
CommentFileSizeAuthor
#164 2377757-block-164.patch16.86 KBtim.plunkett
#164 interdiff.txt1.39 KBtim.plunkett
#163 2377757-block-163.patch16.62 KBtim.plunkett
#163 interdiff.txt2.08 KBtim.plunkett
#163 Configure block | Drupal-2015-09-24.png63.46 KBtim.plunkett
#159 2377757-block-159.patch18.14 KBtim.plunkett
#159 interdiff.txt4.66 KBtim.plunkett
#157 interdiff.txt8.21 KBtim.plunkett
#157 2377757-block-157.patch21.96 KBtim.plunkett
#150 interdiff.txt1.14 KBtim.plunkett
#150 2377757-block-150.patch25.72 KBtim.plunkett
#149 interdiff.txt12.57 KBtim.plunkett
#149 2377757-block-149.patch25.23 KBtim.plunkett
#147 2377757-block-147.patch22.08 KBtim.plunkett
#140 interdiff.txt2.5 KBFrando
#140 2377757-BlockContextUI-140.patch22.08 KBFrando
#133 interdiff.txt1.26 KBFrando
#133 2377757-block-132.patch22.52 KBFrando
#132 interdiff.txt35.37 KBFrando
#132 2377757-block-131.patch23.79 KBFrando
#131 2377757-block-130.patch55.34 KBFrando
#131 interdiff.txt1.26 KBFrando
#130 2377757-block-129.patch55.03 KBFrando
#130 interdiff.txt979 bytesFrando
#128 2377757-block-128.patch54.97 KBFrando
#128 interdiff.txt916 bytesFrando
#121 interdiff.txt3.7 KBtim.plunkett
#121 2377757-block-121.patch21.26 KBtim.plunkett
#120 interdiff.txt2.79 KBtim.plunkett
#120 2377757-block-118.patch22.64 KBtim.plunkett
#115 2377757-interdiff.txt5.11 KBEclipseGc
#115 2377757-115.patch36.37 KBEclipseGc
#112 2377757-interdiff.txt3.12 KBEclipseGc
#112 2377757-112.patch33.24 KBEclipseGc
#110 2377757-110-interdiff.txt1.93 KBBerdir
#110 2377757-110.patch29.94 KBBerdir
#108 2377757-107.patch29.58 KBBerdir
#103 2377757-103-interdiff.txt6.21 KBBerdir
#103 2377757-103.patch31.69 KBBerdir
#99 interdiff.txt15.21 KBtim.plunkett
#95 2377757-95.patch32.89 KBEclipseGc
#95 2377757-interdiff.txt1.36 KBEclipseGc
#94 2377757-94.patch33.01 KBEclipseGc
#94 2377757-interdiff.txt1.48 KBEclipseGc
#89 2377757-PASS-89.patch33.01 KBEclipseGc
#89 2377757-FAIL-89.patch2.46 KBEclipseGc
#89 2377757-interdiff.txt5.22 KBEclipseGc
#86 2377757-86.patch30.27 KBEclipseGc
#86 2377757-interdiff.txt1.43 KBEclipseGc
#84 Selection_055.png27.03 KBdsnopek
#80 2377757-80.patch30.23 KBEclipseGc
#80 2377757-interdiff.txt3.24 KBEclipseGc
#77 2377757-77.patch29.4 KBEclipseGc
#77 2377757-interdiff.txt1.54 KBEclipseGc
#76 2377757-interdiff.txt2.35 KBEclipseGc
#76 2377757-76.patch28.97 KBEclipseGc
#74 2377757-74.patch28.85 KBEclipseGc
#73 2377757-73.patch1.8 KBEclipseGc
#71 2377757-71.patch28.85 KBEclipseGc
#65 2377757-65.patch28.31 KBjaperry
#64 2377757-62.patch26.4 KBjaperry
#63 Block_layout___Site-Install.jpg752.42 KBjaperry
#50 2377757-interdiff.txt2.14 KBEclipseGc
#50 2377757-50.patch25.64 KBEclipseGc
#48 07.2 Block Config - post patch:n feed.png64.27 KBEclipseGc
#48 07.1 Block Config - post patch:n feed.png58.04 KBEclipseGc
#48 06 Block Config - post patch:1 feed.png58.3 KBEclipseGc
#48 05.2 Block List - post patch:post feed.png14.22 KBEclipseGc
#48 05.1 Block List - post patch:pre feed.png12.53 KBEclipseGc
#48 04 Block Config - n feed.png67.7 KBEclipseGc
#48 03 Block Config - 1 feed.png65.94 KBEclipseGc
#48 02 Block Config - pre feed.png62.2 KBEclipseGc
#48 01 Block List - pre feed.png14.22 KBEclipseGc
#46 interdiff.txt2.92 KBtim.plunkett
#46 2377757-block-46.patch24.78 KBtim.plunkett
#44 2377757-44.patch21.86 KBEclipseGc
#42 2377757-42.patch21.82 KBEclipseGc
#40 2377757-40.patch21.62 KBEclipseGc
#37 2377757-interdiff.txt2.16 KBEclipseGc
#37 2377757-37.patch23.11 KBEclipseGc
#31 2377757-interdiff.txt4.35 KBEclipseGc
#31 2377757-30.patch21.61 KBEclipseGc
#29 2377757-interdiff.txt2.14 KBEclipseGc
#29 2377757-29.patch17.27 KBEclipseGc
#28 2377757-interdiff.txt4.07 KBEclipseGc
#28 2377757-28.patch17.19 KBEclipseGc
#26 2377757-interdiff.txt1.78 KBEclipseGc
#26 2377757-26.patch15.29 KBEclipseGc
#24 2377757-interdiff.txt870 bytesEclipseGc
#24 2377757-24.patch14.37 KBEclipseGc
#20 interdiff.txt758 bytestim.plunkett
#20 2377757-block-20.patch14.34 KBtim.plunkett
#16 interdiff.txt4.5 KBtim.plunkett
#16 2377757-block-context-16.patch14.31 KBtim.plunkett
#13 2377757-interdiff.txt4.26 KBEclipseGc
#13 2377757-13.patch13.94 KBEclipseGc
#8 2377757-8.patch11 KBEclipseGc
#5 2377757-5.patch10.5 KBEclipseGc
#4 2377757-4.patch126.09 KBEclipseGc
#1 2377757-1.patch3.16 KBEclipseGc
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

EclipseGc’s picture

FileSize
3.16 KB

I'm thinking something along these lines to start + a conversion and tests, or at least tests that properly cover it. (this is dependent upon #2339151: Conditions / context system does not allow for multiple configurable contexts, eg. language types so until it lands we can't properly test this)

Eclipse

EclipseGc’s picture

tim.plunkett’s picture

Issue summary: View changes
  1. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    @@ -160,6 +164,9 @@ protected function blockAccess(AccountInterface $account) {
    +    $temporary = $form_state->getTemporary();
    +    $contexts = isset($temporary['gathered_contexts']) ? $temporary['gathered_contexts'] : [];
    +    $form['context_mapping'] = $this->addContextAssignmentElement($this, $contexts);
    

    Wow this is ugly. Another +1 for #2371853: Add more helper methods around temporary FAPI storage

  2. +++ b/core/modules/block/src/BlockForm.php
    @@ -310,7 +310,12 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    +      $block->setContextMapping($context_mapping);
    

    Why do we need this here?

  3. +++ b/core/modules/block/src/BlockRepository.php
    @@ -77,6 +77,11 @@ public function getVisibleBlocksPerRegion(array $contexts) {
    +      $block->setAvailableContexts($contexts);
    +      $block_plugin = $block->getPlugin();
    +      if ($block_plugin instanceof ContextAwarePluginInterface) {
    +        $this->contextHandler->applyContextMapping($block_plugin, $contexts);
    +      }
    

    Still don't need this hunk. It's done in BlockAccessControlHandler, which is triggered by the access call two lines away.

We have TestContextAwareBlock in block_test available for tests.

EclipseGc’s picture

FileSize
126.09 KB

3.1: Yup
3.2: You might be right, I'll try with the next patch
3.3: nope, this never maps for the block itself, only conditions

I've converted the AggregatorFeedBlock. It has real contexts now... which is pretty awesome. I had to introduce an AggregatorFeedContext, and I have some questions around it. Perhaps there's a better way to do what it does, but as a proof of concept, I'm happy for the moment.

Eclipse

EclipseGc’s picture

FileSize
10.5 KB

oops, 4 included 2339151 & other changes... This excludes what's in 2339151 but still depends on it.

Eclipse

EclipseGc’s picture

Ok, I tried to not do 3.2 but it doesn't save the context mapping properly w/o.

Eclipse

tim.plunkett’s picture

You're right on 3.2, that's my bad.

And you're also right, 3.3 is needed, but it should move into BlockAccessControlHandler, right above/below where condition context is applied.

This will speed things up for disabled blocks (the first check in BlockAccessControlHandler).

I have some fixes for ContextHandler, but they're out of scope for this issue and need to go into a blocker, which I'll open tomorrow.

EclipseGc’s picture

Status: Active » Needs review
FileSize
11 KB

Ok, essentially starting anew here. I will provide interdiffs going forward from this patch. Now that #2378585: Multiple context requirements cannot be satisfied by a single value & #2339151: Conditions / context system does not allow for multiple configurable contexts, eg. language types have both landed, I can write a patch that could actually pass or fail tests appropriately!

Let's see how this goes.

Eclipse

tim.plunkett’s picture

  1. +++ b/core/modules/block/src/BlockForm.php
    @@ -310,7 +310,12 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    +      $context_mapping = $settings->hasValue('context_mapping') ? $settings->getValue('context_mapping') : [];
    

    You can just do $context_mapping = $settings->getValue('context_mapping', []);

  2. +++ b/core/modules/block/src/BlockRepository.php
    @@ -76,6 +77,11 @@ public function getVisibleBlocksPerRegion(array $contexts) {
    +      $block->setContexts($contexts);
    ...
           if ($block->setContexts($contexts)->access('view')) {
    

    We have setContexts() twice now.

EclipseGc’s picture

1203? That ain't bad! :-D

9.1: I'll check
9.2: DOH!

Eclipse

larowlan’s picture

some minor nitpicks

  1. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,86 @@
    + * Contains AggregatorFeedContext.php.
    

    Needs to be Contains \Full\Path\To\Class and no .php

  2. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,86 @@
    +class AggregatorFeedContext extends BlockContextSubscriberBase {
    

    Needs a docblock

  3. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,86 @@
    +   * Constructs a new NodeRouteContext.
    

    c/p fail?

EclipseGc’s picture

FileSize
13.94 KB
4.26 KB

I'll address 12 in my next patch.

Fixed some tests, getting some weirdness around drupalPostForm(), not sure why yet.

Eclipse

EclipseGc’s picture

Status: Needs work » Needs review

opps

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
14.31 KB
4.5 KB
  1. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,86 @@
    +        list(, $fid) = explode(':', $contexts['feed']);
    

    We don't have any sanity checks around the contents of $contexts, that feels wrong.

Aggregator doesn't depend on block, this has to be optional :(

Also, do we really want to load all of these on runtime?

EclipseGc’s picture

We're only loading blocks of the aggregator_feed_block plugin_id for the current theme. So we should only be loading ones we KNOW to be relevant (not all of them). I'm fine with putting sanity checks in there, but the block's configuration should ensure we get a mapping. Yes at the raw plugin level, you could technically create one w/o context, so I'm fine with sanity, but it's definitely an edge case.

Eclipse

EclipseGc’s picture

  1. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -55,14 +59,12 @@ public function __construct(EntityManagerInterface $entity_manager, ThemeManager
    -    $feeds = array();
    -    foreach ($this->blockStorage->loadByProperties(array('plugin' => 'aggregator_feed_block', 'theme' => $this->themeManager->getActiveTheme()->getName())) as $block_id => $block) {
    +    foreach ($this->blockStorage->loadByProperties(['plugin' => 'aggregator_feed_block', 'theme' => $this->themeManager->getActiveTheme()->getName()]) as $block_id => $block) {
           /** @var $block \Drupal\block\Entity\Block */
           $block_plugin = $block->getPlugin();
    -      /** @var $block_plugin \Drupal\Component\Plugin\ContextAwarePluginInterface */
    +      if ($block_plugin instanceof ContextAwarePluginInterface) {
             $contexts = $block_plugin->getContextMapping();
    -      if (!isset($feeds[$contexts['feed']])) {
    -        list(, $fid) = explode(':', $contexts['feed']);
    +        list(, $fid) = explode(':', $contexts['feed'], 2);
    

    I put the $feeds check in there to prevent us from processing the same feed multiple times if there are multiple blocks placing the same feed.

  2. +++ b/core/modules/block/src/AggregatorServiceProvider.php
    @@ -0,0 +1,32 @@
    +    if ($container->get('module_handler')->moduleExists('block')) {
    

    This is IN block... why are we checking if block module exists?

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
14.34 KB
758 bytes

1) If you're going to check $feeds, you should actually add stuff to it... Leaving out for now.

2) I meant to put this in aggregator.module

tim.plunkett’s picture

tim.plunkett’s picture

Also #2371853: Add more helper methods around temporary FAPI storage went in, so we can rewrite the temporary FAPI calls.

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
14.37 KB
870 bytes

So we have a weird thing where posts aren't happening properly. I changed a couple things, and want to see where we stand on tests. Tim pointed out https://github.com/md-systems/file_entity/blob/8.x-2.x/src/FileEntitySer... which was a great fix to our trials with module handler in the service provider.

Eclipse

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
15.29 KB
1.78 KB

This MAY actually pass... let's see.

Eclipse

tim.plunkett’s picture

  1. +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -152,7 +133,8 @@ public function blockSubmit($form, FormStateInterface $form_state) {
    -    if ($feed = $this->feedStorage->load($this->configuration['feed'])) {
    ...
    +    if ($feed = $this->getContextValue('feed')) {
    
    @@ -194,7 +176,7 @@ public function build() {
    +    $feed = $this->getContextValue('feed');
    

    Seems strange that we wrap this in an if() in one method but not the other.

  2. +++ b/core/modules/block/src/BlockRepository.php
    @@ -76,8 +77,13 @@ public function getVisibleBlocksPerRegion(array $contexts) {
    +      $block->setContexts($contexts);
    ...
    -      if ($block->setContexts($contexts)->access('view')) {
    +      if ($block->access('view')) {
    

    This change is rather pointless, it doesn't matter when we call this as long as it's before access()

EclipseGc’s picture

FileSize
17.19 KB
4.07 KB

Fixing stuff from 27

Eclipse

EclipseGc’s picture

FileSize
17.27 KB
2.14 KB

Ok, I've moved the context mapping form element to be directly before the block plugin's specific configuration form. If this passes, I'm ready to take this to the next review level.

Eclipse

webchick’s picture

Status: Needs review » Needs work
Issue tags: +Usability, +Needs issue summary update

Since this touches the UI, needs to be checked over for Usability. We also need a beta phase evaluation template for this issue.

I tried to give the patch a whirl, but can't seem to see an Aggregator/Feed block no matter what I do. Steps:

1) Apply the latest patch
2) drush si
3) drush en aggregator
4) admin/config/services/aggregator
5) Add a feed to http://rss.cbc.ca/lineup/topstories.xml (Canada FTW)
6) admin/structure/block

No errors, just no block is visible.

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
21.61 KB
4.35 KB

Apparently some stuff changed in head that causes blocks to only be listed by available contexts. Need to update this patch accordingly.

Eclipse

EclipseGc’s picture

I'll update the docblocks in the next patch, I just wanted to see if this would pass.

Eclipse

japerry’s picture

Issue tags: +D8panels

Thanks for pushing this forward EclipseGC! For panels this is a critical component for us that really shouldn't live in contrib.

tim.plunkett’s picture

Issue tags: +Page Manager
dasjo’s picture

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
23.11 KB
2.16 KB

Ok, ran into a bug we'd all missed while doing this. The code changes made to \Drupal\Core\Annotation\ContextDefinition to support TranslationWrapper was completely neglecting that the only way we get that wrapper is by calling the get() method of the Translation annotation. This made it impossible for me to rework the form element to display a form description for the context. It was a minor change, but good to know we got that squared away. Should probably add in some tests that verify we've got title and description text into the context definition properly.

I'm on vacation and took this morning to try to move this patch forward a bit more. It might be a few more days before I can do anything with it. (I expect the previous test to still fail. Tim and I need to discuss it still.)

Eclipse

dsnopek’s picture

Issue summary: View changes

Updated remaining tasks per EclipseGC at SCOTCH meeting.

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
21.62 KB

Untested Reroll, let's see what happens.

Eclipse

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
21.82 KB

Missed a use statement.

Eclipse

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
21.86 KB

Another use statement...

Eclipse

tim.plunkett’s picture

Status: Needs work » Needs review
Issue tags: +Needs usability review
FileSize
24.78 KB
2.92 KB

Our previous test coverage was proving that we didn't support context-aware blocks.
In order to maintain our test coverage now that we do support them, I added a block that has an unsatisfied context (entity:foobar), and tweaked the original test.

aspilicious’s picture

Can we have a couple of screenshots?

EclipseGc’s picture

Yes you may! I've included explanations as relevant. We are using Aggregator Feed block as an example of how this can work, aggregator is attempting to solve a problem the context system was invented for and solves it badly currently, I can illustrate this in the existing UI. Most of what aggregator does wrong would probably apply to most modules needing something similar.

Current HEAD

After installing Aggregator, we visit the block layout admin w/o creating a feed yet and we are treated to this:

If we attempt to configure this block without having created a Feed yet, we get this:

This is kind of ridiculous because there are no feeds, and we're placing a block that cannot even function yet. Once we create feeds we get:


for 1 feed and:


for N feeds respectively.

Summary of HEAD:

We have the ability to place aggregator feed blocks before they're useful. This can lead to misconfigured blocks that don't function.

After Patch:

Leveraging the context system brings some uniform benefits to the UI, first on the list is that before you've created any feed entities, the block layout system won't even give you the erroneous option of placing feed blocks:

After we've created feeds, the block system shows us that we can actually use the block:

If we've only created a single feed, the block doesn't prompt the user for unnecessary configuration, it knows there's only one possible feed to use, and it just auto-configures that single value:

Once multiple Feeds have been created, the user interface expands accordingly:

and

Summary of Patch

The patch progressively discloses to the user features that they can use as they become available. If there's not a feed, the block can't be placed, if there's only one feed, the block doesn't ask which feed you want to use, it's only once you have multiple feeds that the UI presents you with the maximum number of questions.

This would extend to all context consuming blocks.

Eclipse

aspilicious’s picture

Thnx! I had no clue what the patch was doing. This is awesome btw!

EclipseGc’s picture

FileSize
25.64 KB
2.14 KB

Spoke with Alex Pott some and he pointed out a few things I'd missed or done improperly. Let's see how this goes.

Wim Leers’s picture

Thanks for #48. Awesome explanation!

IMHO it'd be better if at https://www.drupal.org/files/issues/06%20Block%20Config%20-%20post%20pat... we'd show that "Drupal Planet" was the feed that was used for this block. Otherwise, the user doesn't know this block is associated with that feed, which may lead the user to think that it'll show all feeds; i.e. that once an additional feed is created, it will also be listed in the block.

EclipseGc’s picture

So, you'd just prefer some sort of visual queue that can't be interacted with?

Wim Leers’s picture

#52: Yes, using '#type' => 'item' or something like it.

EclipseGc’s picture

Ok, I think Bojhan has this on his list for today, if he feels the same way, I'd be happy to do it, but I'm not going to change the existing UI work until I hear from him. I am definitely not opposed to that sort of a visual queue.

Eclipse

Wim Leers’s picture

Here's an initial review.

  1. +++ b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
    @@ -100,7 +100,7 @@ public function __construct(array $values) {
    -      if (isset($values[$key]) && $values[$key] instanceof TranslationWrapper) {
    +      if (isset($values[$key]) && $values[$key]->get() instanceof TranslationWrapper) {
    

    Interesting change. Does this point to a gap in HEAD's test coverage?

  2. +++ b/core/modules/aggregator/config/schema/aggregator.schema.yml
    @@ -44,6 +44,10 @@ block.settings.aggregator_feed_block:
    +        feed:
    +          type: string
    +          label: 'Feed'
    

    This will actually contain a UUID, right? I know we don't have a specific config schema type for that, but a comment noting that here would be welcome.

  3. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,88 @@
    +   * Constructs a new NodeRouteContext.
    

    C/p remnant.

  4. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,88 @@
    +        $context = new Context(new ContextDefinition('entity:aggregator_feed'));
    +        $context->setContextValue($feed);
    +        $event->setContext('aggregator.feed:' . $feed->id(), $context);
    ...
    +      $context = new Context(new ContextDefinition('entity:aggregator_feed', $feed->label()));
    +      $context->setContextValue($feed);
    +      $event->setContext('aggregator.feed:' . $feed->id(), $context);
    

    I don't know contexts well, so please forgive any stupid mistakes.

    But if I understand this patch well, then classes like this one (AggregatorFeedContext) will become pretty widespread. Which means their DX is important.

    I think it's very confusing that these 3 lines are duplicated between these 2 required methods. With only a tiny subtle difference. It's not at all clear why that difference exists.

  5. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,88 @@
    +        list(, $fid) = explode(':', $contexts['feed'], 2);
    

    And this feels like dark magic.

    Needs documentation to explain what it's doing.

  6. +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -23,19 +22,16 @@
    + *   context = {
    + *     "feed" = @ContextDefinition("entity:aggregator_feed", label = @Translation("Aggregator feed"), description = @Translation("Select the feed that should be displayed"))
    + *   }
    

    We always format these across multiple lines.

  7. +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -95,7 +87,6 @@ public function defaultConfiguration() {
    -      'feed' => NULL,
    

    Nice :)

  8. +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -152,40 +131,40 @@ public function blockSubmit($form, FormStateInterface $form_state) {
    -    if ($feed = $this->feedStorage->load($this->configuration['feed'])) {
    

    Even nicer!

  9. +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -194,7 +173,7 @@ public function build() {
    -    $feed = $this->feedStorage->load($this->configuration['feed']);
    +    $feed = $this->getContextValue('feed');
    

    Now needs IDE hint.

EclipseGc’s picture

Status: Needs work » Needs review

1.) Possibly, yes
2.) I had not considered UUID, I'll look at that in more detail as I think that makes a ton of sense but it's been a while since I wrote that bit.
3.) bah
4.) Yeah... so you're not wrong, but aggregator is sort of a weird instance... in fact most example in core are weird cause we do core weird, but the difference between an administrative context and a run time context is that run time needs to have a real data object to back it, while administration does not. In the case of Aggregator though, we know what available feeds we have (since we don't generally display these on a site exclusively through route parameters like we would nodes or terms and we generally have fewer of them). The only difference in your snippet there is the labeling, which is done so that administratively, we know which feed context corresponds to which feed entity. There are existing NodeRouteContext classes and also one for global user. Node has empty administrative contexts (making the difference clearer I hope) but user (since it's current user and we can always get that) suffers the same confusion you're pointing out here.

TL:DR;
If the context object we're dealing with can be resolved to a real data object during administration, these two methods are going to look similar, if it can't they'll look really different.

5.) If your comment in 2 is feasible, I think this would go away.
6.) Errr... we do? I'm not opposed, but we're hardly using ContextDefinitions in core except in Conditions, and I hadn't seen this there. Is there a code standard we have that dictates this? I'm happy to do it, just didn't realize.
7.) :-D
8.) Yup, this is actually the beauty of plugin contexts is that we build constructs responsible for this per calling code use case instead of hard coding the approach into the plugin itself.
9.) Yup, that seems fair.

Eclipse

Bojhan’s picture

Issue tags: -Needs usability review

Thanks for asking for review :)

I am really happy to see this patch, and it looks like its 90% there in terms of the UI approach.

WimLeers is correct in stating that we should inform the user about the connected feed, even when there is only one. This sets the expectation for the next one that you can select it, and informs the user about the content of the block. Which is crucial for understanding the underlying concepts.

In terms of the labeling, I suggest to go with:

"Feed" - no description.

The rest should probably be a given, the point of a selector is to "select" something so you don't have to mention it and the point of a block is to "display" something. So if you connect the concept of which feed, you should be fine from a informing the user point of view of the result.

alexpott’s picture

Status: Needs review » Needs work

I think the aggregator block context should also include block_count - ie. everything from AggregatorFeedBlock::blockForm()? Also the schema for the context mapping should be defined by the block.settings.aggregator_feed_block schema.

EclipseGc’s picture

No, the block count is user input from the configuration form. The context is a statement of requirements. If you don't have a context that satisfies the block's needs, we don't even allow you to place the block. While it could be done as an optional context, it's an abuse of context to leverage it that way, and would be a pretty big change since that'd imply that all plugin settings should run through context across all types.

Aggregator feed block, as it stands today, is a pretty classic case of some data object solving the context needs of some plugin. This clarifies the use of the plugin in question and simplifies various other aspects of the plugin's approach to rendering information associated with that data object. That's classic Context.

Eclipse

fago’s picture

  1. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,88 @@
    +        $contexts = $block_plugin->getContextMapping();
    +        list(, $fid) = explode(':', $contexts['feed'], 2);
    +        $feed = $this->feedStorage->load($fid);
    +        $context = new Context(new ContextDefinition('entity:aggregator_feed'));
    +        $context->setContextValue($feed);
    +        $event->setContext('aggregator.feed:' . $feed->id(), $context);
    

    I must say it's really strange to me that the context provider class is responsible of injecting the context into the block plugin. There should be a separate component that's in charge of applying context mappings based on available context. The job of this class is providing context and that should be it.

    More generally, I do not think it makes much sense to expose the context of each entity globally as done for feed entities in this patch via "agreegator.feed:{ID}" - that way we'd end up with tons of contexts. Instead, that's actually a case where context is full-filled without a context mapping, but by directly provided value by the user (direct input value in Rules slang). For solving that properly adding something along the lines of registering "static context" or more sophisticated context configuration processing in order to support "direct input values" would be required. But imho that's far out of scope for this patch - so maybe we should come up with a better, easier to solve example here?
    One I could think of in core is "users", on the user profile page you have both the context of the viewed and of the current user available.

  2. +++ b/core/modules/aggregator/src/Tests/DeleteFeedTest.php
    @@ -30,10 +30,10 @@ function testDeleteFeed() {
    -    $block->getPlugin()->setConfigurationValue('feed', $feed1->id());
    +    $block->getPlugin()->setContextMapping(['feed' => 'aggregator.feed:' . $feed1->id()]);
    

    This highlights perfectly that there shouldn't be a hard separation between context vs configuration parameter. Also see #1976758: Plugins miss metadata about configuration.

  3. I think the aggregator block context should also include block_count - ie. everything from AggregatorFeedBlock::blockForm()? /blockquote>
    Yep, exactly. That's how the system really ends up being powerful (and Rules works) - but that's a bit off-topic here.

EclipseGc’s picture

1.) "One I could think of in core is "users", on the user profile page you have both the context of the viewed and of the current user available."

The Global user is available through a similar methodology as this and the user profile is available via the routing mechanisms that fubhy could well describe. The "strange"-ness of this approach is due to core's block placement notions. I don't disagree with your basic statement about how strange that is, but it's because of core, and this gives us the appropriate underlying structure required in order to create more sophisticate context resolution processes in contrib. The specific code you've highlighted here is an optimization to prevent every single aggregator feed entity from being loaded into the global context needlessly and only loads them for the blocks that need them. YES there are way better ways to get at these context; NO core can't really afford to contemplate them at this point. That's why this approach is isolated in a block specific context event and block config entities.

2.) I'd prefer to NOT continue to argue this at this point. The amount of work required against core blocks to even contemplate such an undertaking is completely ridiculous at this point, and on top of that, we don't agree. I'd also argue that thinking of context and configuration as "the same thing" is exactly what has made the current aggregator feed block as problematic as it is. You're just trying to come at it from the opposite angle. Even if I could be convinced of this (and I'm still on the fence) that ship has sailed for D8. Let's get reasonable context object mapping into core instead.

Eclipse

japerry’s picture

Issue summary: View changes
FileSize
752.42 KB

Here is a re-roll of the patch. Not sure why, but the annotation doesn't appear to be working. Following webchick's steps in #30, I get to this on the block:

japerry’s picture

FileSize
26.4 KB
japerry’s picture

Status: Needs work » Needs review
FileSize
28.31 KB

After talking a bit with EclipseGC, I've re-rolled again, where it seems to be working correctly with feed aggregator, it also fixes a problem introduced with #1763964: Use #type => link for theme_aggregator_block_item() -- because it defines the link incorrectly (uses href instead of url)

We added a markup value when only one context is available instead of hiding it from the user. Adding this also shows up in conditions, so we know which contexts are being added.

Status: Needs work » Needs review

dsnopek queued 65: 2377757-65.patch for re-testing.

japerry’s picture

This patch takes a slightly different approach to #2429501: AggregatorFeedBlock does not output item links, which should get committed before this.

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
28.85 KB

Ok, we introduced some exceptions with the last patch, let's see how this one fairs.

Changes:

  • 56.2: Converted to UUID instead of feed id.
  • 56.3: Fixed
  • 56.4: Previously addressed.
  • 56.5: Documented inline now.
  • 56.6.: Till I see a documentation standard that says that, I'm leaving this as is. All the condition plugins are single line.

Eclipse

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
1.8 KB

Trying to fix tests.

Eclipse

EclipseGc’s picture

FileSize
28.85 KB

Well... that was novel...

Wim Leers’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
    @@ -100,7 +100,7 @@ public function __construct(array $values) {
    -      if (isset($values[$key]) && $values[$key] instanceof TranslationWrapper) {
    +      if (isset($values[$key]) && $values[$key]->get() instanceof TranslationWrapper) {
    

    Points to missing test coverage?

    Already remarked in #56.1… but never addressed.

  2. +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
    @@ -51,9 +51,17 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
    +          '#title' => $this->t('@context value:', ['@context' => ucfirst($context_slot)]),
    

    Is the ucfirst appropriate? Isn't this enforcing the rules for English?

  3. +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
    @@ -51,9 +51,17 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
    +        // Remove the default value title because we have more than one value.
    

    "default value" vs "context slot" -> strange

  4. +++ b/core/modules/aggregator/config/schema/aggregator.schema.yml
    @@ -44,6 +44,10 @@ block.settings.aggregator_feed_block:
    +    context_mapping:
    +      type: mapping
    +      label: 'Context assignments'
    +      mapping:
    ...
    +          type: string
    +          label: 'Feed'
    

    See #56.2 + #57.2. Also never addressed.

    In fact, I see that *nothing* in #56 was ever addressed. The points were replied to, but the patch was never updated. Marking NW for that.

    If we keep writing reviews without those reviews being actually addressed, we're going to be stuck in an endless loop here.

  5. +++ b/core/modules/block/src/EventSubscriber/NodeRouteContext.php
    @@ -58,7 +61,7 @@ public function onBlockActiveContext(BlockContextEvent $event) {
    +    $context = new Context(new ContextDefinition('entity:node', $this->t('Node from route')));
    

    Where is this translated string exposed? "route" likely is a too technical term.

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
28.97 KB
2.35 KB

75.1: Yeah I've not attempted to fix the test coverage yet. This is totally true.
75.2: I don't care. Changed.
75.3: Tried to add a clearer comment.
75.4: True I didn't add a comment (fixed) however I certainly did reconfigure the entire patch to work on feed entity uuids, so 57.2 was definitely addressed. (my bad for no interdiff)
75.5: It displays in the context title or selection elements. I've changed it to "Node from url" for the time being.

I'll work on 56.1 now, but I want to make sure this still passes tests and everything.

Eclipse

EclipseGc’s picture

FileSize
1.54 KB
29.4 KB

Anyone know why that doesn't appear to have been queued for testing? Well here's another anyway. With the context system now at block's disposal properly we can abstract the cacheTag retrieval for virtually all blocks that require context. :-D

Eclipse

Wim Leers’s picture

  1. +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
    @@ -53,13 +53,14 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
    +        // avaialable, we have multiple and require user interaction.
    

    s/avaialable/available/

  2. +++ b/core/modules/aggregator/config/schema/aggregator.schema.yml
    @@ -48,6 +48,7 @@ block.settings.aggregator_feed_block:
    +        #This will be stored in the format of aggregator.feed:$uuid
    

    s/#This/# This/

    Let's put single quotes around the example, and add a trailing period.

  3. +++ b/core/modules/block/src/EventSubscriber/NodeRouteContext.php
    @@ -61,7 +61,7 @@ public function onBlockActiveContext(BlockContextEvent $event) {
    +    $context = new Context(new ContextDefinition('entity:node', $this->t('Node from url')));
    

    s/url/URL/

I'll work on 56.1 now, but I want to make sure this still passes tests and everything.

Testbot is broken ATM: #2467621: Tests not being added to the testbot queue… :( Almost my entire workday already…

Wim Leers’s picture

The #77 interdiff looks awesome! Generically doing this FTW! Except… I wonder if we actually want \Drupal\Core\Plugin\ContextAwarePluginBase to do that already? ContextAwarePluginBase could implement CacheableDependencyInterface and then do that.

And then it actually could and should also do the analogous thing for ::getCacheMaxAge() + ::getCacheContexts(). Because the context that the block depends on may only be valid for a limited amount of time (max-age) or may vary depending on some context, e.g. negotiated language (contexts).

EclipseGc’s picture

FileSize
3.24 KB
30.23 KB

Ok, I think this addresses everything in 79.

Eclipse

dsnopek’s picture

Issue summary: View changes

Issue summary changes: Added steps to test and put a link to the comment which shows the UI changes.

dsnopek’s picture

Issue summary: View changes
FileSize
27.03 KB

In manually testing this (following the steps in the issue summary), the aggregator block wouldn't show up until I cleared the cache. With all the caching changes lately (including enabling the page cache by default: #606840: Enable internal page cache by default), maybe this is the cause of the tests failures on the latest patch?

Also, the change in #76 that removed ucfirst() (per Wim's review in #75.2) causes the field label in the aggregator block to be "feed value" (with a lower-case first letter) which doesn't match Drupals normal capitalization standards:

Looking at the patch, it looks like it's setting the label for the context to "Aggregator feed":

+++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
@@ -23,19 +22,16 @@
+ *   context = {
+ *     "feed" = @ContextDefinition("entity:aggregator_feed", label = @Translation("Aggregator feed"), description = @Translation("Select the feed that should be displayed"))
+ *   }

... so, I'd expect that to be what appears on the form!

dsnopek’s picture

Per the field label stuff from my last comment, is there a reason we don't switch to:

--- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
@@ -53,7 +53,7 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
         ];
         $element['context_slot_title'] = [
           '#type' => 'item',
-          '#title' => $this->t('@context value:', ['@context' => $context_slot]),
+          '#title' => $this->t('@context value:', ['@context' => $definition->getLabel()]),
           '#markup' => $options[$context_id],
         ];
       }
@@ -65,7 +65,7 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
 
         $assignments = $plugin->getContextMapping();
         $element[$context_slot] = [
-          '#title' => $this->t('Select a @context value:', ['@context' => $context_slot]),
+          '#title' => $this->t('Select a @context value:', ['@context' => $definition->getLabel()]),
           '#type' => 'select',
           '#options' => $options,
           '#required' => $definition->isRequired(),

Or even just use the $definition->getLabel() as the title straight up, like this:

--- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
@@ -53,7 +53,7 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
         ];
         $element['context_slot_title'] = [
           '#type' => 'item',
-          '#title' => $this->t('@context value:', ['@context' => $context_slot]),
+          '#title' => $definition->getLabel(),
           '#markup' => $options[$context_id],
         ];
       }
@@ -65,7 +65,7 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
 
         $assignments = $plugin->getContextMapping();
         $element[$context_slot] = [
-          '#title' => $this->t('Select a @context value:', ['@context' => $context_slot]),
+          '#title' => $definition->getLabel(),
           '#type' => 'select',
           '#options' => $options,
           '#required' => $definition->isRequired(),

In the plugin annotation, the label is inside @Translation(...) where as $context_slot is the machine name, so this should be better from a multilingual perspective too...

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
1.43 KB
30.27 KB

Ok, I'm an idiot... This doesn't fix any review post 80, I just wanted to get the bug fixed.

Eclipse

Bojhan’s picture

I wonder if we can just go with the @context as label itself, so in this case "Feed". It might seem rather direct, but at core that is what its about and how we also show many other labels in our UI. For example "Title" in this screen isn't "Block label value".

dsnopek’s picture

The latest patch in #86 fixes the issue I had with the 'Aggregator feed' block not appearing until I cleared the cache which I mentioned in #84.

Here's a very cut down version of the steps I followed:

  1. Adding the aggregator feed source
  2. Then adding the block to the sidebar
  3. Visiting frong page: no block visible
  4. Then forcing cron to run and verifying that the items were imported
  5. Visiting front page: no block visible
  6. Do drush cr then the block would appear

But with the patch from #86, the block will appear filled with data correctly at step 5!

(Note: this problem only happened with these specific steps - adding the aggregator feed then running cron before adding the block didn't have this problem...)

Anyway, it's fixed now!

EclipseGc’s picture

Ok, wrote a test for 56.1 and fixed all the rest of the issues I know about with this.

Eclipse

EclipseGc’s picture

I had to have a fall back to the context_slot string because the label can be NULL for a context, so we can't depend on that solution. Still pretty minor all things considered and much prettier when the plugin is setup nicely.

Eclipse

EclipseGc’s picture

Issue summary: View changes
dsnopek’s picture

Awesome, it's looking good to me!

I had another random question/comment about the field title:

+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
@@ -51,16 +51,28 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
+          '#title' => $this->t('@context', ['@context' => $title]),

Is running this through $this->t() really necessary? My understanding of @Translation() in annotations is that the string gets run through t() when the annotation is evaluated:

The @Translation() wrapper serves this purpose. It will run the text through t() when the annotation is evaluated.

... from https://www.drupal.org/developing/api/8/localization

If this is just about sanitizing $context_slot in the case that it's used, we could use String::checkPlain($context_slot) when setting the $title variable, right?

Anyway, I don't do as much Drupal 8 development as you guys, so maybe the $this->t() is necessary or part of a coding style thing. If so, please just let me know and I'll stop nit-picking this miniscule bit of code. :-)

EclipseGc’s picture

FileSize
1.48 KB
33.01 KB

Blah, you are correct. We do need to make the strings translatable if they aren't already though, so we conditionally swapp $this->t() or $definition->getLabel().

Eclipse

EclipseGc’s picture

FileSize
1.36 KB
32.89 KB

Ok, we can't really use t() effectively this way, so I've just removed it. We'll file a follow up to make the ContextDefinition annotation label required which will solve this weird situation.

Eclipse

dsnopek’s picture

Thanks! This is RTBC as far as I'm concerned. :-) But I'll leave it to someone more "D8 qualified" to do review and actually update the status.

Wim Leers’s picture

Status: Needs review » Needs work

Would you consider moving the test coverage + associated bugfix in #89/#56.1 into a separate issue? We could get that committed easily, and it's a bug independent from this issue; it was merely uncovered in this issue. You get progress, we get a simpler patch to review.


An almost comprehensive review:

  1. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    @@ -204,6 +208,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +    $contexts = $form_state->getTemporaryValue('gathered_contexts') ?: [];
    

    Where does this magical string come from? Where is it documented?

  2. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    @@ -309,21 +316,39 @@ public function setTransliteration(TransliterationInterface $transliteration) {
       public function getCacheContexts() {
    ...
       public function getCacheTags() {
    ...
       public function getCacheMaxAge() {
    

    Correct me if I'm wrong, but these changes could also be moved into an issue of their own?

    Also: missing test coverage.

  3. +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
    @@ -51,16 +51,26 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
    +          '#title' => $definition->getLabel() ?: $context_slot,
    

    Why can the label be NULL?

  4. +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
    @@ -62,6 +62,10 @@ public function getContextMapping() {
    +    // The ContextAwarePluginAssignmentTrait adds a visual element to the
    +    // context mapping when only one context is available. We should unset this
    +    // before saving the context mapping.
    

    But this base class doesn't use that trait, so shouldn't we do this conditionally?

    It looks like class_uses() is the only option PHP gives us here.

  5. +++ b/core/modules/aggregator/config/schema/aggregator.schema.yml
    @@ -44,6 +44,11 @@ block.settings.aggregator_feed_block:
    -    feed:
    -      type: string
    -      label: 'Feed'
    +    context_mapping:
    +      type: mapping
    +      label: 'Context assignments'
    +      mapping:
    +        # This will be stored in the format of 'aggregator.feed:$uuid'.
    +        feed:
    +          type: string
    +          label: 'Feed'
    

    Is aggregator the only place that uses/needs this?

    The IS doesn't mention aggregator at all. Really, the IS is quite vague, having read it, I barely understand what this issue is about, and I definitely don't understand the consequences. I think it'd be *very* helpful if you could list the concrete consequences and benefits :)

  6. +++ b/core/modules/aggregator/src/AggregatorServiceProvider.php
    @@ -0,0 +1,33 @@
    + * Provides a block context event subscriber if the block module is enabled.
    

    block context? I don't think the context is block-specific at all?

  7. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,91 @@
    +        // The context mapping is stored as aggregator.feed:uuid, so we're just
    +        // extracting the UUID so that we can load the specific feed object.
    

    s/aggregator.feed:uuid/'aggregator.feed:UUID'/

    I think everything after the comma can be omitted. Just everything before the comma is sufficient to understand the next line.

  8. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,91 @@
    +        $context = new Context(new ContextDefinition('entity:aggregator_feed'));
    +        $context->setContextValue($feed);
    +        $event->setContext('aggregator.feed:' . $feed->uuid(), $context);
    ...
    +      $context = new Context(new ContextDefinition('entity:aggregator_feed', $feed->label()));
    +      $context->setContextValue($feed);
    +      $event->setContext('aggregator.feed:' . $feed->uuid(), $context);
    

    These look very bizarre. But not a problem introduced by this patch.

    But that DX looks … painful. So much repetition, or what looks like repetition, it's slightly different every time. I fear this will lead to a lot of frustration.

  9. +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -23,19 +21,16 @@
    + *     "feed" = @ContextDefinition("entity:aggregator_feed", label = @Translation("Aggregator feed"), description = @Translation("Select the feed that should be displayed"))
    

    See #56.6.

  10. +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -58,16 +53,13 @@ class AggregatorFeedBlock extends BlockBase implements ContainerFactoryPluginInt
    -   * @param \Drupal\aggregator\FeedStorageInterface $feed_storage
    -   *   The entity storage for feeds.
    

    Nice! :)

  11. +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -145,51 +123,43 @@ public function blockSubmit($form, FormStateInterface $form_state) {
    -  public function getCacheTags() {
    -    $cache_tags = parent::getCacheTags();
    -    $feed = $this->feedStorage->load($this->configuration['feed']);
    -    return Cache::mergeTags($cache_tags, $feed->getCacheTags());
    

    This looks like a regression though. If the items used in ::build() would use entity rendering instead of custom rendering though, then this wouldn't be a problem.

    Not the fault of this patch, but unfortunately this patch shows again how crappy this block's rendering is. I'm happy to help with that.

    Oops, no, 2. deals with that :)

EclipseGc’s picture

  • 97.1: This was introduced in #2339151: Conditions / context system does not allow for multiple configurable contexts, eg. language types and is part of the block context system that passes available contexts to condition plugins. The actual invocation of this is found in the BlockForm class. This specific key is not particularly documented anywhere, although the actual events dispatched are.
  • 97.2: You're sort of right and sort of wrong. Yes it could be split into a separate issue, but it would be 100% impractical since there's no actual use case (thus why we're actually converting Aggregator Feed Block here). I'd happily discuss what would be good as far as test coverage goes. I sort of assumed there must be existing test coverage for these methods. This could require more nuanced coverage though.
  • 97.3: Just sort of an artifact of Annotations. I could force it to not be allowed to be NULL but when it was introduced, it never displayed in the UI anywhere, so that wasn't a priority at the time.
  • 97.4: Unsetting a non-existent key doesn't appear to have any ill-effects in 5.4+ I'm fine with putting a check here if we really feel it's necessary, but I'm not sure it is.
  • 97.5: I originally added this to the block settings config schema and Alex Pott asked me to make it aggregator feed specific. I dislike that because it requires more from developers of new blocks, but it was not an unreasonable request either. Other blocks can add their own context mapping needs in the same way.

    I'll try to get the IS updated to more clearly communicate what this issue is doing.

  • 97.6: If you read the issue I linked in 97.1's reply, we introduced events for gathering contexts that are specific to blocks. In fact, the class this is conditionally registering on the service container is block context specific since the aggregator feeds would not be made available in the context stack to anyone else because there is not yet a global context stack, and even if there were, we'd need a different way of getting aggregator feed contexts into it #2349679: Support registration of global context
  • 97.7: ok
  • 97.8: You are preaching to the choir here. This is because we backed out of layouts and display management in core, had we moved forward with that, this sort of system would have been for the most arcane of circumstances instead of "I want a context for my block" Functionally we're treating all contextual requirements as though they're global when most of the time context needs are local to a single page. You're right though, this isn't introduced by this issue, this issue is just applying the need for this approach to aggregator feeds to make the block contexts.
  • 97.9: See #71

Thanks for the review Wim, I'll post a patch including 97.7 in the next update.

Eclipse

tim.plunkett’s picture

FileSize
15.21 KB

Here's the interdiff from #65 to #95.
I think we need more tests for some of these changes.

Wim Leers’s picture

#98:

  1. I think this should be documented somewhere.
  2. BlockBase didn't previously return anything for those, because that didn't make sense. So there's no test coverage for those methods in particular. The existing test coverage lives at \Drupal\block\Tests\BlockViewBuilderTest. But for this, you'll want an integration test that has several dummy contexts, and that verifies that their cacheability metadata is merged as expected by these methods.
  3. So perhaps we could change that?
  4. It just doesn't make sense to execute some logic always that only makes sense sometimes. Understandability matters.
  5. Ok, still waiting for the IS update.
  6. Ok.
  7. As I said, not a problem introduced by this patch. But IMO we should have at least a major issue to improve this DX.

You asked me in IRC what test coverage is needed:

  1. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    @@ -204,6 +208,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +    // Add context form elements.
    +    $contexts = $form_state->getTemporaryValue('gathered_contexts') ?: [];
    +    $form['context_mapping'] = $this->addContextAssignmentElement($this, $contexts);
    
    +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,91 @@
    +        $event->setContext('aggregator.feed:' . $feed->uuid(), $context);
    
    +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
    @@ -145,51 +123,43 @@ public function blockSubmit($form, FormStateInterface $form_state) {
    +    $feed = $this->getContextValue('feed');
    

    This looks like magical code, needs test coverage exerting several different scenarios.

  2. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    @@ -309,21 +316,39 @@ public function setTransliteration(TransliterationInterface $transliteration) {
       public function getCacheContexts() {
    ...
       public function getCacheTags() {
    ...
       public function getCacheMaxAge() {
    

    The necessary test coverage for this is already explained above.

  3. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -0,0 +1,91 @@
    +class AggregatorFeedContext extends BlockContextSubscriberBase {
    

    Needs a unit test probably.

  4. +++ b/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php
    @@ -0,0 +1,70 @@
    +class ContextDefinitionTest extends UnitTestCase {
    

    Hrm, weird, shouldn't this already have test coverage in HEAD?

    If it should, then please move this into a separate issue, so we can get that in already.

dsnopek’s picture

Issue tags:
jibran’s picture

Issue tags: +Needs beta evaluation
Berdir’s picture

Status: Needs work » Needs review
FileSize
31.69 KB
6.21 KB

Here's a reroll, proudly presented by the delayed Swiss LX 327 flight to Zurich :p

No major problems during the reroll, updating the block list builder and block repository to use the new block repository was already done as part of applying the patch, so that is not visible in the interdiff.

The interdiff just contains changes to update the new aggregator feed context to the new API. Which is IMHO *so much* easier. No more messing with block entities and parsing their configuration. We get a list of UUID's to load, can query them, load the feeds and return them. Trivial :)

Didn't run any tests but manual testing worked fine.

Oh, and cacheability metadata seems to be bubbling up just as it should. Tested manually that the aggregator feed tag is added to the page.

Fabianx’s picture

  1. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -51,41 +42,37 @@ class AggregatorFeedContext extends BlockContextSubscriberBase {
    -      if ($block_plugin instanceof ContextAwarePluginInterface) {
    -        $contexts = $block_plugin->getContextMapping();
    -        // The context mapping is stored as aggregator.feed:uuid, so we're just
    -        // extracting the UUID so that we can load the specific feed object.
    -        list(, $uuid) = explode(':', $contexts['feed'], 2);
    -        $feeds = $this->feedStorage->loadByProperties(['uuid' => $uuid]);
    ...
    +  public function getRuntimeContexts(array $unqualified_context_ids) {
    +    $ids = $this->feedStorage->getQuery()
    +      ->condition('uuid', $unqualified_context_ids, 'IN')
    +      ->execute();
    +    $contexts = [];
    

    This is an _awesome_ win!

    This shows exactly why the other issue was critical, because else this would have had to load all things or have to implement its own complicated API as seen above.

    +1

  2. +++ b/core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php
    @@ -51,41 +42,37 @@ class AggregatorFeedContext extends BlockContextSubscriberBase {
    +  public function getAvailableContexts() {
         $feeds = $this->feedStorage->loadMultiple();
    +    $contexts = [];
         foreach ($feeds as $feed) {
           $context = new Context(new ContextDefinition('entity:aggregator_feed', $feed->label()));
           $context->setContextValue($feed);
    -      $event->setContext('aggregator.feed:' . $feed->uuid(), $context);
    +      $contexts[$feed->uuid()] = $context;
         }
    +    return $contexts;
       }
    

    Do we really need to load all the feeds for that to work?

    Isn't the ContextDefinition with label and uuid enough?

    Or does entity storage not allow to just get a list of defined properties?

    In any case we should not be setting the runtime context here, then we can at least rely on GC to cleanup the feed object, but I think this could explode quite quickly and we have seen things like that explode in D7 (with bean), so this is also a more generic question.

Berdir’s picture

2. You can't get the label of an entity without loading it.

And the context system still works so that all possible context is loaded and presented so that you can select one.

That's not different from the current code, although that could now very easily use an entity selection widget to select a feed with a nice and scalable autocomplete textfield.

So, yes, I'm interested to get this functionality into core but I'm not convinced at all that aggregator feed blocks is a good example with the way context configuration works now.

The way I solved that with my static context patch in page_manager is to have a configuration form to select a specific entity using such an autocomplete widget and then just expose that as available context.

I understand that it is useful as an example to demonstrate this. But given that this is a normal task (given the current state in of block context in D8, it could probably also be classified as a major bug) I don't see how we can get this committed given that this comes with data changes.

So my recommendation revert those changes and just get the UI and other needed fixes in. And write some tests using a test block plugin.

Fabianx’s picture

Category: Task » Bug report
Priority: Normal » Major

That sounds good, indeed major bug.

I still think we are able to omit the:

$context->setContextValue($feed)

here and only rely on the definition and label.

Berdir’s picture

Status: Needs work » Needs review
FileSize
29.58 KB

Also, this conflicted already again with #2509600: AggregatorFeedBlock should return render array, keeping the changes in the build method minimal and not removing the if.

Berdir’s picture

Status: Needs work » Needs review
FileSize
29.94 KB
1.93 KB

Accidently defined the service in block.services.yml, that wasn't the idea.

EclipseGc’s picture

Having reviewed the interdiffs, I'm very onboard here. I think this patch might pass the test, some very very minor string fixes for the most part.

Eclipse

EclipseGc’s picture

Status: Needs work » Needs review

oops

EclipseGc’s picture

Ok, the block tests for contextual block satisfaction needed to be updated for the new block UI changes and the block placement screen needed to have the context repository. Pretty simple stuff.

The interdiff is missing docs on the contextRepository property, but the full patch has them. Apologies.

Eclipse

EclipseGc’s picture

Status: Needs work » Needs review
Wim Leers’s picture

We somehow completely lost track of this.

What's still left here? AFAICT this part from #106:

So my recommendation revert those changes and just get the UI and other needed fixes in. And write some tests using a test block plugin.

dsnopek’s picture

Issue tags: -Needs beta evaluation

re #117 and #106:

At the SCOTCH meeting today we discussed doing exactly that: splitting the aggregator feed block changes out into it's own issue, and having this issue just be about API and UI changes to make it possible. EclipseGC is going to work on it!

For what it's worth, the tests already in this patch seem pretty good! So, I don't think anything additional needs to be added, just the aggregator changes removed, and the issue summary updated.

Wim Leers’s picture

Sounds great! Let's get this done.

tim.plunkett’s picture

Removed the aggregator stuff. Also fixed a bad reroll.

tim.plunkett’s picture

Noticed extra code in the BlockListBuilder.

tim.plunkett’s picture

Not posting *another* patch right now, these could even be fixed on commit:

  1. +++ b/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php
    @@ -0,0 +1,70 @@
    +   * Test the ContextDefinition Annotation.
    

    Tests

  2. +++ b/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php
    @@ -0,0 +1,70 @@
    +    //throw new \Exception(print_r(, TRUE));
    ...
    +    //throw new \Exception(print_r(, TRUE));
    

    Remove these :)

Berdir’s picture

  1. +++ b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
    @@ -113,7 +113,7 @@ public function __construct(array $values) {
           // @todo Remove this workaround in https://www.drupal.org/node/2362727.
    -      if (isset($values[$key]) && $values[$key] instanceof TranslationWrapper) {
    +      if (isset($values[$key]) && $values[$key] instanceof Translation && $values[$key]->get() instanceof TranslationWrapper) {
             $values[$key] = (string) $values[$key]->get();
    

    might have been discussed before, but why this change exactly?

  2. +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
    @@ -51,16 +51,26 @@ protected function addContextAssignmentElement(ContextAwarePluginInterface $plug
    +        $element['context_slot_title'] = [
    +          '#type' => 'item',
    +          '#title' => $definition->getLabel() ?: $context_slot,
    +          '#markup' => $options[$context_id],
    +        ];
    

    I know we already use that term in this context, but context_slot seems quite weird to me. But I guess it's just consistent to continue use it.

  3. +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
    @@ -64,6 +64,10 @@ public function getContextMapping() {
    +    // The ContextAwarePluginAssignmentTrait adds a visual element to the
    +    // context mapping when only one context is available. We should unset this
    +    // before saving the context mapping.
    

    Yet another thing that doesn't properly consider optional contexts btw: #2484645: Assigning context mapping: allow empty selection for optional contexts.

  4. +++ b/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php
    @@ -0,0 +1,70 @@
    +    // If the label or description are not a Translation annotation instance,
    +    // it should be replaced by NULL.
    ...
    +    // If the label or description are a Translation annotation instance, the
    +    // translated string should be returned.
    

    are a => is a?

Wim Leers’s picture

#123.1: I asked the same in #56.1, then in #75.1, and in #76.1 @EclipseGc suggests it's to fix a bug, but he hasn't fixed the test coverage yet.

dsnopek’s picture

I created an issue which just has the aggregator changes from #115: #2550199: Add a ContextProvider for Feeds

EclipseGc’s picture

Issue summary: View changes
Wim Leers’s picture

Status: Needs review » Needs work
Issue tags: +Needs beta evaluation

The beta evaluation in #92 is very outdated. This issue does not touch caching at all anymore, that was done in #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed.


  1. +++ b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
    @@ -113,7 +113,7 @@ public function __construct(array $values) {
    -      if (isset($values[$key]) && $values[$key] instanceof TranslationWrapper) {
    +      if (isset($values[$key]) && $values[$key] instanceof Translation && $values[$key]->get() instanceof TranslationWrapper) {
    
    +++ b/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php
    @@ -0,0 +1,70 @@
    +    $values = [
    +      'value' => 'entity:node',
    +      'label' => new Translation(['value' => 'test']),
    +      'description' => new Translation(['value' => 'test']),
    +    ];
    +    $context_definition = new ContextDefinition($values);
    +    $definition = $context_definition->get();
    +    //throw new \Exception(print_r(, TRUE));
    +    // If the label or description are a Translation annotation instance, the
    +    // translated string should be returned.
    +    $this->assertEquals('test', $definition->getLabel());
    +    $this->assertEquals('test', $definition->getDescription());
    

    I guess this is the test coverage for #123.1.

    But I wonder if it's a test-only thing, the use of Translation versus TranslationWrapper?

  2. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    @@ -199,6 +202,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +    // Add context form elements.
    

    This needs a much clearer description, for example:

    // Add context mapping UI form elements.
    
  3. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    @@ -199,6 +202,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +    $form['context_mapping'] = $this->addContextAssignmentElement($this, $contexts);
    

    Don't we need test coverage for this new part of the UI?

  4. +++ b/core/lib/Drupal/Core/Block/BlockBase.php
    --- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
    +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginAssignmentTrait.php
    

    No test coverage for these UI changes either.

  5. +++ b/core/modules/block/src/BlockForm.php
    @@ -334,7 +334,12 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
         // Call the plugin submit handler.
    -    $entity->getPlugin()->submitConfigurationForm($form, $settings);
    +    $block = $entity->getPlugin();
    +    $block->submitConfigurationForm($form, $settings);
    +    if ($block instanceof ContextAwarePluginInterface && $block->getContextDefinitions()) {
    +      $context_mapping = $settings->getValue('context_mapping', []);
    +      $block->setContextMapping($context_mapping);
    +    }
    

    The comment now seems to apply to the setContextMapping() call too. This is misleading.

    Let's add another comment just before the if-statement?

  6. +++ b/core/modules/block/src/BlockForm.php
    @@ -345,7 +350,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    -      if ($condition instanceof ContextAwarePluginInterface) {
    +      if ($condition instanceof ContextAwarePluginInterface && $condition->getContextDefinitions()) {
             $context_mapping = isset($values['context_mapping']) ? $values['context_mapping'] : [];
             $condition->setContextMapping($context_mapping);
           }
    

    Why do we need to check if $condition->getContextDefinitions() contains anything, if the body of the if statement doesn't use that?

    It'd make more sense if we were checking if $values['context_mapping'] was not empty.

    So, what am I missing?

  7. +++ b/core/modules/block/src/BlockRepository.php
    @@ -10,6 +10,8 @@
    @@ -32,6 +34,20 @@ class BlockRepository implements BlockRepositoryInterface {
    

    I only see additions here, no removals. together with the fact that we haven't changed ContextHandler at all, this makes me wonder how this works at all in HEAD?

  8. +++ b/core/modules/block/src/Controller/BlockLibraryController.php
    index 5e8b54c..adb3930 100644
    --- a/core/modules/block/src/Tests/BlockInterfaceTest.php
    
    --- a/core/modules/block/src/Tests/BlockInterfaceTest.php
    +++ b/core/modules/block/src/Tests/BlockInterfaceTest.php
    
    +++ b/core/modules/block/src/Tests/BlockInterfaceTest.php
    +++ b/core/modules/block/src/Tests/BlockInterfaceTest.php
    @@ -99,6 +99,7 @@ public function testBlockInterface() {
    
    @@ -99,6 +99,7 @@ public function testBlockInterface() {
               '#options' => $period,
             ),
           ),
    +      'context_mapping' => array(),
           'display_message' => array(
             '#type' => 'textfield',
             '#title' => t('Display message'),
    

    So this is the only UI test change. Which means we don't test it.

    Shouldn't we have at least a cursory test?

  9. +++ b/core/modules/block/src/Tests/BlockUiTest.php
    @@ -166,20 +166,41 @@ public function testCandidateBlockList() {
       public function testContextAwareBlocks() {
    

    For example, this could be expanded to use the context mapping UI, couldn't it?

    (Or, preferably, an additional context-aware block that doesn't use the CurrentUserContext.)

  10. +++ b/core/modules/node/src/ContextProvider/NodeRouteContext.php
    @@ -63,7 +66,7 @@ public function getRuntimeContexts(array $unqualified_context_ids) {
    -    $context = new Context(new ContextDefinition('entity:node'));
    +    $context = new Context(new ContextDefinition('entity:node', $this->t('Node from URL')));
    

    This suggests missing test coverage?

Frando’s picture

Edit: ignore, patch includes cruft from git gone havoc. #133 it is.

Frando’s picture

Status: Needs work » Needs review
Frando’s picture

FileSize
979 bytes
55.03 KB

Edit: ignore.

Frando’s picture

FileSize
1.26 KB
55.34 KB

Edit: ignore.

Frando’s picture

FileSize
23.79 KB
35.37 KB

Edit: ignore.

Frando’s picture

FileSize
22.52 KB
1.26 KB

Edit: Ignore #128 - #132. Git played games with me, sorry for the spam.

So, I was trying to get this to work with #2561935: Create a block for every field (which'll take the entity as context) and the core block layout UI (not with page manager), and it threw errors about no context being present, even though the block placement edit UI showed the mapping correctly. I started to debug, and after an hour arrived at attached interdiff. Not sure if this is correct, but without, the context is lost, because only the block ID and not the already-assigned context is transferred when BlockViewBuilder creates a #lazy_builder.

Didn't address anything else, was for the moment just trying it out and debugging to get the context assignement to work at all. Interdiff does not include rebase on HEAD (no intervention needed but some moved hunks).

Setting to NR to get a test run.

Fabianx’s picture

Not sure what Wim's opinion is, but we talked a lot about context dependent plugins in the lazy builder issue:

#2543340-50: Convert BlockViewBuilder to use #lazy_builder (but don't yet let context-aware blocks be placeholdered) and following has the reasoning we came up with.

It seems context aware blocks do not work, because some Block* manager injects the contexts prior to displaying, which means indeed that - while the context is available in theory (we are in the same request context) - the lazy builder does not have the necessary information on where to get it.

Edit: It is _this_ issue which does that and hence it needs to work around that, core is fine.

Therefore I think opting out blocks with the one line fix of #133 is the right way to go.

Could we push that to another major bug issue though as follow-up to #2543340: Convert BlockViewBuilder to use #lazy_builder (but don't yet let context-aware blocks be placeholdered) - as it could easily get lost in here?

Thanks for working on that. Context is unfortunately not much supported in core and such bugs like these are hard to find and fix - if they don't make tests fail.

Wim Leers’s picture

#133 + #137: See #2543340-41: Convert BlockViewBuilder to use #lazy_builder (but don't yet let context-aware blocks be placeholdered) and #2543340-48: Convert BlockViewBuilder to use #lazy_builder (but don't yet let context-aware blocks be placeholdered). Please read those comments in full.

In a nutshell:

  • we can make blocks use #lazy_builder, but then we have to read the block plugin's annotation, look at the contexts it may consume, and pass those in the #lazy_builder's arguments
  • BUT we can never ever prevent blocks from reading context they should not be reading: block plugins SHOULD only read the contexts they declare they depend on in their annotation, BUT the unfortunate design of allowing blocks to access ANY context means that it is impossible to guarantee no block is doing something it should not do.
Fabianx’s picture

Status: Needs review » Needs work

Ignore #137, an extra issue is not needed, the problem is introduced in this code.

+++ b/core/modules/block/src/BlockRepository.php
@@ -58,6 +77,13 @@ public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) {
+      $block_plugin = $block->getPlugin();
+      if ($block_plugin instanceof ContextAwarePluginInterface) {
+        $contexts = $this->contextRepository->getRuntimeContexts(array_values($block_plugin->getContextMapping()));
+        $this->contextHandler->applyContextMapping($block_plugin, $contexts);
+      }

This is the problem.

You are not allowed to inject the contexts here, but will need to do so in the callback called by #lazy_builder.

Then lazy_builder continues to work and the opt-out is not needed.

Frando’s picture

Status: Needs work » Needs review
FileSize
22.08 KB
2.5 KB

Yes, this makes totally sense. Works as expected.

#138: Do we have to pass the context annotation to #lazy_builder? We load the plugin anyway later on, and the config is present then anyways I think (or not?).

Other feedback still not addressed, and this needs tests. I likely won't be able to work on this in the next week.

tim.plunkett’s picture

  1. +++ b/core/modules/block/src/BlockRepository.php
    @@ -78,12 +78,6 @@ public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) {
    -      $block_plugin = $block->getPlugin();
    -      if ($block_plugin instanceof ContextAwarePluginInterface) {
    -        $contexts = $this->contextRepository->getRuntimeContexts(array_values($block_plugin->getContextMapping()));
    -        $this->contextHandler->applyContextMapping($block_plugin, $contexts);
    -      }
    -
           $access = $block->access('view', NULL, TRUE);
    

    This change should fail tests. If not, we are missing coverage.

    Note the last line of the code I highlighted. We check access here, and we ABSOLUTELY need the contexts applied before calculating that.

  2. +++ b/core/modules/block/src/BlockViewBuilder.php
    @@ -141,6 +140,12 @@ protected static function buildPreRenderableBlock($entity, ModuleHandlerInterfac
    +      $contexts = \Drupal::service('context.repository')->getRuntimeContexts($plugin->getContextMapping());
    +      \Drupal::service('context.handler')->applyContextMapping($plugin, $contexts);
    

    Both of these services should be injected, unless this hunk is reverted.

Frando’s picture

#141.1: Right, I just realized this too. I misread the code in BlockAccessControlHandler::checkAccess() that injects contexts - it only injects contexts into each of the block's conditions, but not into the block plugin itself. So this hunk has to be reverted, yes.

It also makes me wonder though if we shouldn't just move the context injection right into block loading, because this seems really easy to get wrong: Whenever you load a block (to display it somewhere, I think block usage in D8 contrib will extend to other places than just the region assignment) you have to inject context, otherwise access cannot be checked - so why not inject context directly when loading the block, or within the access handler? Are their situations where this is not desired?

#141.2: It's a static method, I don't think we can use DI there

tim.plunkett’s picture

Hm, I was reviewing the interdiff, not the patch.

+++ b/core/modules/block/src/BlockRepository.php
@@ -58,6 +77,7 @@ public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) {
       /** @var \Drupal\block\BlockInterface $block */
+
       $access = $block->access('view', NULL, TRUE);

This is the actual change made in the patch...

Because in HEAD, \Drupal\block\BlockAccessControlHandler::checkAccess() has the getRuntimeContexts() and applyContextMapping() calls.

Frando’s picture

# 143: Isn't that just an empty line that sneaked in?

Otherwise I don't really get what you're refering to - yes, in HEAD (and unchanged in patch) contexts are injected in BlockAccessControlHandler::checkAccess(), and this is why, I think, we don't have to inject contexts in BlockRepository::getVisibleBlocksPerRegion(). That's what my last interdiff did, and what you seemed to imply in #141 would be a nogo.

Fabianx’s picture

#145: Your patch is correct. The interdiff was what was confusing.

Also it is confusing that there are conditions and contexts.

BUT:

conditions == access - work great on HEAD currently - unchanged here
context == variation - does not work in HEAD, is changed here

Tim thought you moved the conditions around, but you only moved the context, so it is all good :).

If you want to inject the things, than you have two possibilities:

- Load the block_view_builder via getting the entity view builder for block and calling a method there from the static method

OR

- create a service for building blocks

OR

- expose block view builder as a service (no idea how that would work).

Overall:

I don't think its possible to DI that properly, so I would make that a follow-up.

@Tim: Thoughts?

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
22.08 KB

Straight reroll first. s/TranslationWrapper/TranslatableString

tim.plunkett’s picture

Assigned: EclipseGc » tim.plunkett

Working on tests for this.

tim.plunkett’s picture

#127

1) This mimics the use of @Translation in plugin annotations.
2) Done
3) Yes, see 9
4) Yes, see 9
5) Done
6) Investigating next, not addressed here.
7) Reverted, that seems to be cruft from earlier patches.
8) Yes, see 9
9) See 3/4/8... :) Yes, I added test coverage for the UI here. It revealed a schema issue, glad to catch that now.
10) I don't personally think this needs coverage, it just enhances the UX as per this change:

-          '#title' => $this->t('Select a @context value:', ['@context' => $context_slot]),
+          '#title' => $definition->getLabel() ?: $this->t('Select a @context value:', ['@context' => $context_slot]),
tim.plunkett’s picture

Okay I looked into #6 more.

The getContextDefinitions() check is a copy/pasted performance micro-optimization. In this method we update the context mapping of both the block and of each condition, yet in the second one we check the pre-munged values. This fixes that.

tim.plunkett’s picture

Someone added a beta evaluation since last tagged.

dsnopek’s picture

@tim.plunkett: Thanks for picking this up!

I did some manual testing with this and this CTools patch #2561935: Create a block for every field (which'll take the entity as context) (which adds a bunch of context-aware blocks) and everything worked great! (Although, I did find some things to improve in the CTools patch ;-))

Unfortunately, the code changes here are a little beyond me for the most part. :-/ But the tests look correct to me. So, from the outside I think this is doing all the things it should be doing.

I only noticed one nit-picky thing:

+++ b/core/tests/Drupal/Tests/Core/Annotation/ContextDefinitionTest.php
@@ -0,0 +1,70 @@
+      //'label' => new Translation(['value' => 'test']),
+      'label' => 'test',

Not sure why the translated label is commented out? If it works, we should definitely use the Translation object. If not, then we should put a comment as to why it doesn't.

dsnopek’s picture

A few more thoughts after further testing:

  1. Required contexts are handled a little different than I would have expected. It makes the mapping required (ie. have to select a value on the form) but it'll still try to render a block which has a required context with no value. I would have expected it to skip rendering the block altogether. But I'm not sure if that comes from this patch, or is an existing thing in core.
  2. If only one possible mapping is available, it's automatically selecting it and not giving the user an option. This makes sense for required contexts, but not for optional contexts. If the context is optional, you should have the choice to not map it, right?

This may be from me misunderstanding how contexts work in D8, though. :-)

dsnopek’s picture

re #153.2: It looks like @Berdir brought up the same thing in #123.3 and there's an issue to fix the code as it exists in core - #2484645: Assigning context mapping: allow empty selection for optional contexts. It sounds like @Berdir was saying that this patch (as of #123 - it might have changed) was making things even worse? I'm not sure if it makes sense to merge that fix in here because now core will be using it, or if it should stay a seperate issue...

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
21.96 KB
8.21 KB

Conflicted with #2458763: Remove the ability to configure a block's cache max-age.

Addressed #152.

Also removing as many unnecessary changes as possible.

16 files changed, 294 insertions(+), 13 deletions(-)

EclipseGc’s picture

Status: Needs review » Needs work
+++ b/core/modules/block/tests/modules/block_test/src/ContextProvider/MultipleStaticContext.php
@@ -0,0 +1,80 @@
+/**
+ * @todo.
+ */

Please fix.

Otherwise this is looking pretty great.

Eclipse

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
4.66 KB
18.14 KB

I revisited the @Translation change again, and it *is* out of scope. So is the other change to submitting conditions.

Fixing the @todo as well.

14 files changed, 222 insertions(+), 10 deletions(-)

dsnopek’s picture

Issue summary: View changes

Took a stab at updating the beta evaluation.

japerry’s picture

Status: Needs review » Reviewed & tested by the community

I agree that the translation part should be removed for now, since the test written doesn't appear to be right from the beginning.

If there is missing test coverage for translation, it probably can be taken care of in a future issue.

Bojhan’s picture

We are not changing UI, yay! Looks good overall, I hope we can find a solid integration point in 8.1

tim.plunkett’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
63.46 KB
2.08 KB
16.62 KB

We have one weird thing remaining. When you have a context, but only one, this patch is adding a weird UI element.
It doesn't need to, it doesn't convey any helpful information, and it is out of scope here (I found it while trying to reduce changes).

tim.plunkett’s picture

Somewhere along the way, the original test for #2277981: Provide a service for handling context-aware plugins was broken.
Since the Block UI changed, the xpath needs to change too.

dsnopek’s picture

Regarding the changes in #163: this was something requested by Wim in #51. I know it's fewer changes, and not something we did in D7 (in the Panels world) but I kinda liked it. Since "Current user" is always available, when you select a block that shows data from the user, it's instructive to see "Current user" on the settings page so you know where this data is coming from.

In any case, I'd be fine with either including on not including that feature - getting this committed is most important! - just to give it's history and say that I liked it. :-)

dsnopek’s picture

Status: Needs review » Reviewed & tested by the community

Assuming this passes tests, this should be ready to go! Marking as RTBC

EclipseGc’s picture

Completely ok with this UI (non) change. In fact I prefer it. RTBC+1

Eclipse

Berdir’s picture

See also #2484645: Assigning context mapping: allow empty selection for optional contexts, which is somewhat related, I think it would be a easier to just always make the select visible. I think it does have useful information as you might not actually know what kind of context the block uses and what is used. And as shown in the issue above, it is currently impossible to *not* select a context for an optional context if at least one is available that would match.

tim.plunkett’s picture

I think that will likely be the solution in that other issue. But the logic around the select is not changed in this patch.

It needs further usability review and discussion over there.

Wim Leers’s picture

This looks great to me now. Fewer, more focused changes. A clear scope really helps. #165++ and #169++ regarding my remark in #51: we can do that in the other issue, it does indeed merit usability review.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Reviewed the screenshots in #48 with @tim.plunkett, @EclipseGc, @japerry, @mikepotter, et al at DrupalCon Barcelona.

The one thing I had questions around was the title/description of the selector, but this is set per-context definition, which means they can decide what makes the most sense for their use case. +1.

The code has been reviewed by many awesome people, this is a contrib project blocker, let's get it done.

Committed and pushed to 8.0.x. Thanks! :D

  • webchick committed f056772 on 8.0.x
    Issue #2377757 by EclipseGc, tim.plunkett, Frando, Berdir, japerry,...
dsnopek’s picture

Huzzah! Thanks everyone! :-)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

Wim Leers’s picture

StryKaizer’s picture