Retrieve content items

The Xperience API allows you to work with content items using the content query API and one of the following approaches:

  • Generated content type classes (recommended) – classes generated by the system that allow you to work with content type fields using strongly-typed objects. These classes also allow access to all general data (title, ID, GUID, creation date, publish date, etc.) as well.
  • Custom data transfer objects (advanced use case) – using custom classes grants you control over the data you map. Before using custom classes, make sure you are familiar with how the mapper API behaves. Also note that some Xperience APIs that work with content items expect certain system fields to be present in the model and will not work as expected otherwise.

For more information about content querying and available parametrization, see Content item API and Reference - Content item query.

Retrieve content items

To retrieve content items, use the content query APIContentItemQueryBuilder and IContentQueryExecutor (available in the CMS.ContentEngine namespace) together with generated classes for individual content types. The generated content type classes allow you to work with strongly typed objects and easily access their fields.

C#
Retrieve content items


// An instance of required services (e.g., obtained using dependency injection)
private readonly IContentQueryExecutor executor;

private async Task SampleContentQuery()
{
    // Configures the query builder to select 3 banners
    var builder = new ContentItemQueryBuilder()
                        .ForContentType(
                            "My.Banner",
                            config => config
                                .TopN(3)
                                .WithLinkedItems(1)
                        ).InLanguage("en");

    // Executes the configured query
    IEnumerable<Banner> banners = await executor.GetMappedResult<Banner>(builder);

    // Accesses the content item data
    foreach (Banner item in banners)
    {
        // Accesses content type fields
        string header = item.BannerHeaderText;

        // Accesses system fields
        VersionStatus status = item.SystemFields.ContentItemCommonDataVersionStatus;
    }
}

Filter content items based on tags

You can limit the query to retrieve content items with any of the specified tags using the WhereContainsTags parametrization method of the ContentItemQueryBuilder.

C#
WhereContainsTags

// A collection of tags, e.g., obtained from a Tag selector
IEnumerable<Guid> tagIdentifiers;

var builder = new ContentItemQueryBuilder()
    .ForContentType(
        "Some.Type",
        subqueryParameters =>
            // Retrieves items with the specified tags
            subqueryParameters.Where(where =>
                where.WhereContainsTags("SomeTaxonomy", tagIdentifiers))
    ).InLanguage("en");

You can also create a TagCollection object and pass it as an argument of the WhereContainsTags parametrization method. The TagCollection object then represents a collection of all tags specified as the input and any tags that are children of the specified tags. This is useful when you need to retrieve all items that belong in a subtree of a taxonomy.

C#
WhereContainsTags

// A collection of tags, e.g., obtained from a Tag selector
IEnumerable<Guid> tagIdentifiers;
var tagCollection = await TagCollection.Create(tagIdentifiers);

var builder = new ContentItemQueryBuilder()
    .ForContentType(
        ArticlePage.CONTENT_TYPE_NAME,
        subqueryParameters =>
            // Retrieves items with the specified tags and any child tags
            subqueryParameters.Where(where =>
                where.WhereContainsTags("SomeTaxonomy", tagCollection))
    ).InLanguage("en");
    

The retrieval API is designed to work with data from the Tag selector editing component, which is a collection of identifiers (IEnumerable<Guid>).

  • To convert a collection of identifiers to a collection of tags, you can use the RetrieveTags method of the ITaxonomyRetriever interface.

    C#
    RetrieveTags
    
      // Service obtained via dependency injection
      private readonly ITaxonomyRetriever taxonomyRetriever;
    
      // A collection of tag GUIDs
      IEnumerable<Guid> tagIdentifiers;
    
      // Retrieves a collection of Tag objects
      IEnumerable<Tag> tags = await taxonomyRetriever.RetrieveTags(tagIdentifiers, "en");
      
  • To convert a collection of Tag objects to a collection of identifiers, you can use the Select LINQ method.

    C#
    Select LINQ method
    
      // A collection of Tag objects
      IEnumerable<Tag> tags;
    
      // Retrieves a collection of tag identifiers
      IEnumerable<Guid> tagIdentifiers = tags.Select(tag => tag.Identifier);
      

