Overview
As discussed in Slack at https://drupal.slack.com/archives/C072JMEPUS1/p1762326259781829, we need the ability to unpublish Canvas pages.
Canvas pages currently lack unpublish functionality that would allow hiding previously published pages from visitors. We cannot simply set status to 0 because draft pages are also unpublished, and we need to distinguish between these states in the UI.
We use the term "unpublish" because it matches existing Drupal concepts (content moderation, entity status). This approach keeps the implementation simple and won't complicate the planned Workspaces integration (Q2 2026).
Proposed resolution
Implement an "Unpublish" action for Canvas pages with a simple approach:
- Allow users to unpublish previously published pages
- Unpublishing is staged through auto-save and committed with "Review changes" workflow
- Homepage cannot be unpublished
- No Views integration needed (keeps implementation simple)
- Works alongside the Trash module for complementary workflows
How it works (Technical)
- New PATCH endpoint updates the auto-save entry's published status
- When user clicks "Unpublish", it updates the auto-save entry (not the live entity)
- When "Review changes" is published, the unpublish action takes effect on the live entity
- Uses existing
entityIsConsideredNewlogic to distinguish drafts from unpublished pages
Publication States
- Draft: Page is new (never published), identified by default "Untitled page" title
- Published: Page is live (status = 1)
- Unpublished: Page was published before but now has status = 0
User interface changes
Before:
- No ability to unpublish pages
After:
- "Unpublish page" link in Page Data dropdown for published pages
- "Publish page" link for unpublished (non-draft) pages
- "🕦Unpublish" indicator for pages pending unpublish action in Review changes
- "Unpublished" status badge for unpublished pages
- Homepage protection prevents unpublishing
Complementary solutions
Trash module: For soft-deleting pages (different from unpublishing)
- Trash = page is pending removal (recoverable deletion)
- Unpublish = page is hidden from visitors but remains fully editable
Canvas is compatible with the Trash module for sites using Drupal CMS.
Remaining questions
All previous questions have been resolved. MR 543 simplifies the approach by:
- Using the existing auto-save workflow instead of Views plugins
- Leveraging
entityIsConsideredNewto distinguish drafts from unpublished pages - Avoiding extra complexity that would complicate Workspaces integration
Previous questions (all resolved)
This issue assumes we need to distinguish between archived and unpublished pages. Currently they both have status = 0, only difference is draft pages have never been publishedResolved: Using entityIsConsideredNew logicDo we want to avoid adding extra field to store publication state?Resolved: No extra field neededWill Nodes edited directly in Canvas also need the 3 states?Resolved: Yes, handled by same approachWhen we use Workspaces will it be a hard dependency?Resolved: Not relevant to this simplified approachDo we need extra permissions for state changes?Resolved: No extra permissions needed
Follow-up issues
- #3573408: Prevent unpublishing a Canvas Page if it is the home page, This situation can't happend using the UI changes in this MR, but only using standard Drupal form, after staging an unpublish actions and before publishing the changes
- #3572851: Dynamically use current auto-save title instead of "untitled page" in views, delete dialog, and elsewhere in the UI This is an existing problem with the view at admin/content/pages. Also helps with Trash module integration
- #3572636: Trash module compatiblity: Can't publish restored page with existing auto-save Existing problem discovered in this issue
| Comment | File | Size | Author |
|---|---|---|---|
| #64 | Screenshot 2026-02-04 at 11.33.25 PM.png | 49.02 KB | tedbow |
| #55 | Archive*_screenshot.png | 99.77 KB | kunal.sachdev |
| #32 | changes-listing.png | 17.85 KB | tedbow |
| #32 | page-listing.png | 29.6 KB | tedbow |
| #19 | Workflow video.mov | 22.51 MB | vipin.mittal18 |
Issue fork canvas-3556265
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:
Comments
Comment #4
tedbowHaven't review but "Needs work" at least for test failures
Comment #5
vipin.mittal18To enable the archive feature, code fixes are needed, as the content cannot be published even it is archiving upon changes. Refer attached video
Comment #7
tedbowThere is no information about how this work or the expected behavior which should be in the issue summary.
Also it needs tests, there are existing test classes for the controllers on the phpunit side which would be good to start with.
Comment #8
narendrarWith current approach, ApiContentControllers::normalize() - The 'isNew' field indicates if this is a draft (never published) vs archived (previously published). Frontend uses this to show correct badge ("Draft" vs "Archived") and determine available operations.
Current MR addresses most of the feature for this issue, i.e:
admin/content/pagesis a view and it is not differentiating between draft/archived as both are unpublished state.I am not sure if we should continue with the current approach and modify the view to handle the draft/archived state, or if we should change our approach entirely—perhaps by adding a new
field_archivedto the page entity, or using existing 'published' field for archive status (2)—or handle it in another way. Open to suggestions.Comment #10
tedbow@narendrar thanks update in #8, thats very helpful!
I think this is key problem. @lauriii should confirm but it can't imagine it would acceptable to have admin/content/pages not be able to distinguish betwen draft and archived. It would be very confusing to when you unpublished a bunch of pages they would show at the top of this listing as "unpublished"
I considered these options to solve this problem
Options Considered for Draft vs Archived Differentiation
Problem: Views can't distinguish between draft (never published) and archived (previously published) pages since both have
status=0.Approaches Considered:
publication_stateField with Auto-Sync ✅ ChosenonChange()hook to keep field synchronized with publication statusWhy Chosen:
I pushed up new PR to explore the chosen https://git.drupalcode.org/project/canvas/-/merge_requests/409
It solves this only for Page but right now this all we support.
I think the problem with this approach is I think there is plan to some day use Workspaces. and we might not need this field any more.
Comment #12
tedbowHow pushed up another MR https://git.drupalcode.org/project/canvas/-/merge_requests/411
You may have guessed that AI made this summary, and made the code with me helping, but I think this good idea if we still plan on moving to Workspaces and don't want to add a field we will have to get rid of
We could also add the view integration in a follow-up. but good to know it is possible without too much code
Temporary Views-Only Publication State Field
This implementation adds Draft/Published/Archived differentiation in Views without storing data in the database. This approach was chosen
specifically because Canvas will eventually integrate with the Workspaces module, which provides its own content staging and workflow capabilities.
Why Not Store the Field?
Adding a stored
publication_statefield would create significant backward compatibility issues:publication_stateand Workspace states would be confusingHow This Works
The implementation uses SQL CASE expressions with EXISTS subqueries to compute state dynamically:
This provides full Views functionality (display, filter, sort) without touching the entity schema.
Removal Path
When Workspaces support is added:
No data migration, no stored field cleanup, no complex update hooks.
Files
src/Hook/CanvasViewsHooks.php- Registers field/filter/sort with Viewssrc/Plugin/views/field/PublicationState.php- Display handlersrc/Plugin/views/filter/PublicationStateFilter.php- Filter handlersrc/Plugin/views/sort/PublicationStateSort.php- Sort handlerAll files are marked as deprecated and reference the future Workspaces integration.
Comment #13
tedbowComment #14
lauriiiThat would be ideal but we should not implement this at the cost of making the future Workspaces integration significantly more complicated.
We are planning to work on Workspaces support in 2026 so we should avoid making the migration to Workspaces significantly more complicated than it's today. Are there drawbacks with the Views solution that we should be aware of because you're generically claiming that it might be better to go with the alternative approach? I assume we want to prioritize easy migration to Workspaces over whatever that is but it might be good to document the downsides explicitly.
Comment #17
tedbow@lauriii
I just generally if we weren't planning to switch Workspaces that it would easier to keep the logic of the Page publiciation state internal to the Page entity rather than relying on special views integration and \Drupal\canvas\AutoSave\AutoSaveManager::entityIsDraft. It would be more understandable and maintainable.
But since we are planning on moving to Workspaces and since we are planning to add Node edits, which also has the problem of only have boolean status, determine 3 possible publication states(if we want display "archived" next to nodes), then rely on special logic in \Drupal\canvas\AutoSave\AutoSaveManager::entityIsDraft makes sense.
So @narendrar I have close the other merge requests and brought in the Views integration into the original MR.
The views integration should solve this problem from #8
We will not need to add any extra fields the Page entity and keep the approach of the original MR
Comment #18
vipin.mittal18Comment #19
vipin.mittal18Added workflow video
Comment #20
lauriiiThanks for the workflow video! A couple of clarifications:
The workflow should allow going directly from Draft to Archived. There are valid use cases where a user might want to archive a draft page without ever publishing it first (e.g., a page that was started but is no longer needed, or content that was created speculatively and then abandoned). Forcing users to publish first just to archive seems unnecessarily restrictive.
Can we confirm the expected behavior for the publishing flow? When you have a draft page and go through "Review changes" → "Publish", this action should publish the page directly. There shouldn't be a separate/explicit "publish" step required before you can use the standard publish flow. In other words: Draft → Review changes → Publish should result in a published page. It should not require Draft → (explicit publish action) → Review changes → Publish which is what I see in the video in #19.
Could you update the workflow diagram to reflect these cases?
Comment #21
vipin.mittal18Hello Laurii,
------------------------------------------------------------
Current (Existing) Behavior
- When a page is in Draft state and the user goes through
“Review changes” → “Publish”,
→ all current draft changes are published immediately.
------------------------------------------------------------
New Scenario: “Publish page” option in Top Dropdown
If a user:
- Starts creating a page, and
- Directly clicks “Publish page” from the top dropdown (without going through Review changes),
What is Recommended / Expected Behavior
Q: Should The page be published immediately without even touching top-right unpublished changes section?
Q: Should All unpublished changes visible in the top-right section be included in that published version?
Comment #22
lauriiiI think there may be some confusion here. When a page is in Draft state, the "Publish page" option should not be visible in the dropdown at all.
For draft pages, the available action should be "Archive". This allows users to archive a page that has never been published. Archiving a draft means the content won't be published when changes are reviewed/published; it essentially shelves the draft.
To clarify the expected behavior:
Draft page (never published):
Published page:
Archived page:
I believe this was the behavior in an earlier version of the MR but has since changed. We should revert to that previous behavior. The workflow diagram should also be updated to show that Draft → Archived is a valid transition.
Comment #24
tedbow@lauriii thanks for the clarification.
re
The current approach in 3556265-create-ability-to will not work in this case.
Since "Status" is boolean field it cannot record 3 different states, Draft, Published, Archive.
The current MR was getting around this by relying on
\Drupal\canvas\AutoSave\AutoSaveManager::entityIsDraft and the views integration depend on this logic
If you can go from draft -> archive then in that case the above won't work because there can be Archived pages without having a published revision.
I am not saying we can't do it, just we have use a different approach.
In !409 I added a
publication_statefield which can store 3 states but I closed that MR because in #14 you saidI thinking adding a
publication_statefield that we would then need to remove because after "publication state" would be managed by Workspaces would make the migration to Workspaces more complicated.Probably the migration to Workspaces to will be complicated anyways but if we don't add any new field in this issue we won't be making it worse than 1.x
But now I don't see how we get around adding the
publication_statefield to Page or some sort of storage. Below I will outline a plan for that but before that I think we need to consider how this would affect editing individual nodes(can't find an issue but 99% certain is on the roadmap).Would nodes also need the same publication state workflow as pages? I assume yes, but should get confirmation as this would probably affect how we handle Pages now. Nodes will have the same problem of having a boolean
statusbut needing store 3 publication states.We could use
hook_entity_base_field_info_alterto add the samepublication_statefield to Nodes that we do for Pages. But if we are going to do that we should probably usecanvas_publication_statefor both so don't have conflicts on node with other modules also altering the base fields(could happen on pages too?)The other option would be to add a
canvas_publication_statecontent entity type but that seems like we would be starting to reimplement the Workflows\Content Moderation modulesTransition to Workspaces
Assumptions for what Workspaces integration would mean
If those assumption are correct then in an update hook when we require Workspaces we could
canvas_publication_stateinto the default workspacecanvas_publication_statefrom both Page and Node. We should markcanvas_publication_stateand anyisDraft(), isArchivedetc functions as internal/deprecated when we add it here, so we are free to do this.Even if we didn't do the current issue I would assume we would still have to do 2 & 3 we would just determine draft by
status === 0 && count(published revisions) === 0So major complication would be adding
canvas_publication_stateto both Pages and Node(assuming editing of nodes happens before Workspaces integration), then removing it.So this bring back to @lauriii's comments(in Italics)
in response to
That would be ideal but we should not implement this at the cost of making the future Workspaces integration significantly more complicated.
and
We are planning to work on Workspaces support in 2026 so we should avoid making the migration to Workspaces significantly more complicated than it's today.
I do think this would make "migration to Workspaces significantly more complicated than it's today.". What I detailed is not that complicated but I think there could definitely be "known unknowns" unless we are going to plan in detail now our migration to workspaces, and even then not sure, a plan is just a plan.
So do we not do this issue at all or do abandon
I do understand this a valid use case but is worth making the migration to Workspaces more complicated? question for @laurii
I think the other options for
is just deleting the draft or choosing not to publish and leaving it in draft incase it is needed later. Maybe not ideal but maybe an ok comprise
I added extra questions to "Remaining questions"
Comment #25
mglamanI am extremely concerned that Canvas is going to over complicate this and make Content Moderation not possible. I've already spoke to two different organizations who won't adopt Canvas due to lack of Content Moderation support for pages and their compliance requirements. I am also in the camp who believes content moderation and workspaces are not exclusive, so we still have to consider content moderation.
This is how I would handle it if pages are not managed by content moderation. And expose a published boolean in the page data.
Comment #26
lauriiiGiven the complexity of the ideal experience described here, I think we can remove the option to mark draft as archived. In that scenario, we should not display either publish or archive as an option for draft content.
For the use case of abandoning draft pages that are no longer needed, users can either delete the draft entirely or leave it in draft status by not publishing it. Not perfect, but a reasonable compromise that keeps the implementation simpler.
That's true today. However, I personally believe we can achieve requirements that users of both modules have with a single solution.
Comment #27
tedbow@lauriii ok thanks for the clarification
@NarendraR that means we are back to MR 346 with the View integration
Comment #29
narendrarComment #30
narendrarThis feature is now ready for review. Features implemented:
Comment #31
utkarsh_33 commentedWhile testing this i had some observations which may or may not be done as a part of this issue but just documenting it on the issue to get opinions:-
Unpublished changesand when we select any archived page and saypublish 1 selected, it might be confusing for the user what's happening all at once. What i suggest is change the titleUnpublished changesto something more generic likeUntracked changesandpublish 1 selectedto something more generic(I don't have a suggestion on top of my head).Comment #32
tedbowI am the middle of reviewing this but found this confusing UX, I also agree with #31 that is also confusing
"published page" is page that is page that is currently published but I clicked "Archive page"
"A page" is a currently archived page that has no changes
Both of these having "Archived" label is confusing
The page that will be archived says "Archived". If didn't already know who this works I would probably
assume this is currently archived page that is going to have changes made it. I think here it should be changed to "Archive"
Comment #33
tedbow.
Comment #34
tedbow@narendrar getting closer!
I left a review and pushed up a couple commits
Comment #35
tedbowThings look good. @NarendraR said he was going address a couple extra things.
It still failing linting
It would be good if someone else could review the JS
Comment #36
tedbowI created #3565219: Setting homepage doesn't take into account draft and delete pages for problems I found in 1.x while testing the current issue
@NarendraR can you look that issue and
Thanks
Comment #37
narendrarRe #32,
Now we have an visual identifier to differentiate:
Re #36, #3565219: Setting homepage doesn't take into account draft and delete pages, Problem 1 will not happen when this issue is merged as we are not showing 'Set as Homepage' for draft pages and we have tests to validate that. Problem 2 is still an issue.
We have some cypress tests failing for this MR, not sure if they are related to changes made here. Moving it to Needs review for another review.
Comment #38
narendrarPlease ignore my comment in #37 related to #36,
I have reverted the unnecessary changes in commit https://git.drupalcode.org/project/canvas/-/merge_requests/346/diffs?com... for draft page to not show 'Set as Homepage' as that is existing functionality and should not be altered.
So, both problems identified in #3565219: Setting homepage doesn't take into account draft and delete pages are not solved by this MR and needs to be handled separately in that issue.
Comment #39
wim leersComment #40
wim leersOh and the CR talks about relying on Content Moderation, but neither the issue title nor summary say that? 😅 So: is that indeed what is happening?
Clarifying all that would improve reviewability of this MR 🙏
EDIT: skimming the >30 comments above it seems that in the last ~dozen comments there's been some pivots based on feedback from @lauriii in response to Ted, Naren & Matt. Summarizing those into the issue summary is essentially what I'm asking 😇
Comment #41
tedbowremaining questions have been answered
Comment #42
tedbow#40, I have updated the summary. This issue will not require Workspaces but does acknowledge that we will use workspaces eventually and therefore how Pages are determined to be "draft" will change. This also affects how "archived" pages are determinedbecause currently there is no field explicitly tell the difference between draft and archived both unpublished.
Content Moderation will not be used, per Lauriii
Re the change recorded I am not sure we need one, https://git.drupalcode.org/project/canvas/-/merge_requests/346#note_647461. Or if we do maybe it should just simply state you can now archive pages.
Comment #43
mglamanBut will whatever we do here prevent integration with it by others?
Comment #44
lauriiiI'm agnostic to whether we should use Content Moderation or not. I believe that in future, Content Moderation and Workspaces will converge. Integrating Canvas with Workspaces is a high priority meaning it's something we'd do likely some time in 2026. Because of that, we should implement this in a way that 1) minimizes any friction for us to integrate with Workspaces 2) keeps implementing this as simple as possible. I don't know how using Content Moderation impacts that.
Comment #45
narendrarFeedback addressed. I've removed the change record link as it's no longer needed. Moving this back to Needs Review.
Comment #46
tedbowI tried to simplify things. Please review the changes. There is at least 1 comment I didn't get to also
Thanks
Comment #47
narendrarMoving it again for review.
Comment #48
aneek commentedComment #49
wim leersI did a high-level review: I focused on data integrity/tech debt/long-term thinking.
See MR for details.
I think with an MR of this complexity (deep into the entity system and views, and also affecting our internal HTTP API), it's important that the tests have been thoroughly reviewed.
Naren & Ted: did both of you thoroughly review all of the tests (~50% of this MR AFAICT!)? That's IMHO crucial before merging this, to ensure >1 person understands this 🤝
Comment #50
tedbowre #49 I need to re-review the tests still
Comment #51
tedbow@NarendraR, could you address @wimleers' review? Feel free to only address the ones you feel you have clear direction on, and then re-assign them to me to take on the others. Thanks!
Comment #52
narendrarAssigned to @tedbow to address remaining feedback.
Comment #53
tedbow@NarendraR , thanks for the update test and other changes. Have left a few comments that need to be addressed
It still needs someone to do a review of the changes on the front-end.
Comment #54
narendrarComment #55
kunal.sachdev commentedIn the middle of testing, I found this case confusing: when I have a published page and I click on Archive page but this change is not yet published, the status is shown as Archive*.
I don't think this makes sense and it is quite confusing. In my opinion, we shouldn't show anything in this case; if the change to archive the page isn't published yet, we shouldn't show any status related to archiving. Attaching a screenshot for reference.
Comment #56
acbramley commentedJust chiming in to echo #25, all of our clients need content moderation but we have never used workspaces. I don't see the 2 coverging any time soon personally.
Having a quick skim through the MR a lot of this code would no longer be required if it were using workflow states instead.
Comment #57
mstrelan commentedI note in #10 that content moderation was not chosen due to significant complexity. Well now we have two significant complexities that achieve the same thing. Why not standardise one the one that has already existed in core for over a decade?
Comment #58
acbramley commentedFurther to #57 I'm not sure why CM would be considered a significant complexity in the first place?
In any case, using CM will also give free integration with other already built features such as Scheduled Transitions
Comment #59
catchI don't understand why an extremely complex workaround is being implemented here, just to avoid unpublished (archived) pages being shown alongside unpublished (draft) pages - that's a longstanding issue with every entity type in existence.
At the moment no-one has any unpublished (archived) canvas pages at all because this issue isn't fixed, fixing it isn't going to suddenly flood the UI with them.
Ordering the list by created descending would generally move archived pages to the bottom. Seems like a pretty small temporary UX niggle compared to the complexity added here, and then actual workflow and separation of listings can be added on top.
All of this time would be much better spent actually figuring out workspaces support instead.
On content moderation vs. workspaces, once #3486378: [Plan] Allow for / implement simplified content workflow with workspaces is done (it's a couple of hard issues away at the moment), content moderation will use workspaces under the hood. The back end and UI of workspaces are already completely separated in core so you can use the underlying mechanism without exposing separate workspaces in the interface.
I don't know the specific plans for how canvas is planning to implement with workspaces but it shouldn't end up being an either/or choice as such, unless workspaces implements an essentially hard-coded workflow UI on top. But again - defining that would be more productive than complex CASE THEN ELSE queries and adding views plugins which will eventually need to be deprecated then removed again.
Comment #60
narendrarComment #61
lauriiiWe've considered Content Moderation, but there are two challenges. First, to integrate it with Canvas we'd need to support only a subset of its functionality, so it would end up being an opinionated experience regardless. Second, adopting Content Moderation now could make our eventual Workspaces integration harder.
I've talked to users who have Content Moderation enabled and would benefit from Workspaces-style staging, but the UX friction and complexity of Workspaces has prevented adoption. That's a challenge Canvas will need to solve by providing a simplified/opinionated experience on top of the underlying system.
Even when #3486378: [Plan] Allow for / implement simplified content workflow with workspaces lands, Canvas will still need its own opinionated layer to deliver the experience we're targeting.
We're currently planning to work on Workspaces integration in Q2. In the meantime, if the team is willing to maintain this implementation and handle a potential deprecation path when Workspaces support arrives, I'm comfortable with the approach in this MR. The distinction between archived and draft states improves consistency between the Drupal admin UI and Canvas, which is a UX improvement even if it's not strictly required.
Comment #62
kunal.sachdev commentedLooking good!!
Comment #63
catchLeft comments on the MR about the upgrade path for the view.
Would also like to state here for the record that I don't think this workaround should be committed at all. It's 3000+ lines of code including new views filters etc. which will then have to be undone again when the actual solution is committed, including even more views updates, deprecations etc. the time would have been better spent working on the actual feature, instead of a simulacrum of the feature.
Comment #64
tedbowHere is an idea to limit the scope of this issue.
After thinking about this, I think one thing to consider is that the view at admin/content/pages is probably not that useful now.
The most confusing thing about this view is that all the "draft" pages currently show up as "Untitled Page". This is regardless of what the title of these pages is in the Canvas UI. As soon as you update a page's title in the Canvas UI, everywhere in the Canvas UI you will see the new title. You see it in at least 4 places: 1) the page data form, 2) the preview, 3) the navigation dropdown, and 4) unpublished changes.
So if you have 10 "draft" pages that you have updated the title for, you will see those titles in all those places.
At admin/content/pages, you will see all those 10 pages as "Untitled Page". So as far as listing pages and directing you to view or edit the pages, as soon as you have more than 2 pages, this listing is pretty useless. The "Updated" time is also pretty useless because it is only ever going to show you the time it was created. You would just have to click on various "Untitled pages" until you find the one you want.
The reason this happens is that after you first create a page, until you publish a page, we never do a real entity save. So the draft entities, as far as the view at admin/content/pages is concerned, never change until you publish, and they are no longer drafts.
The reason for this is that we have a separate autosave mechanism, but of course a user who doesn't know how Canvas works would have no idea about this and really should not have to.
The thing is, as far as I know, nobody has filed an issue for this. My guess is because everyone is just using the Canvas UI navigation to find their draft pages. If people were actually using admin/content/pages to find and manage draft pages, we would have heard complaints by now.
While this issue as is would at least show you the "draft" and "archived" status, it would not solve the "untitled page" problem on admin/content/pages. We could try to solve that, regardless of what we do here, but given we are going to add Workspaces integration AND nobody has complained, I am not sure it is worth it.
So, here are my thoughts of what we could do:
This would leave admin/content/pages showing Published and "archived" pages labeled as "Unpublished"(we can easily fix that, see next point)
We could even output a message on this page pointing to /canvas with the text "See your draft page in the Canvas editor"
This would not help existing sites but we could put a mention in the release notes about how to manually import the view config/optional if they want to.
If we did this then for new sites, admin/content/pages would show only "Published" and "Archived" pages with the same labels they have in the Canvas UI.
Comment #66
mstrelan commentedI never use the Canvas UI navigation to switch pages, I find that concept confusing and don't understand how it will scale well to hundreds of pages. Nevertheless, I have indeed noticed "Untitled page" show in the Pages view, but I've only ever had one draft at a time so far.
Comment #67
tedbowI pushed up an MR for the suggestion in #64. It does remove some extra complexity around views but this is still a lot of code. Still I would say archiving is big feature so maybe ok.
Re #63
This does remove the views plugins, so won't have worry about deprecating them.
Also I was surprised the original MR was 3000 lines. I saw that we had needless text fixture that was a copy of unupdated View. I removed that which removed almost 1000 lines
Comment #68
tedbowre #66
@mstrelan I agree that current navigation is not going to scale to hundreds of pages. But we could improve the in UI navigation to avoid someone having to leave the Canvas editor to find pages
Comment #69
amateescu commentedI was wondering if the Trash module was considered for this functionality, but found nothing after a quick search.
It's already included in Drupal CMS, and provides the archiving capability requested here. Trashed pages would be hidden from the default view provided by Canvas, they would be available under
/admin/content/trash/canvas_page, and they can be restored (or purged) when needed.Comment #70
penyaskitoIMHO archiving + trash are different concepts, specially since trash allows to permanently delete content based on when it was deleted. I might want to archive for historic reasons, so no published revision of this content is visible to the public, but I have to keep the content around for whatever reason, including but not limited to legal reasons.
AFAIK there's no incompatibility with the trash module, but if there is it should be a different issue.
Comment #71
tedbow🤦♂️ @amateescu I think that is a pretty good idea.
We could just recommend the module now and it would work with the View already at "admin/content/pages."
Also, I checked and our "delete" link inside the Canvas Editor works too for sending items to the trash. This removes them from the Canvas UI, but you can still see them at `admin/content/trash/canvas_page`. If you take them out of the trash, they will then show up again in the Canvas UI.
Honestly, this might be better UX than we have now in either of our MRs right now. Currently, if you "archive" a page, it is still present in the Canvas UI. So, if you used Canvas for a number of years with our approaches in the MR, the Canvas UI navigation would start to fill up with archived pages, which is probably not what you would want.
So out of the box, Trash would just work. We could, though, make it better with a couple of small tweaks on our side.
We could tweak \Drupal\canvas\Controller\ApiContentControllers::delete to fix this. Not sure exactly yet, but it seems solvable.
That could be enough, especially since Drupal CMS uses this, so presumably, it would make the experience similar for pages and nodes.
At a later date, we could make a fancy trash UI inside the editor.
Comment #72
tedbowwhoops didn't mean to change the title 🤦♂️🤦♂️
Comment #73
lauriiiI agree with @penyaskito that Trash is a different concept from archiving content, and I don't believe the Trash module is a 1:1 replacement for the functionality proposed here.
The most important distinction is how this interacts with the Canvas publishing workflow. The benefit of the Archive functionality in this MR is that it treats "unpublishing" as a state change that can be staged. This allows a user to "take down" pages as part of a larger set of changes. For example, if a user wants to launch a page and remove the old one simultaneously, they can draft the new page and set the old one to "archive" status, then publish both changes in a single action.
Another difference is that you cannot edit pages in the trash. Trash is almost like a "soft delete" state and the content is pending removal and would have to be restored to be modified. There's also currently no way to publish the content again after restoring from trash. However, if we implemented this issue, the page from trash would become archived and could now be published.
Comment #74
mstrelan commentedThis problem is not unique to canvas, we should solve this in workspaces / content moderation. Currently we use the scheduled_transitions module for this purpose but each entity is scheduled independently. Batching the changes like in workspaces would be better.
Comment #75
tedbow@lauriii, I think the decision is up to you.
The case for Trash
This issue started from a Slack thread which didn't give reasons for why they wanted to unpublish, so as far as what users asked for, I am not sure the distinction between what the Trash module gives us is meaningful. I agree the UX is better in the two merge requests that are open. However, I wonder whether it is worth the technical debt, considering we plan to work on workspaces in a few months.
In #14 regarding adding the "publication status" field, you said:
I think the two merge requests would definitely make the migration to Workspaces significantly more complicated than it is today, compared to using the Trash module.
Re #73
I checked this. If you restore a page that was published, it will immediately be published. A restored draft page will be a draft.
I think the remaining question is, when we implement this, will it be a hard requirement? We could say Trash is good enough for now because we don't want to implement what we will get from Workspaces eventually.
Another advantage for Trash is that Drupal CMS will use it, so this will give the user a better experience for consistency between other entities and pages.
Sunk costs?
If @amateescu had suggested Trash the day after we made this issue, would we have put all this effort into our custom solution knowing Workspaces work was coming up?
The existing MRs
If we are going to use one of the MRs, I suggest we do https://git.drupalcode.org/project/canvas/-/merge_requests/543 because it does not have Views plugins and does not update the view on existing sites. I think the UX is probably better or as good.
Comment #76
pameeela commentedThe only thing to flag with Trash is #3530375: Update deletion confirmation message for Canvas entities for compatibility with trash module -- the message says it will be deleted immediately, even though it won't be.
Comment #77
amateescu commentedThat's exactly the experience that Workspaces + Trash gives you out of the box, the ability to stage content (soft-)deletions.
It's already solved by Workspaces / Content Moderation :) Unless that "in" was meant as "with", to which I wholeheartedly agree!
I also agree with @tedbow's analysis: the easiest way forward at the moment is to direct users to use Trash for a quick way to enable a "pseudo-archiving" feature, while the future work of integrating Canvas with WS / CM would provide
Archiveas an actual content workflow state. And even after that, archiving and soft-deleting pages are complementary features, you can both soft-delete and archive different pages in a set of content changes.Comment #78
mstrelan commentedEven better! I haven't used workspaces so wasn't sure.
If that's the goal for the future, and this issue is just trying to resolve a UX issue that previously published pages appear as drafts, why not just temporarily introduce a computed field that stores if an unpublished entity has been published before? If yes then it's treated as archived, if no then it's draft.
Edit: wait, do we have revisions? Maybe this is not possible.
Comment #79
catch#3530375: Update deletion confirmation message for Canvas entities for compatibility with trash module was opened by @pameela 9 months ago in the Canvas queue. There are eight comments on it, this issue is coming up to 80.
Comment #80
mherchel@lauriii asked me to chime in.
After reading through all the trashy comments, it seems that'd solve the 80% use case.
The remaining use cases (being able to edit unpublished content etc) are still important, but since I'm not sure of the state of the MRs, I can't really say what's preferable.
Comment #81
pameeela commentedI think Trash is a decent temporary solution to this, not perfect but preferable to introducing a bunch of complexity that will be problematic later.
Comment #82
tedbowIn #61 @laurii states
Above, in the "Transition to Workspaces" section, we have
But in #63 I stated
I have confirmed with @lauriii directly that this decision has not been made yet.
In relation to the current work, he told me
Thinking more about this, I think it is very relevant.
What makes the whole situation complex without Workspaces is that we have a boolean field
statusbut we need to store 3 states: draft, published, and archived. Since Workspaces would allow us to create new published pages (and nodes etc.) without those pages appearing on the live site, we could just create those pages as published in the workspace. Therefore, we would no longer needstatus === 0for both draft and archive pages. AFAICT, there would be no need for a "draft" of an individual page inside of a Workspace because the whole Workspace would be a draft.Possibilities for Workspaces integration
Workspaces is optional
Workspaces is NOT optional
Comment #83
tedbowOk. I tried to take a look to see if we could make Merge request 543 work.
I found out that the major change to \Drupal\canvas\AutoSave\AutoSaveManager::entityIsConsideredNew we can't actually remove 🎉.
The existing behavior in 1.x where we base this off the default title still works. There is no way in the UI to change the title of a saved page before publishing. That also is how 1.x works.
Sorry to go back and forth. I am beginning to think this method might actually work. I don't think we have to think of this as 3 states anymore. We really just have published and unpublished. We just have a special UI workflow around "new" pages which we are calling drafts, but this is not a complete separate state like you would have in Content Moderation.
One addition that will make this work better is if we prevent publishing a page with the default title, "Untitled page". I think this likely could cause a problem in 1.x too; we just never looked into it. \Drupal\canvas\AutoSave\AutoSaveManager::entityIsConsideredNew would tell you a published page is new in 1.x if you never changed the title. So we could do this in a separate issue if we don't want the existing MR to be more complex, and take on existing problems.
I will look at this again tomorrow, but I am hopeful.
Also, I don't think this method would stop us from also making the module compatible with Trash. See #3572636: Trash module compatiblity: Can't publish restored page with existing auto-save, which is a very small fix and would not conflict with this.
Comment #84
catchIf you always want to track published to unpublished as 'archived' regardless of whether any kind of moderation solution is in place, that's what the workflow module in core is for.
Comment #85
tedbowre #83. I now definitely think we should go with Merge request 543
Caveat, I changed all mentions of "archive" to "unpublish" in the MR. It is more accurate to what we are doing. The rest of my reasoning assumes this.
What 543 Does
Major Backend changes
route canvas.api.content.patchwhich calls the new\Drupal\canvas\Controller\ApiContentControllers::patchThis updates the auto-save entry for a content entity (limited to pages for now). Limited to allowing changing
$entity_type->getKey('published')for now. It also disallows unpublishing the homepage.\Drupal\canvas\Controller\ApiAutoSaveController::postto not publish all content entities it is processing.This is needed because of point 1. Before this when saving any entity there it would be upublished.
\Drupal\canvas\Hook\PageHooks::preventHomepageDeletiontopreventHomepageModificationto prevent unpublishing the homepage.
Note to self: check if the logic here is correct
\Drupal\canvas\Controller\ApiContentControllers::getEntityOperationsFront-end changes
Handles new unpublish/publish links.
Displays an "unpublished" status for pages that we don't consider "new" and are unpublished. "New" is not a new concept in this MR and is used in 1.x for displaying "draft".
Displays "🕦Unpublish" for pages that are currently published but where the user has clicked "unpublish".
"new" meaning
We are not changing
\Drupal\canvas\AutoSave\AutoSaveManager::entityIsConsideredNew. This existing"New" pages are ones that still have the original title; basically, they have never been saved (besides auto-save) since they were created in the UI. There are built-in assumptions in Canvas that all pages will be created within the Canvas UI and therefore we control the title. This also is not new in this MR.
How "new" relates to this MR: We don't show the "publish" action link that will update the auto-save on "new" pages even though they are unpublished. This is because all "new" pages will be updated automatically when submitting the "Review X changes" form.
Workspaces integration
Since my comment in #83, I reverted the change in
\Drupal\canvas\AutoSave\AutoSaveManager::entityIsConsideredNewthat gave me concern in how it will affect eventual Workspace integration. We may or may not need to update this based on whether workspaces are optional, but that doesn't change with this MR.Having gone over and described the changes in this MR without needing to introduce an "archive" state, I think we are just introducing an overlooked ability to unpublish pages, and this shouldn't affect Workspaces.
The AutoSaveManager may need to change when we integrate with Workspaces, but it will likely not go away. It is used for allowing quick saves and saving entity states even if they do not completely validate (though this is caught before actually saving them).
admin/content/pages View concerns
This does not solve the problems I have listed in #63, but it doesn't make them worse. I have opened up #3572851: Dynamically use current auto-save title instead of "untitled page" in views, delete dialog, and elsewhere in the UI that should help with this View. It should also help with the Trash module or other places we list Pages in Views.
Trash Module
These changes should not be incompatible with the Trash module. I think it is important that the UI changes in this MR now say "Unpublish" vs "archive" because unpublish is the existing concept in Drupal, and therefore the users don't have to think about what the difference is between archive and trash, and archive and unpublish.
I opened #3572636: Trash module compatiblity: Can't publish restored page with existing auto-save, which is an issue regardless of this one. I mentioned that #3572851: Dynamically use current auto-save title instead of "untitled page" in views, delete dialog, and elsewhere in the UI helps with Trash Views but also will help dialogs in Trash to not show "untitled page" for all "new" pages.
Comment #86
tedbowCreated follow-up #3573408: Prevent unpublishing a Canvas Page if it is the home page,
I explain in that issue why I think it can be a follow-up.
Comment #87
tedbowComment #89
tedbowI chatted with @lauriii and he is ok with the latest approach. I did a self review and now someone else should review.
Comment #90
kunal.sachdev commentedLooks good!!
Comment #92
tedbowThanks for the work and input everyone! I think it was good we decided to scale back the scope of this issue
Comment #94
penyaskitoSomething introduced here: #3578094: Fix REST API violation of the Canvas Page PATCH operation: auto-save operations should be auto-save prefixed
Comment #95
penyaskitoComment #97
mlncn commentedDoes not appear this needed fix is on the 1.3.x branch yet. Is the relationship to this 1.x dev branch to the three (!) supported branches (1.2.x, 1.3.x, and 1.4.x) documented anywhere, like if/when fixes get (cherry-picked?) over to the release branches?