Overview
Follow-up to #3478565: Add Entity Update controller. At the point of the issue we directly receiving the Layout and Model from the client in ApiContentUpdateForDemoController but not metadata fields like "title". but since that issue we added ApiPublishAllController which using the AutoSave data to save entity via ClientDataToEntityConverter, not directly from the client in the same request. we also then did #3493889: Change ApiContentUpdateForDemoController to save from auto-save instead of request data so the 2 controllers will act the more similar.
So we now we need ClientDataToEntityConverter also handle converting the metadata field which will be coming from version of the entity form on the client. The client is not yet sending this fields, that is being handled in #3487484: Save page data form values in application state with support for undo/redo. Once that is done the temporary "Publish" button should save the field since uses autosave to ClientDataToEntityConverter.
ApiPublishAllController functionality will still not be able to work because there is no "Publish all" button on the client. The start to that is being worked on in this issue #3491459: Implement the "Review N changes" button
We also need to save the entity fields that are exposed in the UI
Proposed resolution
Change ClientDataToEntityConverter to expect the 'entity_form_fields' key from the auto-save and save these values to the entity.
Because \Drupal\experience_builder\AutoSave\AutoSaveManager::save takes all the client request data and saves it to the auto-save ApiPreviewController will not need to be updated. We can just add an empty entity_form_fields to auto-save \Drupal\experience_builder\AutoSave\AutoSaveManager::save if it does not receive this key with a todo to remove it in #3487484: Save page data form values in application state with support for undo/redo
this issue will include tests updates that prove that both ApiPublishAllController and ApiContentUpdateForDemoController will both update entity fields like title if sent by client
User interface changes
Issue fork experience_builder-3488368
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3488368-convert-fields
changes, plain diff MR !474
- 3488368-save-metadata
changes, plain diff MR !423
Comments
Comment #2
tedbowI have started work on top of #3478565: Add Entity Update controller for now
Comment #4
tedbowComment #6
wim leersI think this MR is quite a bit too optimistic?! 😅🙈
\Drupal\Core\Field\WidgetInterface::massageFormValues()implementations.I'm fine with not fully solving this here. But not acknowledging that is not acceptable IMO.
In addition to acknowledging that, this code should point to a follow-up issue where that does get fixed. And #3446434: Document "Semi-Coupled Theme Engine" and "Redux-integrated field widgets" components landed 2 days ago, and documented precisely this in
docs/redux-integrated-field-widgets.md.Comment #7
larowlan@tedbow and I discussed this in light of the 'publish' functionality.
As we understand it, the publish feature will be similar to publishing a workspace. In the first instance it will take all auto-saved items and turn them into entity revisions. This won't make use of workspaces in the original implementation but it is hoped that it will by the end state.
If that's the case, then is there any need for this issue/controller? The only thing that writes to the entity will be the publish operation, i.e. all other controllers only write to the tempstore. In that case should we focus more on the publish operation taking all of the auto-saved items and 'promoting' them to entity-revisions?
And if so, that raises a number of UX questions:
So thinking out loud, perhaps the frontend needs to keep track of the list of unsaved entities that a publish operation will actually publish? A list controller in Drupal can expose the list of unsaved entities so that this can be syncronized with the frontend. This would likely need to support pagination for the case of a large number of items. This list could deal with the reality that some pages won't have an entity ID until they are published - so the autosave controller will need to deal in UUIDs or similar. This list could be used by the editor to navigate between pages being edited. It would also allow for validation to provide meaningful actions for the user. e.g. they attempt to publish 7 pages, but pages 2 and 3 are invalid. Being able to present the list of unsaved pages to the user will allow them to navigate to the invalid ones. We could also possibly show invalid components in the tree outline in the editor. This also probably means that the controller endpoint for fetching the current layout needs to work with the auto-saved ID rather than entity ID.
In terms of addressing the 'large number of entities' I think if the publish controller is configured to return a 307 while each group is processed that should just work with the
fetchAPI. But that puts us into a state where several entities could save and one could fail. Probably post 0.2 to consider once we have workspaces support.So tl;dr I think we likely need a list controller that returns the current 'auto-save'/in-progress content - i.e. the things that publishing will publish. This will allow nice UI affordances like `Publish (3)` and also a UI to navigate between pages in progress from with XB.
Comment #8
tedbowI talked to @larowlan a bit about this and said I would write my thoughts up.
First thanks to @larowlan @bnjmnm and @wimleers for the reviews
But I no longer think we should do this issue as is, here is why
All the saves will happen when the user clicks "Publish now" to save all the entities that have auto-save states (at least the valid ones)
ApiContentUpdateControllerthe above was not clear (at least to me) or decided. Therefore it was reasonable to think the user would be able to save an individual entity directly from client data andApiContentUpdateControllermade senseApiContentUpdateControllerno longer makes sense. For the endstate of 0.2 there will never be a request from the client with the XB field and other entity fields which should then directly result in a real entity save.saveEntityFromAutoSave()to be triggered for each auto-save state when the user hits the "Publish all button"saveEntityFromAutoSave()on many may not be something that can be done in a single request, but because of the following point I don't think we should worry about that.saveEntityFromAutoSave()will happen before the user clicks the "Publish all" button. It may be that all the "Publish all" button will do at that point will be move real entities to the live site in the regular Workspace way (though probably with our UI)Because of this the problem calling
saveEntityFromAutoSave()many times in 1 request is likely only a problem for 0.2 or at least pre-XB-WorkspacesAt the very least we don't know this will be a problem once we start using Workspaces.
Since 0.2 is truley a preview I think it would be reasonable to put some limit on the number entity changes we support in XB at once. Because otherwise we likely will be solving performance problems that might go away when using Workspaces.
All this makes me think more work on
ApiContentUpdateControlleris just churn and what should be doing.<
In this we would create a service that would do something like
saveEntityFromAutoSave(). Hopefully we could start that from what we have now inApiContentUpdateControlleras the data this controller gets from the request is exactly the same as what we currently save in the auto-save state. #3489743 will add more metadata about the autosave but it should still retain the data the auto-save has now.Of coures all this would delay the ability to actually save the XB data to the DB and therefore be able to view a node(an eventually a Page entity) with the components that were added in the XB UI. I understand that even if we eventually remove it having a "Publish" button on the XB UI that saves an individual node makes XB seem much more real. It also would let people demoing XB to create multiple different nodes that would show what you could make with XB.
Therefore while we are doing the above I think we should make an issue for Create a temporary Publish button to save an individual node via ApiContentUpdateController. Hopefully that issue could just use
ApiContentUpdateControllerand not worry about the metafields as they are not being sent from the client right now anyways. This would be stop-gap until we do the steps above to convert auto-saves to real entity saves.Comment #9
tedbowAssigning to @wim leers because I think my proposal in #8 needs review. Then I would make issues accordingly if he agrees
Comment #10
tedbow#3478565: Add Entity Update controller got committed 🎉
so my suggestion above
has already been done
Comment #11
wim leers@larowlan in #7
But if all those auto-saved-to-tempstore items exist only in tempstore, how can you preview them together? I guess that's your point: you won't "in the first instance" — i.e. that's for a later phase/release?
The first 2 UX questions you raise are exactly what not saving to tempstore but to a workspace would address.
The third UX question is a really good point! That requires clarification from Product Manager @lauriii and the design team, because not only is that a performance/scalability matter, it's also a UX concern: what if >100 changes are accumulated prior to publishing them all? 😨
@larowlan on the MR: in almost every of your comments on the MR you refer to the inability of this controller to update the
Patternconfig entities. But that's not relevant here. The controller being updated exists specifically for updating content entities that have an XB field. ThePatternconfig entity type already has full CRUD support via an HTTP API, see\XbConfigEntityHttpApiTest::testPattern()😊Comment #12
tedbowCreated #3489994: Create an endpoint to publish all auto-saved entities that I believe we should do before this issue so don't have refactor the controller again. See my proposal in #3489994: Create an endpoint to publish all auto-saved entities
Comment #13
wim leers@tedbow in #8
0.2too, but it will need to be supported later, right?So I'm not at all following how we conclude #3489743: Create AutoSave service and HTTP API to retrieve all entities with pending changes is the correct next sttep.
Comment #14
wim leers#8.3/#13.3
@tedbow just talked me through this. I now understand it! The future button will publish an entire workspace, meaning 1, 2 or possibly 100 content entities (and perhaps even config entities) all at once, with a single click of a button. Given that, a single request to publish everything auto-saved into tempstore is the closest current equivalent. So +1.
Per @tedbow and @larowlan, this should hence be postponed on #3489994: Create an endpoint to publish all auto-saved entities, which will refactor the logic that #3478565: Add Entity Update controller introduced into an explicit
ClientToServerConverterservice.After that is done, this issue will then update that
ClientToServerConverterservice to also support (metadata) fields.Comment #15
wim leersNeeds input from @lauriii and/or design.
Blocking this issue on those clarifications, but it really should be spun off into its own issue.
Comment #16
lauriiiThis is demonstrated by this user flow: https://www.figma.com/design/Ps3m4APGHIILsBrm0Uj31N/Experience-Builder?n.... Let me know if you want me to record a short video to walk through that since this is one of the more complex flows and it might be helpful.
We can accept the scalability problems for this initial state. We'd probably publish at maximum 10-15 entities with the intermediate solution. We could probably accept even if it was just 5-10 but then we'd need to document it somewhere.
Do these scalability concerns exist with the Workspace based solution too? From design perspective, it seems that the current solution is good enough for now. It would probably be worth opening a spinoff issue to define in a bit more detail how we'd expect the panel to behave in this scenario.
Comment #17
larowlanThanks, personally in my head there's an end-state where that should be one and the same and abstracted behind routing/storage layers - much like how LB has little distinction between writing to config entities (defaults), content entities (overrides) or simple config (navigation module). But that's for the future.
In my opinion the issue still exists for Workspaces - it makes use of
set_time_limitto continue to increase the execution time on a workspace publish event. This might work for low-end setups but for most HA setups the CDN won't hang around waiting for a response from the backend that long. E.g. Cloudfront, Cloudflare all have time-out limits after which they return a 504. So we will likely need to resolve that too if this is going to be for enterprise setups. I created a small POC yesterday to check if thefetchAPI respects a 307 header and it indeed does. So there is scope for us to craft a controller for the publish that processes chunks (e.g. we could use the existing settings.php flag for entity_update_batch_size) and returns a 307 with a Location header and JSON body that contains the progress. AKA an API based batch API. In my testing, I created a controller that responded with 4 307s and then a 201, and the FE did indeed make the subsequent requests to the Location header until the 200 was returned. So I think there's a world with nice UX where we put the operations in a queue and process them.I don't have access to that design @lauriii - so yes please to a short video
Comment #18
wim leersLB writes to config entities and simple config?! 🤯
Where? (Do you have a URL? 🙏)
How? (Zero config was validatable at the time Layout Builder shipped!)
Woah! 😱 Indeed:
\Drupal\workspaces\WorkspacePublisher::publish()uses that…🤩👏
😬 And no response yet ~24 hours later, escalated internally. Sorry about this 😞
Comment #19
lauriii@larowlan Here's the correct Figma link: https://www.figma.com/design/Ps3m4APGHIILsBrm0Uj31N/Experience-Builder?n.... Updated the original link too.
Comment #20
effulgentsia commentedFor 0.2.0, you won't be able to preview them outside of XB. However, you'll be able to preview them inside of XB. For example, if there's auto-saved changes to both the Page content entity (#3482259: Landing page integration: new content entity type for unstructured content) that you're currently viewing in XB, and the PageTemplate config entity, and one or more JS component config entities (once #3483267: [exploratory] PoC of Preact+Tailwind components editable via CodeMirror is done), then the XB canvas will reflect all of that. There's also been some ideation (not sure if a d.o. issue has been opened yet for the implementation) for a button in XB to remove most of XB's UI and just show its canvas full-screen to make it even closer of a "show me what this will look like on the real site" preview.
Comment #21
larowlanThanks for access @lauriii
Comment #22
larowlan@lauriii thanks, it looks like the design matches my mental model of this and your refinement questions match mine in #7
I think it would be good if in the list of 'unpublished changes' there was some sort of icon to indicate a page had an error, and if these could be links to lead the user to loading that page in the editor. Or even better if the errors could link to both the page and open the component in the side panel
Comment #23
lauriiiThe designers suggested that the errors should have a have a link to the page where the error appears, and it should indeed focus the error that the user clicked. I think it's a good point that it might be beneficial to additionally highlight the pages that have errors in the list of pages. 👍
Comment #24
larowlan@lauriii one thing the design is missing is translations, it is feasible that there will be multiple translations of a page in the auto-save store, so we might need to convey that in the review changes list somehow. Or should the list only return items in the current language?
Comment #25
lauriiiWe should display translations there but we need to determine how, because the design doesn't account for how translations are displayed in there (yet). I can think of at least two ways to display this:
Comment #26
tedbowcomitted #3489994: Create an endpoint to publish all auto-saved entities
Comment #27
effulgentsia commentedComment #28
tedbowNot currently working on this
Comment #29
tedbowworking again on this
Comment #30
tedbowNot a blocker but I think we should also do this #3493889: Change ApiContentUpdateForDemoController to save from auto-save instead of request data. who knows how long the
ApiContentUpdateForDemoControllerwill actually be around and it should be doing the save in the same way general way, ApiPublishAllController is, including for metadata fields. That issue is very small changeComment #32
tedbowIt would be great if we could get a couple issues in first that would make this issue smaller.
ApiContentUpdateForDemoControllerTestwould not have to be updated to change it's call to\Drupal\experience_builder\ClientDataToEntityConverter::convertto deal with metadata and the client would not have send the meta fields.Comment #33
tedbowComment #35
tedbowNot completely done but probably could use other eyes on to make this is going the right direction.
Assigning to @wim leers but @larowlan if you happen to review this first and have feedback I can address feel free to assign it back to me and set to Needs work. Obviously also welcome to work on your self.
Comment #36
larowlanThis is ready for another pass from Wim
Comment #37
tedbowAdded more ideas to #3494915: Support entity-level + field-level access checking in auto-save — i.e. in `experience_builder.api.api.(layout.post|auto-save.post)` while I was thinking about it. There is todo in the code for this
Comment #38
wim leersThe issue summary (and title) still (see #14) don't convey the scope. A screenshot/screencast might help, but AFAICT this is not modify the UI, only under-the-hood things?
Shouldn't this have Cypress E2E tests? If not, or while awaiting those, how should I manually test this?
Detailed review + manual test screencast in https://git.drupalcode.org/project/experience_builder/-/merge_requests/4...
Comment #39
tedbowComment #40
tedbow@wim leers thanks for the feedback and @larowlan thanks for addressing those problems,.
@wim leers see the update summary for the issue scope
Comment #41
tedbowTest are failing because of #3494525: Since twig/twig 3.12: Twig Filter "spaceless" is deprecated which has a fix awaiting approval
Comment #42
wim leersPer the issue summary, this is clearly in the critical path for many things. ⇒ Bumped to priority + tagged .
Comment #44
wim leers