Problem/Motivation

Diff now provides a revision tab. But it is very hard to display feasible data to represent a revision, specifically to understand what the difference between revisions is.

Nodes have a revision comment field to allow editors to enter a comment. In most cases though, revision comments are empty and forcing a user to enter a revision comment leads to bad usability with quick edits...

The original idea came up when discussing
#2462053: Provide a container revision summary

8.2 core introduced an interface to deal with revisions: RevisionLogInterface
Node implements it and sure returns empty strings if no log message is stored.
Still the Revision tab can not be altered easily but we override it anyway.

Proposed resolution

Add the fallback logic into our override tab controller and check for RevisionLogInterface and if inexisting or not available, fall back to some generated summary. I think that helps most for fast iteration and doesn't add new assumptions or APIs to maintain.
We can then bring in our extensibility requirements to Entity module or Core when we are well progressed.

Entity has RevisionOverviewController with getRevisionDescription for a revision.
For the specific case of Collect, Diff won't be able to offer a reasonable default.
We will create a follow-up to switch away from the own Controller for revisionOverview to the generic Entity revision tab and then implement the interface to offer a generated summary.

A revision summary could be to loop over all fields and compare each of the values and mention which field changed.
That however would require the summary of each revision to load the previous revision too.
But it seems that will always be needed for a generated change summary.

Remaining tasks

Decide about approach, implement.

User interface changes

API changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Berdir’s picture

Not just the summary, everything that could possibly be different based on the entity type could be moved there, with default implementations when possible.. routes, local tasks, revision date, author, getting a list of revisions...

lhangea’s picture

Just to make sure I understand correctly: you are basically saying that we need to display some sort of a revision summary in the revision overview table and as an example, with that we should be able to override the revision log message which is displayed by default for node revisions.

mbovan’s picture

miro_dietiker’s picture

@lhangea the revision comment field only exists on node revisions. All other entities don't have a similar comment. As a result, the revision tab could only display the revision id and date. Also the revision author is optional data.

You can see an example revision tab in collect module: #2446011: Show container revisions
revision tab from collect module
To show more than just the date, the image outputs the content with a cutoff at 255 chars. This is still not satisfying. The general revisions tab needs to provide extensibility for a nice revision summary.

We want to decouple the general revisions tab from a pluggable revision handler. As berdir pointed out, the summary is just one thing.
This would be a healthy pattern to adopt by core once in future.

lhangea’s picture

Ok, got it, thanks

miro_dietiker’s picture

Component: Code » User interface
Status: Postponed » Active

Unpostponing. The generic controller has landed.
Revision summary still easily sucks.

miro_dietiker’s picture

IMHO if the revision summary is not provided by the user, the system could add a revision change / summary statement. Not just if the revision comment field is technically not existing.

johnchque’s picture

Assigned: Unassigned » johnchque
miro_dietiker’s picture

Soo... I just checked the issue queue.
Most issues are cleanups and small bugs. After the most recent bugfix.

This is the only remaining major feature we are eager to see.
It's not a release blocker though. We are also fine to add it on a 1.1+ release as it's also not introducing any incompatibility.

In any case, a diff release is near. :-)

phenaproxima’s picture

Now that Diff has Views integration, is there any real reason not to provide a view to replace the core /node/%/revisions route? This would grant the user a lot of power and configurability, and be easy to implement. We've already done it in a feature branch of Lightning, and I'd be happy to extract that as a patch if it would be helpful.

If we went with that approach, we could provide a Views field handler that would display the revision comment if one was available, or otherwise summarize it if not.

Berdir’s picture

Not sure if it the diff integration is not specific or not, but even if it is generic, it would have to generate views dynamically for other entity types.

We could do it just for node as a start but we should probably keep the code and routes anyway, as it can be tricky to have code rely on views-provided routes, in case that view is deleted or changed.

phenaproxima’s picture

That's a good point. I'm thinking about the out-of-the-box use case -- Diff already specifically provides a route subscriber whose only purpose is to override core's node revision controller. It seems to be pretty un-opinionated (again, OOTB) about other versionable entities. I merely propose taking that node-specific functionality and making it a view. I agree about not having code depend on it -- what are you specifically thinking of?

miro_dietiker’s picture

This whole discussion about the view is more related to #2452523: [Meta] Offer a revisions tab for all entities
In code there is a reference to the core issue that wants to convert the revision tab to a view... You might want to create a separate issue about this subject?

miro_dietiker’s picture

Title: Introduce a revision/diff handler » Introduce a revision handler

So after the progress, discussions and the direction about diff pluggability, i think it's clear:
This handler will provide revision (summary) information and not really anything diff specific.

