Problem/Motivation
It can be quite difficult to get Content Blocks revisioning with the entity to which they are attached. This is complicated by the fact that scenarios in which content blocks are heavily used can clutter the block placement UI with an enormous number of blocks that were likely never intended for reuse.
Proposed resolution
We need a new way of interacting with Content Blocks that does not save them in a traditional entity approach. A null save that allows us to get the result of this save and serialize it into the block configuration. Serialized content blocks would be treated as a revisionable string in panelizer's normal operation which would mean that content blocks used this way would automatically revision with the rest of the "config" blocks because the storage would all be on the panelizer field.
Remaining tasks
We need to really clean up the following patch, update schema, make certain our UI stuff all makes sense and test out weird stuff like dates and file/image fields. Also, it's best if this field hides the configuration block's label form elements and just overrides the normal label with the title of the content block entity. We do that already for panelizer's IPE integration but need to figure out how to do it more generically.
User interface changes
A new section under IPE's block placement will be "custom" (I'd like to probably change this to inline). Within that section you'll find "Inline ..." blocks. These are what you want to test with. New ones should appear there automatically when you create new content block types.
API changes
This code depends on Inline Entity Form module. Be certain to download it if you want to play with this.
Data model changes
We introduce a new block type (inline block). So the existing data model's not changed, we're just abusing it a bit. :-D
Initial credit
I didn't do this first pass at a patch on my own, phenaproxima was instrumental in helping to make this happen as was tedbow.
Comment | File | Size | Author |
---|---|---|---|
#46 | 2844054-46.patch | 38.18 KB | phenaproxima |
#44 | 2844054-44.patch | 43.69 KB | phenaproxima |
Comments
Comment #2
DamienMcKennaOh, I like the idea! This is very similar to what Paragraph Panes does.
Comment #3
DamienMcKennaIt might help to add a test method that includes a file field.
Comment #4
phenaproximaAdded a little more rigorous testing and fixed bugs discovered by it. This is going to need really thorough test coverage.
Comment #5
DamienMcKennaWould it be ok to finish this after the 3.0 release?
Comment #6
phenaproximaHere's a new version of the patch that fixes some stuff. I have added thorough kernel-level test coverage of the panelizer_block_content shadow entity type, proving that it behaves identically to a block_content entity as far as Drupal is concerned, and transparently so.
Comment #8
samuel.mortensonI noticed that the current test coverage doesn't assert that rendering blocks will result in the same HTML - is that still going to be covered?
Comment #9
phenaproximaYes. Absolutely. And I want to test Quick Edit compatibility too. But all of that, in my mind, is contingent upon the shadow blocks being transparently identical to normal ones. So the storage stuff is the foundation.
Comment #10
phenaproximaAnother iteration. This patch:
Comment #11
DamienMcKennaThinking through it, this might be better in Panels so that it could be used by *all* Panels-based display systems.
Comment #12
phenaproximaIT WORKS!
This has no tests yet, and there are known bugs (particularly a bizarre double-save that might be IPE's fault, but I don't know yet) but it proves the concept. Behold, the prototype of inline revisionable content blocks!
Props to @EclipseGc for his invaluable help in making this happen, and for writing the original versions of the InlineBlock plugin and its deriver.
Still need Quick Edit integration and tests.
Comment #14
EclipseGc CreditAttribution: EclipseGc commentedOk, fixed an issue we had with custom blocks actually getting created in the previous patch.
Eclipse
PS If you want to try this against imagefield blocks, you need to hack the shared tempstore class and use the dependency serialization trait. Still trying to figure out how to move something that will suffice for that shortcoming into the patch.
Comment #15
phenaproximaThis is blocked by #2854542: PanelsIPEBlockPluginForm should use a real submit function.
Comment #16
DamienMcKennaPer today's scotch meeting, I'm moving this over to CTools so it can be a more foundational system that all Panels-ish modules can use.
Comment #17
EclipseGc CreditAttribution: EclipseGc commentedOk, so I moved this all over to ctools and added some small checks to make it work inside of regular block layout as well. Tested with imagefield and it worked (which seems to be the high bar here). I moved the tests over but didn't get them working yet. Removing quickedit tag for the moment. We'll do that in a follow up.
Eclipse
Comment #18
phenaproximaWon't pass the tests yet, but this formalizes the ctools_inline_block module and introduces a mechanism for loading inline blocks like normal entities.
Comment #19
phenaproximaOK. I've updated all the tests, so this should have parity (and then some) with the original patch for Panelizer. I'm leaving the "needs tests" tag applied because we should probably have coverage of how inline blocks interact with image fields -- that was a point of possible pain, so let's head off those problems at the pass.
Comment #21
phenaproximaAdded a functional JS test of using image fields with inline blocks! And it passes. On my localhost, anyway...
Comment #23
phenaproximaThe failures are not due to anything wrong with the patch; I suspect wonkiness in Drupal CI, and the fact that it cannot load in dependencies. Therefore, I'm marking this as needing manual testing because you need to run the test suite on a local machine.
Comment #26
DamienMcKennaI moved the new service into its own issue: #2858103: Add serializable tempstore service (extracted from #2844054)
This patch contains everything else. All changes by EclipseGc and phenaproxima.
Comment #27
DamienMcKennaNote: the tests will (most likely) fail because it needs #2858103.
Comment #29
DamienMcKennaI'm thinking of listing this as a blocker for Panelizer 3.0/4.0 final.
Comment #31
phenaproximaQueued #26 for re-testing now that #2858103: Add serializable tempstore service (extracted from #2844054) is in. I don't think it'll pass the tests unless Drupal CI knows how to download dependencies (seeing as how this depends on Inline Entity Form). But, let us wait and see...
Comment #33
phenaproximaProbably won't pass the tests. Don't care, because this really fixes problems with the storage layer.
As of this patch:
- Inline blocks will report their entity type ID as 'inline_block_content'. Themes can figure out on their own how to handle any CSS classes generated from that.
- Thanks to some good advice from @Berdir, this uses a few hooks to link block_content bundles' fields to inline blocks, without having to actually duplicate the configuration in the database.
- It works with entity reference fields of all kinds. I tested briefly with a user reference and an image field.
- The most arcane parts of the previous approach are gone. Everything just kinda...makes more sense. It's really nice.
Comment #35
phenaproximaYAY! This passes all tests locally. Drupal CI will fail it because it can't load up Inline Entity Form, but consider this as "needs review".
Comment #36
EclipseGc CreditAttribution: EclipseGc commentedAdded dispatching for block add/update/delete events on the BlockVariantTrait and panelizer support during those events.
Eclipse
Comment #37
DamienMcKennaPer discussion in Slack, this needs to accommodate for displays which are exported and then reimported via CI, so that the appropriate {inline_block} records are created.
Comment #38
EclipseGc CreditAttribution: EclipseGc commentedCurrent patch. Sorry for the lack of interdiff, kind of drive-by-patching today since #drupalcon.
I'll try to get an interdiff later.
Eclipse
Comment #39
phenaproximaRe-rolled on top of #2876732: Implement an API to "mask" entity types. This issue now blocked on that one.
Comment #40
sylus CreditAttribution: sylus commentedI took this patch for a spin against latest dev on lightning + landing_page and works really great :)
I did notice that when I saved the block once I wasn't able to subsequently change the title unless I changed the description (info property).
I'd be happy to do any further testing :)
Out of curiosity and sorry for the naivety but down the line could this potentially work with translations?
Comment #41
samuel.mortenson@sylus I believe that, with the way the patch is currently written, we assume that the storage for the Panels Display (i.e. the "panelizer" field) should handle translation, which means that inline blocks get translated the same way everything else (i.e. block titles) in the Panels Display does.
So hypothetically, if translation was enabled for the panelizer field for instance, you would just visit the translated version of a Panelized node and edit the inline block, and everything should just work. We should test this. :-)
Comment #42
sylus CreditAttribution: sylus commentedAh that is great news! I was only asking because that is what I tried and wasn't able to translate. That being said it might be because as mentioned earlier I am unable to save a different title for an already saved inline block item. This might be why when on my french panelized page it didn't work either.
I really appreciate you taking the time to respond and great news that in theory this functionality should work ^_^
Comment #43
EclipseGc CreditAttribution: EclipseGc commentedOk, this shouldn't be postponed because we committed the patch it depended on.
Eclipse
Comment #44
phenaproximaOkay, here we go. A re-roll on top of the committed Entity Mask submodule.
I pulled out all the stuff relating to Panelizer and Panels IPE -- those integrations belong in those modules. I'll file patches against those modules which integrate with this one. Plus dedicated tests.
Comment #46
phenaproximaHere's a version without the block variant stuff. To keep things manageable, I'll re-file that in a separate patch.
Note that this will not pass Drupal CI because it depends on Inline Entity Form. So I'm restoring the "Needs manual testing" tag.
Comment #48
phenaproximaAdding #2883103: BlockVariantTrait should fire events as a related issue, since it will be needed to integrate inline blocks with Page Manager and the Panels ecosystem.
Comment #49
phenaproximaFiled an initial patch against Panels to load inline blocks out of tempstore: #2883154: Support inline blocks in Panels display variants
Comment #50
phenaproximaThis blocks #2883154: Support inline blocks in Panels display variants.
Comment #51
dsnopekSo, I ended up here because I was trying to figure out how to implement #2886202-2: Make the Custom Block creation and placement process into a single form (see my thoughts there) and came to more-or-less the same conclusion as the issue summary here
However, I skimmed the patch, and I have question: Why does this need a new content entity type? Why not just a new block plugin that stores
\Drupal\entity_mask_test\Entity\BlockContent
entities from core in the block plugin's configuration?The extra content entity type is present all the way back to the earliest patch, and I didn't see an explanation in the comments here.
But I haven't dug too deep into this yet, so maybe I'll figure it out on my own when I do, but just thinking it through in my head it seems like that should work and be simpler... I think the problem really is
\Drupal\block_content\Plugin\Block\BlockContentBlock
and that its using derivatives (rather than configuration) - not the BlockContent entity type. What am I missing?Comment #52
dsnopekI've been reading this patch, trying to understand it. It still seems like the architecture is more complicated than necessary, and could be done simpler, but I'm assuming the extra complexity must be there to support something or workaround some issues, but it's not clear from the code. So, I'm going to review the patch and treat the parts I don't understand as documentation issues to make those things explicit. :-)
Could this class be renamed
InlineBlockContent
? It makes the other code really confusing to read because everytime I seeBlockContent
I have to keep doing Ctrl-Q in PHPStorm to see if it'sDrupal\block_content\Entity\BlockContent
or\Drupal\ctools_inline_block\Entity\BlockContent
. This was part of my quest to understand why this entity type is even necessary :-)Speaking of which, can the docblock for this class give the purpose of this entity type? My best guess so far is that it's to allow using inline_entity_form without saving a real content block to storage? Anyway, please document the why we need this entity type and what it's used for.
Since (I think) this is storing data for the
InlineBlockStorage
, maybe this code could be moved there and the hook implementation could just load the entity type storage handler and call some method? That way the implementation of that service isn't spread between the class and these hooks...What? Ok, so is the entity is going to be core
BlockContent
or the ctools_inlink_blockBlockContent
entity? (Another reason to rename that class ;-)) Is this again just bookkeeping for theInlineBlockStorage
? This deserves a comment explaining or at least making the code explicitly call into the storage class so it's clear what's going on, and why.Can you please document what this is for? Why do we need it?
Since this patch allows the actual value of the content block to be stored in the block plugin configuration, why do we need classes to load blocks? It doesn't appear to be used in ctools_inline_block so it must be an external API - can some information be given here for why another module would want to use this?
Except that it does save to the database! It doesn't store data on normal entity and field tables, true, but it does store stuff in the database. Can something be added to the docblock about why it's storing what it's storing to the database?
Also, this is "container aware", and while it's not necessary to implement the
ContainerAwareInterface
to work, I think it'd be nice so it's declaring to the outside world (or anyone reading this code) that you need to call->setContainer($container)
on it for the class to function correctly. Sort of "self documenting" code.So, this isn't something I'm confused about, but this could be more descriptive. Maybe "Block plugin that stores block content entities in configuration rather than referencing them"? That's kinda technical, but speaks more to the purpose of the class
Could be clearer. Maybe something like "Derives inline block plugins for every bundle on the content block entity type" ?
If I'm getting some this wrong (which, actually, I'm sure I am), I'd love an explanation! If that can work it's way into the patch itself, even better :-)
Comment #53
dsnopekI did some manual testing with this patch. Placing a block via core (ie. in Structure -> Block layout) worked great! I placed a custom block which had an image field on it, and the creation form worked perfect, and the block rendered fine on display too.
However, I tried to place a block with the Panels IPE with Panelizer and had problems. I used the patches at #2883154: Support inline blocks in Panels display variants and #2883394: Allow Panelizer to load inline blocks as well. I'm not sure if this is a problem with the patch here or one of those. But placing a custom block which has an image field in it fails on the "Upload" button with an AJAX error in the Javascript console - submitting the form without pressing the "Upload" button will complete, but the preview of the block isn't shown in the IPE or after saving when the Panels display is rendered. I tried with a custom block which just had a text field (ie. no image field) and the results were the same.
Comment #54
andypostRelated issue shows limit of string translation, so translations revisions will abuse string translations that used to translate config
Comment #55
NWOM CreditAttribution: NWOM commented#46 is sadly not compatible with newer D8 versions or D9. When forcing the install of
ctools_inline_block
andctools_entity_mask
via Backward Compatibility it WSODs with the following error messages:Warning: Declaration of Drupal\ctools_inline_block\Entity\InlineBlockStorage::cleanIds(array $ids) should be compatible with Drupal\Core\Entity\ContentEntityStorageBase::cleanIds(array $ids, $entity_key = 'id') in include() (line 15 of /modules/contrib/ctools/modules/ctools_inline_block/src/Entity/InlineBlockStorage.php)
Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException: The entity type inline_block_content does not have an "revision_created" entity revision metadata key. in Drupal\Core\Entity\EditorialContentEntityBase::revisionLogBaseFieldDefinitions() (line 35 of /core/lib/Drupal/Core/Entity/RevisionLogEntityTrait.php).
Even though I know that Layout Builder has more or less replaced Panels/Panelizer/FPP, etc, it is still incredibly useful when using Panels Everywhere since Layout Builder Everywhere still has quite a few bugs.
Edit: It looks like Page Manager now has a Layout Builder variant type. Adding inline-blocks should now be possible without problems. However there is still an issue with Panels Everywhere (for those overriding the entire page) here: #3143487: Not compatible with "Layout Builder" variant as site template (Workaround found)