Hide menu Last updated: Dec 11 2017
Area: Episerver CMS Applies to versions: 10.1 and higher

Working with content approvals

In this topic

Working with approval definitions

Creating an approval definition

This example shows the creation and saving of a definition:

using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink; IApprovalDefinitionRepository definitionRepository;
var langEN = new CultureInfo[] { CultureInfo.GetCultureInfo("en") }; var langSV = new CultureInfo[] { CultureInfo.GetCultureInfo("sv") }; // Creates a content approval definition var definition = new ContentApprovalDefinition { ContentLink = contentLink, Steps = new List<ApprovalDefinitionStep> { new ApprovalDefinitionStep("step1", new ApprovalDefinitionReviewer[] { new ApprovalDefinitionReviewer("user1a", langEN), new ApprovalDefinitionReviewer("user1b", langSV), }), new ApprovalDefinitionStep("step2", new ApprovalDefinitionReviewer[] { new ApprovalDefinitionReviewer("user2a", langEN.Union(langSV)) }) } }; // Saves the definition await definitionRepository.SaveAsync(definition);

Updating an approval definition

In this example, a definition is updated with a new reviewer in the second step. This reviewer can approve items in all languages using CultureInfo.InvariantCulture.
using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink; IApprovalDefinitionRepository definitionRepository;
var langInvariant = new CultureInfo[] { CultureInfo.InvariantCulture }; // Gets a definition ApprovalDefinition definition = await definitionRepository.GetAsync(contentLink); definition = definition.CreateWritableClone(); definition.Steps[1].Reviewers.Add(new ApprovalDefinitionReviewer("user2b", langInvariant)); // Saves a definition await definitionRepository.SaveAsync(definition);

Adding a role to an approval definition (New in 10.10)

In this example, a definition is updated with a new role reviewer in the first step. This role can approve items in the swedish language.
using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink; IApprovalDefinitionRepository definitionRepository;
var langSV = new CultureInfo[] { CultureInfo.GetCultureInfo("sv")}; // Gets a definition ApprovalDefinition definition = await definitionRepository.GetAsync(contentLink); definition = definition.CreateWritableClone();
definition.Steps[0].Reviewers.Add(new ApprovalDefinitionReviewer("managers", langSV, ApprovalDefinitionReviewerType.Role)); // Saves a definition await definitionRepository.SaveAsync(definition);

Deleting an approval definition

This example deletes a definition using ID:
ApprovalDefinition definition;
IApprovalDefinitionRepository definitionRepository;

// Deletes a definition
await definitionRepository.DeleteAsync(definition.ID);

Note: A definition cannot be deleted if running approvals exist. All approval instances must be completed or aborted before deleting an approval definition.

Getting an approval definition

Gets a definition in a couple of different ways:
using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink; ApprovalDefinition definition; Approval approval; IApprovalDefinitionRepository definitionRepository; IApprovalDefinitionVersionRepository definitionVersionRepository; // Gets the latest version of a definition using a definition id. var definition1 = await definitionRepository.GetAsync(definition.ID); // Gets a specific version of a definition using a version id. var definition2 = await definitionVersionRepository.GetAsync(approval.VersionID); // Gets the latest version of a definition using a ContentReference. var definition3 = await definitionRepository.GetAsync(contentLink); // Gets the latest version of a definition by resolving a ContentReference. var definitionResolveResult = await definitionRepository.ResolveAsync(contentLink); // The Resolve-method returns a result with a definition and a flag specifying if the definition was found on an ancestor var definition4 = definitionResolveResult.Definition as ContentApprovalDefinition; var isInherited = definitionResolveResult.IsInherited;

Working with approvals

IApprovalRepository is the base interface for saving, deleting and listing approvals and their decisions. IApprovalEngine is built on top of IApprovalRepository to streamline the handling of approvals. Use the repository for listing approvals and use the engine for handling approvals. The engine also raises events that can be hooked up in IApprovalEngineEvents.