Using logical operators with tags

You can use the And() and Or() logical operators to combine multiple conditions. For example, the following code snippet shows a content item query that retrieves items that contain both the first and the second tag from the provided list of tags at the same time:

C#
WhereContainsTags

// A collection of tags, e.g., obtained from a Tag selector
IEnumerable<Guid> tagIdentifiers;

var builder = new ContentItemQueryBuilder()
    .ForContentType(
        "Some.Type",
        subqueryParameters =>
            // Retrieves items with both specified tags
            subqueryParameters.Where(where =>
                where.WhereContainsTags("SomeTaxonomy",
                            new List<Guid> { tagIdentifiers.ElementAt(0) })
                     .And()
                     .WhereContainsTags("SomeTaxonomy",
                            new List<Guid> { tagIdentifiers.ElementAt(1) }))
    ).InLanguage("en");

Retrieve content items from smart folders

Smart folders give content editors the power to select a specific set of content items. This is achieved by configuring filter conditions, such as “items published in the last 7 days”, “items with the Acme tag”, etc.

For smart folders with Dynamic content delivery enabled, you can use the content item query API with the InSmartFolder parametrization method to retrieve the content items that match a folder’s filter conditions, and then display or otherwise use them in your channels. This allows content editors to control which items are delivered directly in the Content hub UI, without needing to adjust the code.

The InSmartFolder method requires you to specify the smart folder by its ID, GUID or code name identifier. To get the identifier, we recommend using fields with the Smart folder selector UI form component. The smart folder selector is supported in the following scenarios:

You can also find the identifiers of smart folders manually in the Content hub application – expand the menu actions of a folder and select Properties.

C#
Retrieve content items from a smart folder

var smartFolderGuid = model?.Properties?.SmartFolderSelectorField.Identifier ?? Guid.Empty;

var builder = new ContentItemQueryBuilder()
        .ForContentTypes(parameters =>
            // Retrieves content items from the specified smart folder
            parameters.InSmartFolder(smartFolderGuid)
        )
        .Parameters(parameters =>
            // Orders the items by their publish date and limits the number of items to 5
            parameters.OrderBy(new OrderByColumn("ContentItemCommonDataLastPublishedWhen", OrderDirection.Descending))
                      .TopN(5))
        .InLanguage("en");

Setting the order and maximum number of retrieved items is not part of the options configured for the smart folder in the administration UI. If required, control these parameters using the OrderBy and TopN query parametrization.

The InSmartFolder parametrization causes the query to return an empty result if the specified smart folder:

  • Doesn’t exist
  • Doesn’t have dynamic content delivery enabled
  • Has invalid filter conditions (for example if a tag saved in the Taxonomy filter option was later deleted)

Using multiple InSmartFolders calls in a single query is not supported and results in an exception.

Because smart folders may contain items of multiple content types, the InSmartFolder method is only available in the ForContentTypes parametrization. If you need to ensure that only items of one specific content type are retrieved (regardless of the smart folder’s filter condition), add OfContentType to the parametrization.

C#
Retrieve items of a specific content type from a smart folder

var builder = new ContentItemQueryBuilder()
        .ForContentTypes(parameters =>
            // Retrieves items of one specific content type from a smart folder
            parameters.InSmartFolder(smartFolderGuid)
                      .OfContentType("Sample.Type")
        ).InLanguage("en");

Smart folder content and language fallbacks

Language fallbacks are not used when retrieving content items from a smart folder in a specific language.

For example, if you have Spanish configured to fall back to English, and you retrieve content from a smart folder in Spanish, items are not included if their Spanish language variant doesn’t fulfill the folder’s filter conditions (even if their English variant does).

Cache data retrieved from smart folders

Caching the data of retrieved content items is recommended in most cases. However, smart folder conditions are set in the administration UI by content editors and are evaluated dynamically. Items move in and out of smart folders as their content and metadata changes. This makes it challenging to ensure that cached data does not become outdated.

