Open LukePercy opened 12 years ago
Before we can come up with a solution we have to think about what the customer requirements are:
With that in mind, there's (at least) a couple of ways to approach this problem technically:
1) Right now any page in a workflow transitions from WorkflowAction to WorkflowAction via manual control. You could extend this to allow transition on a schedule. By setting a transition to only move to publish after a certain date you can implement embargo.
Implementing expiry this way is a bit trickier. Effectively expiry can be seen as an embargoed change set to happen on the expiry date, where the embargoed change is either "revert to old version" or "remove page from live". Or we can have pages maintain their workflow state indefinitely, so that a page version can move in and out of published state multiple times, and implement expiry as another transition
2) We could ignore advancedworkflow entirely. Treat embargo / expiry as an extension to Versioned instead, where instead of "publishing" from Page_versions to Page, publishing consists of updating a timeline which defines what version should be live on a particular date.
3) We could provide an underlying API that both modules hook into
An uncovered issue: how does the UI for this look? How do we clearly convey to administrators when changes will happen, and what they'll look like.
My personal feeling:
Deal with expiry has embargoed change to a newer version, not as a separate concept
Extend the Page_versions table to include a date to be published on. When that date passes, update live Page table to have that new version
Show in the tree the upcoming versions, like:
When using advancedworkflow, each pending version will have it's own workflow like:
Instead of having embargo / expiry dates, each action can be performed in the future, i.e.
Save & Publish Save & Publish On.... Delete from Live Delete from Live On...
Internally implemented by providing IsPublishedInterface which both expiry/embargo & advancedworkflow would hook into. Example implementation:
class ExpiryEmbargo implements IsPublishedInterface {
static $version_fields = array(
'LiveFrom' => 'Date',
'LiveTo' => 'Date'
);
function IsLive($version) {
return $version->LiveFrom <= time() && $version->LiveTo >= time();
}
function LiveDescription($version) {
return "from ".$version->LiveFrom;
}
}
Good points and I like your thinking. One additional layer of complexity is that the embargo and expiry dates would be requested then approved.
E.g. the button would effectively need to provide this label's
functionality:
"(I request that Hamish, my manager, will) Save & Publish On…."
in other words... "Request Publish for date…."
This therefore means that when the manager gets the response, the date has been pre-filled.
I'm pretty sure some of our customers (like one that flies planes) use the above concept already in SSv2 (that you can seek a date for adding or removing a page, for your manager to enact.)
Sig
On 14 November 2012 15:07, Hamish Friedlander notifications@github.comwrote:
Before we can come up with a solution we have to think about what the customer requirements are:
-
When you set an embargo, what does it mean? Does the existing page (if their is one) remain until the embargo passes, or does the page disappear / become unaccessible until the embargo date? Can you have more than one embargoed version? What happens if you need to fix a spelling mistake on the current live page when there's a newer version under embargo - do you have to discard your pending changes (which might be difficult to replicate later) in order to fix the current issue?
When you set an expiry, what happens once the expiry passes? Does it revert back to the previous version? Or to a newer version? Or just disappear / become unaccessible?
With that in mind, there's (at least) a couple of ways to approach this problem technically:
1) Right now any page in a workflow transitions from WorkflowAction to WorkflowAction via manual control. You could extend this to allow transition on a schedule. By setting a transition to only move to publish after a certain date you can implement embargo.
Implementing expiry this way is a bit trickier. Effectively expiry can be seen as an embargoed change set to happen on the expiry date, where the embargoed change is either "revert to old version" or "remove page from live". Or we can have pages maintain their workflow state indefinitely, so that a page version can move in and out of published state multiple times, and implement expiry as another transition
2) We could ignore advancedworkflow entirely. Treat embargo / expiry as an extension to Versioned instead, where instead of "publishing" from Page_versions to Page, publishing consists of updating a timeline which defines what version should be live on a particular date.
3) We could provide an underlying API that both modules hook into
An uncovered issue: how does the UI for this look? How do we clearly convey to administrators when changes will happen, and what they'll look like.
My personal feeling:
Deal with expiry has embargoed change to a newer version, not as a separate concept
Extend the Page_versions table to include a date to be published on. When that date passes, update live Page table to have that new version
Show in the tree the upcoming versions, like:
- Home
- About
- About (from 1 Feb)
- About Us (from 10 Feb)
- (from 20 Feb)
When using advancedworkflow, each pending version will have it's own workflow like:
- About
- About (from 1 Feb)
- About Us (from 10 Feb, currently pending approval by Editor)
- (from 20 Feb)
Instead of having embargo / expiry dates, each action can be performed in the future, i.e.
Save & Publish Save & Publish On.... Delete from Live Delete from Live On...
Internally implemented by providing IsPublishedInterface which both expiry/embargo & advancedworkflow would hook into. Example implementation:
class ExpiryEmbargo implements IsPublishedInterface { static $version_fields = array( 'LiveFrom' => 'Date', 'LiveTo' => 'Date' );
function IsLive($version) { return $version->LiveFrom <= time() && $version->LiveTo >= time(); }
function LiveDescription($version) { return "from ".$version->LiveFrom; }}
— Reply to this email directly or view it on GitHubhttps://github.com/silverstripe-australia/advancedworkflow/issues/47#issuecomment-10352477.
Sigurd Magnusson Business Relationship Manager SilverStripe
DDI: +64 4 978 7332 Mobile: (NZ): +64 21 42 12 08 Skype: sigurdmagnusson twitter.com/SigurdMagnusson www.silverstripe.com
I kind of like the idea of having an effective_from effective_to field pair on versioned; theoretically it shouldn't be tooo tough to make use of these for viewing Stage content.
One possibility is that as soon as someone sets a future publish/expire date, a 'future' version is created, and a 'now' version (ie what exists in the base table) is recreated from live content
How would changes to 'now' content be then merged into 'future' version (this is basically the same problem as the 'minor edit before the future publish date' problem you raised Hamish)? Would it be something that the user is notified about to make sure they're updating changes manually? What about fields that are set programmatically?
A couple of thoughts -
Probably not that many cases where there are multiple changes, except in the case where Expiry becomes a second Embargoed change, which is probably the easiest way to deal with the various things you might want to do after Expiry.
As far as creating "now" content, I was thinking the logic for finding the "current" live version would just be stepping back from the most recent version, checking for validity on each version until we find one that is - if expiry isn't set, a version would be valid from the embargo date onwards, and vice versa. If neither is set, it's valid forever. This does mean we'd need to handle deleting by explicitly adding a "deleted" record to the versions table though, since we couldn't just use presence in the live table to determine deletion.
Splitting Versioned would be fun but hard. Publishable is a simpler case of the more generic workflow / acts as state machine design. You could have each version travel through a state machine where some states are "published", and then the current live version is the most recent version that is in a "published" state.
Thinking about it, it'd have to travel through multiple state machines. One would be workflow (or just publish), one would be embargo / expiry. A version would only be live if all state machies were in published states.
Not pictured is how we trigger transitions from one state to the other on a schedule. It feels like an event system would be nicest, so you could also transition in all sorts of situations (when 1000 members have signed up, when a particular data object has a field set in a particular way).
Context, probably obvious, from a user perspective. Seems what you guys are talking about supports all of this, but I basically see two a half scenarios.
2.5: In the case you want to make the $999 TV deal turn into an insane $899 deal or return to the 'old price' of $1299 effective midnight Friday, I assume you would use embargo on an already published page.
On 14 November 2012 15:54, Hamish Friedlander notifications@github.comwrote:
Probably not that many cases where there are multiple changes, except in the case where Expiry becomes a second Embargoed change, which is probably the easiest way to deal with the various things you might want to do after Expiry.
As far as creating "now" content, I was thinking the logic for finding the "current" live version would just be stepping back from the most recent version, checking for validity on each version until we find one that is - if expiry isn't set, a version would be valid from the embargo date onwards, and vice versa. If neither is set, it's valid forever. This does mean we'd need to handle deleting by explicitly adding a "deleted" record to the versions table though, since we couldn't just use presence in the live table to determine deletion.
Splitting Versioned would be fun but hard. Publishable is a simpler case of the more generic workflow / acts as state machine design. You could have each version travel through a state machine where some states are "published", and then the current live version is the most recent version that is in a "published" state.
— Reply to this email directly or view it on GitHubhttps://github.com/silverstripe-australia/advancedworkflow/issues/47#issuecomment-10353350.
Sigurd Magnusson Business Relationship Manager SilverStripe
DDI: +64 4 978 7332 Mobile: (NZ): +64 21 42 12 08 Skype: sigurdmagnusson twitter.com/SigurdMagnusson www.silverstripe.com
The solutions suggested thus far, appear to be good but are particularly difficult and time-consuming to implement (howsoever "proper" each solution appears to be).
Note the embargo/expiry functionality that comes out of the box (along with enabling the QueuedJobs module) takes care of a bog-standard implementation, but is admittedly rather crude in comparison with some of the suggestions above.
As far as Sig's requirements go, the ability to simply "enable" and "disable" the published state of a content-object based on future dates, will suffice given a few UI tweaks.
So my middle-ground proposals are as follows, and comprise augmenting the existing WorkflowEmbargoExpiryExtension functionality - apologies if I'm simply re-wording what has already been suggested and claiming as my own :-P
1).
The provision of a DropDownField on WorkflowEmbargoExpiryExtension comprising a list of all versions of the current content-object. When selected and saved, publishes that object at that version when the expiry date comes around. By default the page is simply un-published and any attempts to access it from the frontend result in a 404. (Current behaviour), or a more intelligent default might be to revert back to the previous version - either way, as long as the CMS admin user was availed in some way as to how their system is configured.
2).
As well as/instead of 1). (With a radio/checkbox selection) a TreeDropDownfield of SiteTree objects, that when selected and saved, results in front-end requests to the current content-object being redirected to the selected item, when the current content becomes un-published.
Thoughts welcome.
For UI improvements see: https://github.com/silverstripe-scienceninjas/advancedworkflow/tree/issues/47-B
Note that a pull-request has been accepted on this, but the ticket will remain open for now, to encourage further discussion on improvements to the current embargo/expiry implementation.
The Terraformers (SilverStripe Ltd. team in Auckland) have started to build a separate embargo/expiry module over at https://github.com/silverstripe-terraformers/embargo-expiry. Its initial requirements are pretty basic. The main aim is to separate it from advancedworkflow. As well as getting it working with SS4 and the new silverstripe/fluent
2.0 version. Ideally this would work with advancedworkflow, but it's not a goal for that particular client project, so would need community help to get over the line. I'll start filing some enhancement issues in the module to map the critical path of missing features, and provide a space for more focused discussions.
Yep - ideally we'd not have two separate modules providing the same functionality, so if embargo-expiry was able to replace the functionality of advancedworkflow I'd happily remove it.
Some Usability Adjustments:
Embargo: 'Select Publish date'
'Select Publish time'
Expiry: 'Select Un-publish date'
'Select Un-publish time'
Maintainer note: See framework commit 585a8bc "Removed 'showdropdown' option from TimeField, use custom libraries instead (Ingo Schommer)" This is why a dropdown no-longer shows for this workflow functionality.