Hooking up events

[InitializableModule]
public class ApprovalLogger : IInitializableModule
{
private IApprovalEngineEvents _approvalEngineEvents;
public void Initialize(InitializationEngine context)
{
_approvalEngineEvents = context.Locate.Advanced.GetInstance();
_approvalEngineEvents.Approved += OnApproved;
}
public void Uninitialize(InitializationEngine context) => _approvalEngineEvents.Approved -= OnApproved; private void OnApproved(ApprovalEventArgs e) => LogManager.GetLogger(typeof(ApprovalLogger)).Debug("Approve"); }

Starting an Approval

A content approval is not started by saving an approval but by saving a content item with SaveAction.RequestApproval. This automatically creates and saves a ContentApproval for this content item, if a definition can be resolved.
IContent content;
IContentRepository contentRepository;

var approvalContentLink = contentRepository.Save(content, SaveAction.RequestApproval);

Aborting an approval

This deletes an approval and raises an Aborted event:

Approval approval;
IApprovalEngine approvalEngine;

await approvalEngine.AbortAsync(approval.ID, "user");

Making a decision

Use the engine to decide to approve or reject a step or the whole approval. There is a possibility to add an optional comment to explain the reason for the decision. If the decision finishes the approval (approves the last step or rejects a step), this comment is saved on the approval as CompletedComment.

Approval approval;
IApprovalEngine approvalEngine;

// Approve a step if user is part of the current definition step
await approvalEngine.ApproveAsync(approval.ID, "user", 1, ApprovalDecisionScope.Step);

// Reject a step if user is part of the current definition step, adding a comment
await approvalEngine.RejectAsync(approval.ID, "user", 1, ApprovalDecisionScope.Step, "This is why I did it");

// Force approve a step whether the user is part of the current definition step or not
await approvalEngine.ApproveAsync(approval.ID, "user", 1, ApprovalDecisionScope.ForceStep);

// Force approve the whole approval 
await approvalEngine.ApproveAsync(approval.ID, "user", 1, ApprovalDecisionScope.Force);
There are also a number of more explicitly named extension methods to the engine that can be used, for example ApproveStepAsync and ForceApproveAsync.

Listing approvals

Use the GetAsync / GetItemsAsync methods on IApprovalRepository to get specific approvals using ID or ContentReference:
using EPiServer.Approvals.ContentApprovals;

ContentReference contentLink; IApprovalRepository approvalRepository; var approval = await approvalRepository.GetAsync(contentLink);

The ListAsync method takes an ApprovalQuery / ContentApprovalQuery object which searches the approvals based on the specified query-data and returns a filtered list. This list can be paged.

This example gets all approvals in review for a user based on a specific definition. 
using EPiServer.Approvals.ContentApprovals;

ContentApprovalDefinition definition; IApprovalRepository approvalRepository; var approvals = await approvalRepository.ListAsync(new ContentApprovalQuery
{
Username = "user",
Status = ApprovalStatus.InReview,
DefinitionID = definition.ID
});
This example list decisions made for an approval:
Approval approval;
IApprovalRepository approvalRepository;

var decisions = await approvalRepository.ListDecisionsAsync(approval.ID);

Comments

Hi,

Does this really work "ApprovalDefinition definition = await definitionRepository.GetAsync(contentLink);"? I have version 10.7.0 and only have Uri and id as parameter options.

#region Assembly EPiServer, Version=10.7.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7
#endregion

using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace EPiServer.Approvals
{
public static class ApprovalDefinitionRepositoryExtensions
{
public static Task DeleteAsync(this IApprovalDefinitionRepository repository, int id);
public static Task<ApprovalDefinition> GetAsync(this IApprovalDefinitionRepository repository, int id);
public static Task<ApprovalDefinition> GetAsync(this IApprovalDefinitionRepository repository, Uri reference);
}
}

Never mind, Realized I need to reference "using EPiServer.Approvals.ContentApprovals;" to get the contenterference overload.