Closed chillu closed 6 years ago
@stecman @bigSiebs @adrexia @andrewvt @micschk @lukasfrey @jebates @tylerkidd @stevie-mayhew @bendubuisson You have all been involved in GridField/Versioned-related modules - I'd like to get your feedback on what you see as the most useful actions for SilverStripe leading up to the 4.x release. We've made the Versioned system more solid through ChangeSets
("campaigns"), but versioning your DataObjects is still opt-in for 4.x. Assuming that authors generally prefer the draft/publish flow to extend to all managed objects, how can we make it easier for devs to opt-in to this model?
I'm not sure what the possible scope of changes is at this point, but for what it's worth, the VersionedDataObject
extension in heyday/silverstripe-versioned-dataobjects (originally written by @camspiers) only exists to make the Versioned
extension from the framework useable, since it's not out of the box. It seems like Versioned
was written very specifically to provide versioning for SiteTree
, rather than a generic versioning solution. I haven't dug through all of the SS4 code, but it looks like this hasn't really changed.
What I'd really like to see is the framework provide the glue that VersionedDataObject
does currently; where you can add the extension to a DataObject
and it just works. Basic out-of-the-box UI support for the versioning provided by the framework is great, but Versioned
probably needs to be easier to drop in (and well advertised) for the UI side of it to really be useful.
@ingo have you seen https://github.com/silverstripe/silverstripe-versioned/blob/master/src/VersionedGridFieldItemRequest.php this was built based on the versioned data objects module, as well as some inspiration from @unclecheese's better buttons.
- Publish/unpublish buttons on detail form
- Cascading publish by "owner" (e.g. a page record with has_many relationships)
- Mark "owner" record as modified if it contains unpublished dependencies (SS4 only?)
Potentially covered by these, but nested versioning is a bit of a nightmare UX-wise in the CMS. It might be worth providing an option to slave versioning in a way that's invisible to the user.
In the past I've replaced the publish/save draft actions in related versioned records with a "save" action that saves as draft. When the parent/owner is published, all of these related records are implicitly published. As far as the CMS user is aware, it's all one thing - avoiding the "pages are versioned but these things inside it are published immediately" conversation.
I agree with @stecman here. The main work which I've done involving versioning has been publish chains for related objects and implementing custom actions for gridfields like the ones that @tractorcow linked above.
We've also attempted to have Versioned parents having versioned children objects, and then to be able to "publish" the children which allows the parent to be informed of that change while allowing the parent to "publish" all of its children on a change. It has been quite complicated in some instances to achieve this, which is a problem I believe campaigns will solve for us.
Our module https://github.com/bluehousegroup/silverstripe-data-object-version-viewer has https://github.com/heyday/silverstripe-versioneddataobjects as a dependency for exactly the reason @stecman mentions above (versioned dataobjects doesn't work out of the box without it). Before we discovered the silverstripe-versioneddataobjects module we were neck deep in handling stages correctly ourselves which was a handful and produced spooky behavior left as is. I strongly recommend the functionality in silverstripe-versioneddataobjects be put right in the framework so that when versioning is turned on it works reasonably out of the box.
We've found that while versioning is required from a tech point of view for content blocks and similar, most clients don't actually think about sub items versions at all, so similar to @stevie-mayhew above, we've created nested publishing flows and remove versioning UI from subobjects as it's just too complex for a client to get their head around.
SS4 versioning Data extension looks a lot better then v3 and the changes seem to point to versioned data objects module surplus to requirements thanks to that work.
@pitchandtone without any special GridFields, the default behaviour in the SS4 CMS is that stage content is shown, and that as long as you set up $owns
metadata, clicking publish on the page will also publish all subordinate versioned dataobjects. If I understand your user needs correctly, this should give you what you want?
If you add the page to a campaign, then all of the subordinate dataobjects that are due to be published will be listed out in the campaign, to give more detail.
@chillu it looks like the only thing in heyday/versioneddataobjects missing from silverstripe/versioned is the VersionedModelAdmin, which seems like a bit of an orphan.
@sminnee yeah that sounds good. We do have a couple of clients that require sub-object publishing, but that's the exception to the rule.
Publish/unpublish buttons on detail form Cascading publish by "owner" (e.g. a page record with has_many relationships) Inclusion of draft records in ModelAdmin search
I've confirmed that these is already in place for SS4
Publish draft/publish/modified status indication on GridField table view Publish draft/publish/modified status indication on ModelAdmin search results
Created https://github.com/silverstripe/silverstripe-versioned/issues/22 for this.
Which other items from my list in the ticket description are important? Any crucial bits missing from SS4?
we've created nested publishing flows and remove versioning UI from subobjects as it's just too complex for a client to get their head around. [...] We do have a couple of clients that require sub-object publishing, but that's the exception to the rule.
How did you deal with this in terms of unpublishing sub-objects? While cascading publish is pretty straightforward, I could see cases where an author wants to move a sub-object back to draft (take it off the published page) without unpublishing the page itself. So in this case, you wouldn't want a "publish" button on the sub-object, but still need an "unpublish" button. Or do you just use "delete/archive"? Related issue: https://github.com/silverstripe/silverstripe-versioned/issues/21
@chillu the issue there is often that you will need to "remove" the object back to a draft version, and often then have publish the parent object so that this cascades through - especially if you have some form of caching involved.
I'm not sure of the best way to provide an "out of box" functionality for this, as its more configuration than convention in that case.
How did you deal with this in terms of unpublishing sub-objects? While cascading publish is pretty straightforward, I could see cases where an author wants to move a sub-object back to draft (take it off the published page) without unpublishing the page itself.
I wouldn't take it as self-evident that users will need this, or at least not in all cases.
For some applications, sure, in which case you could have an option to provide publication controls on every atomic record. But I wouldn't assume that every piece of nested data needs these.
For other cases, it would probably be overkill. In these cases I would expect that deleting the record and then publishing the parent page would be a reasonably natural way of handling this.
By way of analogy, you wouldn't want separate publication controls on the Content and Title fields of a page, and some nested data types are going to be as lightweight as those in the user's mental model.
So if we were to have per-sub-record controls for publish/unpublish, I would make it an optional add-on. This might require better extension points in the GridFieldDetailForm (which would also allow for a refactor of betterbuttons)
One of the assumptions we have struggled with in v3 (unsure if this has been split more in v4 yet) is the assumption that publishing and unpublishing permissions are the same.
While it's an OK default, it should be configurable as in there should be two different functions canUnpublish and canPublish with extends calls so that you can handle this discreetly. For example, workflow module disables publishing when in place, which also means people can't unpublish things without hacking around stuff.
Agree with both @chillu and @sminnee re unpublishing items, most of the time we've seen that people still think about the page and are fine with delete object, re-publish page, but there are some clients who are saavy enough to need unpublishing per sub object, however as @stevie-mayhew points out, it opens up the question of what else is affected when this happens.
I think it's safe enough to have delete object, unpublish page as the default, but I'd be keen to see what comes back from @chillu's user testing on that front.
OK so related to this, I've been playing with $owns tonight, so far it seems like the recursive publish works well. So I was about to delete some extensions that we had added to elemental to control unpublishing and duplication, as I assumed $owns would be used recursively in both of those methods but that doesn't seem to be the case.
Is this by design (shall I leave my extensions in but try use $owns) or an oversight?
Next question is if $owns is used by duplicate, should the variable be moved to DataObject instead rather than Versioned and then utilised by Versioned?
And on delete
Hrm, these sound like oversights although in the case of many-many and has-one "owned" data it's not clear what the logic should be. Can you raise a ticket?
So if we were to have per-sub-record controls for publish/unpublish, I would make it an optional add-on. This might require better extension points in the GridFieldDetailForm
That's what https://github.com/silverstripe/silverstripe-versioned/issues/21 is about. Can we determine "sub-records" though? It's context specific. You might want to auto-publish a "staff member" when associated to a page, but also provide a separate "staff members" ModelAdmin where they can be individually published. At the moment Versioned->findOwners()
will only determine owners for the current record, not for the type of record. And GridFieldDetailForm
doesn't have a reference to the parent object to check if it owns the currently edited record. We could opt-in to individual publish in ModelAdmin
, but leave it disabled in GridField's default config?
Hrm, these sound like oversights although in the case of many-many and has-one "owned" data it's not clear what the logic should be. Can you raise a ticket?
I've created https://github.com/silverstripe/silverstripe-versioned/issues/23
Also, I think a lot of the discussions around ownership are repeats from earlier discussions lead by Hamish/Damian during the RFC stage: I'm just getting a strong coffee and re-reading that RFC: https://github.com/silverstripe/silverstripe-framework/issues/4932
You might want to auto-publish a "staff member" when associated to a page, but also provide a separate "staff members" ModelAdmin where they can be individually published.
We could opt-in to individual publish in ModelAdmin, but leave it disabled in GridField's default config?
That makes sense to me. This could be an optional VersionedModelAdmin base class or extension that you apply to the applicable model admins? I don't think it needs to automagically appear if there's a simple way to enable it.
That makes sense to me. This could be an optional VersionedModelAdmin base class or extension that you apply to the applicable model admins? I don't think it needs to automagically appear if there's a simple way to enable it.
I've updated https://github.com/silverstripe/silverstripe-versioned/issues/21, and flipped it around: VersionedGridFieldEditForm
should be opt-in, not opt-out.
I've also just realised that "revert" doesn't work the way authors might expect, and we need to at least add a warning: https://github.com/silverstripe/silverstripe-versioned/issues/24. ChangeSet::revert()
is proposed, but not actually implemented. ChangeSet::unpublish()
isn't even proposed in the RFC, probably because of the ambiguity it creates around what's going to be unpublished?
A related comment on 4.2.2.15. DataObject::unpublish in the RFC
Note: the meaning of the term "unpublish" has been left consistent with the behaviour of "unpublish" action in 3.x. This feature could potentially be replaced with a new action, which is to "undo the last publish of this record", which would instead query the DB for live version of this record prior to the current, and revert it to that. This would require additional scoping.
Another goodie: Unpublish cascades to owners by default - https://github.com/silverstripe/silverstripe-versioned/issues/25
@chillu @sminnee @tractorcow any thoughts on duplicate and delete also utilising $owns?
Duplicate has "duplicate with children" and "duplicate self only" UX (at least for pages). It could use owns, but it's risky if some owned object's don't easily support duplication (e.g. files).
@tractorcow is this a similar distinction in that has_one are problematic?
I'd guess has_one Link or has_one Page would not normally be required either.
It's not so much the relationship as the datamodel. it's not always logically valid to assume an object can be duplicated, e.g. Member, File.
File fits in the has_one category and Member doesn't seem like something that would ever appear in an $owns array, particularly because it isn't Versioned (although I'm arguing that perhaps $owns should be wider than Versioned).
If $owns isn't relevant (and stays Versioned specific), I'll add an equivalant for duplicate and delete via an Extension.
For discussion on recursive unpublish please continue the conversation over at https://github.com/silverstripe/silverstripe-versioned/issues/23
In SS4, we've created the
ChangeSet
API (with the "campaigns" module) for the specific purpose of giving authors more control on the publication of content. Which relies on applyingVersioned
toDataObjects
in your project code.At the moment, we don't have any support in core to actually manage those versioned
DataObjects
efficiently. While this can be added through third party modules, it's not really a long-term solution if we're serious about making all types of content managed in SilverStripe "publishable".Since we're likely going to create a React/JSON-powered GridField in the 4.x release cycle, I'm a bit cautious expanding the GridField API surface in core significantly. So I'm wondering if we should improve the existing versioneddataobjects module instead of pulling this into core, and focus on better core extension points for now.
Potential features:
Existing modules:
versioneddataobjects
module, adds a "history" button in GridField and ModelAdminPotential core improvements:
GridField->extend('updateConfig', $config, $dataList)
so that third party modules can conditionally add or replace components without a developer needing to change every instance of GridField. Modules could choose to replaceGridFieldDetailForm
with a specialised versioned one if the managed$dataList
has theVersioned
extension.CMSPageHistoryController
into a reuseable UI component (React-based?) which can handle all versioned DataObjects, not just SiteTree records (and replace data-object-version-viewer)VersionedModelAdmin extends ModelAdmin
becomes unnecessary (a ModelAdmin should auto-detect if versioned records are managed, and set the correct GridField configuration)CMSPreviewable
PHP class interface in core to support the silverstripe-dataobjectpreview use case