The most practical approach is to set a reasonable short expiration time for cached data that is retrieved from a smart folder, depending on how often your project’s content editors adjust content items and smart folder filter criteria.

In scenarios where you are retrieving one specific content type, you can also set a cache dependency on all items of the given content type, which ensures that the cache is cleared whenever any items of the type are updated.

C#
Cache data from a smart folder


// Instances of required services (e.g., obtained using dependency injection)
private readonly IContentQueryExecutor executor;
private readonly IProgressiveCache progressiveCache;

// ...

// Sets the cache expiration time to 30 minutes
var cacheSettings = new CacheSettings(cacheMinutes: 30,
                                      cacheItemNameParts: new[] { "SmartFolderItems", smartFolderGuid.ToString() });

// Adds a cache dependency on all reusable content items of the 'Sample.Type' content type
cacheSettings.CacheDependency = CacheHelper.GetCacheDependency("contentitem|bycontenttype|Sample.Type");

// Caches the loaded data
var cachedContentItems = await progressiveCache.LoadAsync(async (cacheSettings) =>
{
    // Prepares the query for retrieving content items from a smart folder
    var builder = new ContentItemQueryBuilder()
        .ForContentTypes(parameters =>
            parameters.InSmartFolder(smartFolderGuid)
                      .OfContentType("Sample.Type")
        )
        .Parameters(parameters =>
            parameters.OrderBy(new OrderByColumn("ContentItemCommonDataLastPublishedWhen", OrderDirection.Descending))
                      .TopN(5))
        .InLanguage("en");

    // Executes the configured query
    return await executor.GetMappedResult<SampleType>(builder);
}, cacheSettings);

Content item security

Content items can be secured to allow access only for authenticated users. You can filter out secured content items during retrieval by passing the appropriate bool value to the IncludeSecuredItems property of the ContentQueryExecutionOptions and providing them to the query executor.

C#
Retrieve secured content items


// Services obtained via dependency injection
private readonly IContentQueryExecutor executor;

// Information about whether to include secured items in the query execution passed from the caller
public async Task ContentItemRetrieval(bool includeSecuredItems)
{
    // Configures the query builder
    var builder = new ContentItemQueryBuilder()...;

    // Configures the query options for the query executor
     var queryOptions = new ContentQueryExecutionOptions()
    {
         IncludeSecuredItems = includeSecuredItems
    };

    // Executes the query and stores the data in generated 'Banner' models
    IEnumerable<Banner> banners = await executor.GetMappedResult<Banner>(
                                                builder: builder,
                                                options: queryOptions);
}

The secured state of a retrieved content item is indicated by its item.SystemFields.ContentItemIsSecured property. You can use this property to display information to visitors accordingly.

For example, you can pass the content item’s ContentItemIsSecured property to a model and display an appropriate message to visitors. 

cshtml
Reflect content item security in views


@model CompanyName.Models.MyModel

// In this case, 'ContentItemIsSecured' is mapped to the 'IsSecured' property of the view model 
@if (model.IsSecured && !User.Identity.IsAuthenticated)
{
    <p>
        Sign in to view this content.
    </p>
}

Both pages and content items use the same property to determine their security configuration. When working with collections of linked content items (provided, e.g., via a selection UI managed by the combined content selector UI form component), you can determine which content items are marked as secured via simple projection.

C#
Determine authentication requirements


// Contains a collection of sample 'Clinic' content items - retrieval code omitted for brevity
var clinic = await GetMedicalClinics();

var securedClinics = clinics.Select(x => x.SystemFields.ContentItemIsSecured);

Content item names

There are several name fields related to each content item that represent various names used throughout the system:

  • Code name – a unique identifier of the content item, used primarily in code. Code names are stored in the ContentItemName property of each content item.
  • Display name – the name displayed for the item in the administration interface. Display names are not available via the API, use a Title field instead.
  • Title field – a custom field that we recommend you to add to your content types. The Title field represents the name which is used when displaying the content item in the presentation layer.

