Managing API
Even though users can manage content items via the Orchard Core Administration UI, we can also build our own custom API endpoints to allow programatically managing the content items. This is particularly useful when you need custom logic, automation, or third-party integrations.
For content management, in order to handle content items programatically, we need to use another Orchard Core service, IContentManager
. It provides an abstraction layer for managing the lifecycle of content items. Essentially, it is responsible for the CRUD operations of content items within Orchard Core.
Versionable Content Item
The Versionable
option is a content item option in Orchard Core that determines whether a content item should maintain multiple versions (such as drafts, published, and historical versions).
When enabled, it unlocks features like:
Draft and published separation.
Version history for content.
The ability to revert to previous versions.
Content Management Behaviour with Versionable Disabled
Without the Versionable
setting, Orchard Core does not maintain separate versions for draft and published states.
Editing a published content item will directly modify the live version. There will be no new draft created.
If Versionable
is not enabled, the content item will only have one single version, meaning:
Edits overwrite the current version directly.
There’s no draft-published distinction or version history.
The Latest
and Published
flags both remain true
when the content item is updated, as there is only one version in existence.
We can still unpublish and republish a content item, but the system won’t retain separate states or versions between these actions. Programmatic operations like creating a new draft, discarding drafts, or managing historical versions become irrelevant since no versioning exists.
In this page, we will thus only discuss content management behaviour with versionable enabled.
Versionable Content Item Workflow
There will be several content item states as listed below.
Draft
A version of the content item that is being worked on but is not yet visible to the public. Exists only when modifications are made to a previously published item or when a new item is created but not published.
Published
The version of the content item that is publicly visible and available to end users. There can only be one published version of a content item at any given time.
Historical
Older versions of content items that are retained for history or rollback purposes. These versions are neither draft nor published but serve as a reference.
Removed
Content items can be "soft deleted" using methods like RemoveAsync()
. This makes them invisible but does not delete them permanently.
Orchard Core uses a combination of the Published
and Latest
flags to implicitly determine the state of a content item. Here's how it works:
True
True
Published
True
False
Draft
False
True
Historical
False
False
Removed
When a new content item is created, it starts as a draft. When a content item is published, its Published
flag is set to true
, and it becomes publicly visible. If we edit a published content item, Orchard Core creates a new draft version. The published version remains live until the draft is published.
Create Content Item
To create a new content item for Product Information, we need to assign an instance of ProductInformationPart
to the Content
of the contentItem
. For simplicity of the demo, we will use GET method to create a sample product information as shown below.
Now, when we visit the Orchard Core Administration UI, we should be able to see the entry with ProductName and Description filled.
Update Content Item
Before we can update a content item, we need to be able to fetch it. To retrieve a content item, we can use the IContentManager.GetAsync()
method. The method accepts a content item ID and a VersionOptions parameter to specify which version of the content item to retrieve:
VersionOptions.Published: Retrieves the latest published version.
VersionOptions.Latest: Retrieves the most recent version (draft or published).
VersionOptions.Draft: Retrieves the draft version only.
VersionOptions.DraftRequire: Ensures that the draft version is retrieved (or created if it doesn’t exist).
As we discuss earlier, when we edit a published content item, Orchard Core creates a new draft version. So, we need to use VersionOptions.DraftRequire
.
Calling the method above will generate the following audit trail.
If your Versionable
is disabled for the content item, you can use the following simpler way of updating. The following code shows how we can update the content of an existing content item after retrieving it with its contentItemId
.
Publish/Unpublish Content Item
In Orchard Core, we can use PublishAsync
and UnpublishAsync
to manage the publishing state of a content item programmatically. Before that, we need to determine whether a content item has a published version using the HasPublishedVersionAsync
method.
Unpublishing does not delete the published version but marks it as unavailable.
The following code shows how we can publish or unpublish a content item depends on its publishing state.
Delete Content Item
There is a method called RemoveAsync
we can use to delete content item. However, we need to take note that it only removes ContentItem.Latest
and ContentItem.Published
flags from a content item, making it invisible from the system. It does not physically delete the content item.
There is also another method called DiscardDraftAsync
which deletes the draft version of a content item. Take note that it only removes the draft version of a content item if it exists, leaving the published version (if any) untouched.
So what will happen if we perform UnpublishAsync
or RemoveAsync
on a content item and then call DiscardDraftAsync
? Here is what happens step by step:
Since we know that when we unpublish a content item using UnpublishAsync
, the Published
flag is removed from the content item. The content item will still have the Latest
flag set, indicating that it's in a draft state. So, calling DiscardDraftAsync
after using UnpublishAsync
should have the same effect as RemoveAsync
. Nevertheless , the content item is still stored in the database.
Let's go through some examples. For example, we have a published content item which can be represented as the following JSON.
Now, if we call RemoveAsync
on it, both the Latest
and Published
flags will be set to false, so the end result of calling RemoveAsync
should be as follows.
At this moment, this content item is still queryable with the following code.
However, it is not queryable with the following because the Latest
flag is already set to false.
You may wonder, what if we use DiscardDraft
on a published content item? The answer is simple. As shown in the following screenshot, there will be an error thrown.
Now, let's move on to a different content item which is a draft and is represented as the following JSON.
Will using RemoveAsync
on a draft throws an exception also? Nope, because what RemoveAsync
will do is simply set the Latest
flag of a draft to false since its Published
flag is already false.
In fact, calling DiscardDraft
on a draft will do the same as calling RemoveAsync
on it because the outcome is always having both Latest
and Published
flag set to false.
And yes, for a discarded draft, it is still queryable with QueryContentItemsAsync
but not GetAsync
, as we discussed above.
Last updated