johnchque’s picture

Status: Active » Needs review
FileSize
2.74 KB

Early patch uploaded. Not really sure if this is the right approach.

miro_dietiker’s picture

Status: Needs review » Needs work

Yeah a valid first step.

Checkout Node.php. It has a list_builder in the annotation or 'translation' for a TranslationHandler.

What we want is to introduce a new key here for the revision handler.
We could alter entity definitions and bring the key in for all entities.
Or we could fall back to our code when no specific one is declared. I guess that's more minimalistic.

johnchque’s picture

Status: Needs work » Needs review
FileSize
3.52 KB
727 bytes

Made some small changes, this should allow to add our handler.

miro_dietiker’s picture

Status: Needs review » Needs work
  1. +++ b/src/EntityRevision.php
    @@ -0,0 +1,76 @@
    +      $revision_summary = Xss::filter($revision->getRevisionLogMessage());
    ...
    +      // @todo Implement a method to get a revision summary when is none.
    

    Also do the fallback if the RevisionLogMessage is empty.

  2. +++ b/src/EntityRevisionInterface.php
    @@ -0,0 +1,22 @@
    + * Defines an interface to build entity listings.
    

    Almost. ;-)

  3. +++ b/src/EntityRevisionInterface.php
    @@ -0,0 +1,22 @@
    +interface EntityRevisionInterface {
    ...
    +  public function summary(EntityInterface $revision);
    

    I think our name doesn't differentiate yet and can be easily confused with RevisionableInterface. Interestingly this skips "Entity" completely.
    Are we only handling the summary or are we going to add other revision related methods too?

miro_dietiker’s picture

Title: Introduce a revision handler » Check RevisionLogInterface for summary and provide
Issue tags: +Needs issue summary update

We had some general discussions and change directions.

It turns out that 8.2 core introduced an interface to deal with revisions: RevisionLogInterface
Node implements it and sure returns empty strings if no log message is stored.
Still the Revision tab can not be altered easily but we override it anyway.

We only need pluggability if we stop overriding and the revision tab is provided by Entity.
To stay simple, let's stay within our straight scope:

Add the fallback logic into our override tab controller and check for RevisionLogInterface and if inexisting or not available, fall back to some generated summary. I think that helps most for fast iteration and doesn't add new assumptions or APIs to maintain.
We can then bring in our extensibility requirements to Entity module or Core when we are well progressed.

Entity has RevisionOverviewController with getRevisionDescription for a revision.
For the specific case of Collect, Diff won't be able to offer a reasonable default.
We will create a follow-up to switch away from the own Controller for revisionOverview to the generic Entity revision tab and then implement the interface to offer a generated summary.

johnchque’s picture

Status: Needs work » Needs review
FileSize
1.26 KB

Then we should add it in the current PluginRevisionController? Or create a new controller for it?

Berdir’s picture

Status: Needs review » Needs work

Yeah, and also use there :)

miro_dietiker’s picture

A revision summary could be to loop over all fields and compare each of the values and mention which field changed.
That however would require the summary of each revision to load the previous revision too.
But it seems that will always be needed for a generated change summary.

johnchque’s picture

Status: Needs work » Needs review
FileSize
2.44 KB
21.72 KB

Did some code to load the previous revision and get the fields that have changed. For now it lists the fields that have changed.
Looking like this now. Not sure yet where to use it in the current controller.

miro_dietiker’s picture

Status: Needs review » Needs work
+++ b/src/Controller/PluginRevisionController.php
@@ -277,4 +285,37 @@ class PluginRevisionController extends ControllerBase {
+    $revisions = $this->getRevisionIds($storage, $revision->id());

getRevisionIds is node specific. Also some things are this line seem wild. ;-)

Berdir proposes to use an entity query.

This method should get the current and the previews revision, or at least call another method that gets both params.

johnchque’s picture

getRevisionIds doesn't seem to be Node specific, its a function present in the controller.

Berdir’s picture

Sorry my fault. Still, that would mean you need to get all the revision ids again for every revision, that's extremely inefficient.

You already have a list of revision ids, work for example with an index based on the current revision and access the next/previous in your list or invert the order and always start with the first revision, then you can easily store the previous revision ID in a variable. Then pass that to your method as an additional argument.

johnchque’s picture

Shouldn't be easier to use the left revision and right revision that are used in the controller itself?

Berdir’s picture

Yes if we have that already, sure, use those.

johnchque’s picture

Status: Needs work » Needs review
FileSize
2.66 KB
3.4 KB

Great, then would this make sense? Not really sure where to place that summary.

Berdir’s picture