Date and time fields

DateTime fields and system properties of retrieved content items always have values in the time zone of the server where the application is running. If you wish to display values in a different time zone (e.g., in a website visitor’s local time), perform a time conversion using the standard .NET API.

Retrieve linked content items

To retrieve a list of content items linked to a page or another content item:

  1. Retrieve the object representing the content item or page containing linked content items using the content query API.
  2. Retrieve linked content items by accessing content item fields of the retrieved object.
C#
Retrieve linked content items


// An instance of required services (e.g., obtained using dependency injection)
private readonly IContentQueryExecutor executor;

private async Task SampleContentQuery()
{
    // Configures the query builder to select 3 banners
    var builder = new ContentItemQueryBuilder()
                        .ForContentType(
                            "My.Banner",
                            config => config
                                .TopN(3)
                                .WithLinkedItems(1)
                        ).InLanguage("en");

    // Executes the configured query
    IEnumerable<Banner> banners = await executor.GetMappedResult<Banner>(builder);

    // Accesses the content item data
    foreach (Banner item in banners)
    {
        // Accesses linked content item
        Image image = item.BannerBackgroundImage.FirstOrDefault();

        // Accesses content type fields of the linked item
        string description = image.ImageShortDescription;

        // Accesses system fields of the linked item
        VersionStatus status = image.SystemFields.ContentItemCommonDataVersionStatus;
    }
}

You can use the WithLinkedItems method to ensure that the linked items are loaded within the same database query as the retrieved content item. The maxLevel parameter controls the depth to which linked items are retrieved.

Retrieve assets

To retrieve information about an asset (e.g., filename, file size, URL) from a content item that has an asset field:

  1. Retrieve the object representing the content item or page containing linked content items using the content query API
  2. Retrieve a ContentItemAsset object from the asset field and access its properties.
C#
Retrieve content item assets


// An instance of required services (e.g., obtained using dependency injection)
private readonly IContentQueryExecutor executor;

private async Task SampleContentQuery()
{
    // Configures the query builder to select 3 banners
    var builder = new ContentItemQueryBuilder()
                        .ForContentType(
                            "My.Banner",
                            config => config
                                .TopN(3)
                                .WithLinkedItems(1)
                        ).InLanguage("en");

    // Executes the configured query
    IEnumerable<Banner> banners = await executor.GetMappedResult<Banner>(builder);

    // Accesses the content item data
    foreach (Banner item in banners)
    {
        // Accesses linked content item
        Image image = item.BannerBackgroundImage.FirstOrDefault();

        // Accesses the asset object in the ImageFile property of the Image content type
        ContentItemAsset asset = image.ImageFile;

        // Accesses properties of the asset
        string extension = asset.Metadata.Extension;
        string url = asset.Url;
    }
}

Secured assets

Content item assets that are configured to require authentication return HTTP 403 (Forbidden) when requested by unauthenticated visitors. In such cases, consider displaying a placeholder teaser image instead. For example, a popular practice is to display a blurred teaser image overlaid with a lock icon.

Retrieve content items for preview

If you need to retrieve content items in their latest available version regardless of the workflow state and regardless whether they are secured or not, configure the ContentQueryExecutionOptions and provide them to the query executor.

C#
Retrieve content items for preview


// Services obtained via dependency injection
private readonly IContentQueryExecutor executor;

// Information about whether to include secured items in the query execution passed from the caller
public async Task SampleContentQuery(bool includeSecuredItems)
{
    // Configures the query builder
    var builder = new ContentItemQueryBuilder()...;

    // Configures the query options for the query executor
    var queryOptions = new ContentQueryExecutionOptions();
    {
        ForPreview = true,
        IncludeSecuredItems = includeSecuredItems
    };

    // Executes the query and stores the data in generated 'Banner' models
    IEnumerable<Banner> banners = await executor.GetMappedResult<Banner>(
                                                builder: builder,
                                                options: queryOptions);

}