I think I see why you are confused now :)

This is about the revision list, not the diff. Which is not generic yet I think but node specific.

Status: Needs review » Needs work

The last submitted patch, 29: check-2462731-29.patch, failed testing.

johnchque’s picture

Ohhhhh I see, so I have to place the summary in the revisionOverview right? OK, will try :) Thank you! So instead of using the method in the controller itself I have to use it in the revision form, I see!

johnchque’s picture

Status: Needs work » Needs review
FileSize
6.34 KB
40.95 KB

Moved the functions to a service. Working nicely. Still need to get rid of those fields that are not useful for the user.

Status: Needs review » Needs work

The last submitted patch, 33: check-2462731-33.patch, failed testing.

miro_dietiker’s picture

Nice. I think we should make it more compact and implode by "," so it fits better the text format of a revision log.

As an idea to better indicate change:
Git displays on the terminal repeatingly + or - varying the amount of changes. Or sometimes % change. And a summary on a merge.

Example:

core/lib/Drupal/Core/Session/SessionManager.php           |  3 ++-
 .../src/Controller/TestPageTestController.php             |  3 ++-
 core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php | 15 +++++++++++++++
 core/tests/Drupal/Tests/BrowserTestBase.php               |  8 ++++++--
 4 files changed, 25 insertions(+), 4 deletions(-)

So the challenge would be that we need to trigger the diff engine to know how many characters / words / lines have been removed and added.
Title +3/-15, Body +1k/-3.1k

Or even more simplified, we could output the change in length?

Title -34%, Body -15%
Title -12, Body -2.1k
Title 28>16, Body 14k>11.9k

But i guess those are lots of ideas for follow-ups... But not yet sure what we need to boost UX.

johnchque’s picture

Status: Needs work » Needs review
FileSize
6.18 KB
3.8 KB

For now imploding with ",". Looks nice.

Status: Needs review » Needs work

The last submitted patch, 36: check-2462731-36.patch, failed testing.

miro_dietiker’s picture

Ha, funny, the first field i get listed as changed is "Revision ID", so we should definitively only list fields that are subject for diff like we filter the fields in our field settings UI.

As much as at a first glance this looks nice:
"Revision ID, Changed, Revision timestamp, Revision log message, Body"
Only the "Body" part of it is real information. The other ones are all garbage.

BTW how are we determining field sequence?

I'm not 100% sure if we can only list the fields or if we need to prepend the message with "Changed: " or "Changes on ".

johnchque’s picture

Status: Needs work » Needs review
FileSize
8.46 KB
3.21 KB
63.38 KB

Actually this changes filter those useless lines, for now it checks if there is a plugin for that field, in that case it is added to the summary.

Status: Needs review » Needs work

The last submitted patch, 39: check-2462731-39.patch, failed testing.

johnchque’s picture

Status: Needs work » Needs review
FileSize
8.46 KB
1005 bytes

hmmm this should fix the tests.

miro_dietiker’s picture

Status: Needs review » Needs work

Great stuff.

Hey tests, where are you hiding? :-)

johnchque’s picture

Status: Needs work » Needs review
FileSize
9.52 KB
2.32 KB

When no changes. Added "No changes." summary, tests added.

miro_dietiker’s picture

Status: Needs review » Needs work
  1. +++ b/src/DiffEntityComparison.php
    @@ -292,16 +292,19 @@
    +        $summary .= $summary != '' ? ', ' : 'Changes on: ';
    

    I don't like these crypto lines.
    i'd just build an array of items, after the loop check for count() and implode conditionally with prepending the text with else "No changes".

  2. +++ b/src/Form/RevisionOverviewForm.php
    @@ -205,7 +205,7 @@
    -          $key - 1 > 0 ? $old_key = $key - 1 : $old_key = 0;
    +          isset($vids[$key + 1]) ? $old_key = $key + 1 : $old_key = $key;
    

    Any what is this crapto line all about?

johnchque’s picture

Status: Needs work » Needs review
FileSize
9.3 KB
3.64 KB

This is more readable. :)

Status: Needs review » Needs work

The last submitted patch, 45: check-2462731-45.patch, failed testing.

johnchque’s picture

Status: Needs work » Needs review
FileSize
9.38 KB
1.06 KB

Added a check if the element in array is set.

miro_dietiker’s picture

Issue summary: View changes
Status: Needs review » Fixed
Issue tags: -Needs issue summary update +Usability

Ha! Funny how simple this code can be!
Plus really awesome to have this in now!

Committed and updated summary a bit from comments.

miro_dietiker’s picture

Created Follow-up.

Status: Fixed » Closed (fixed)

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