xantari / kontent-management-sdk-net

Kentico Kontent Management .NET SDK

Home Page:https://kontent.ai

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Kentico Kontent Management .NET SDK

Build status Stack Overflow

Package Version Downloads Documentation
Management SDK NuGet NuGet 📖
Content Item Edit-URL Builder NuGet NuGet 📖

Summary

⚠️ Please note that this SDK uses Management API v1 with the exception of the Assets API Endpoints. Those are using the V2 API.

The Kentico Kontent Management .NET SDK is a client library used for managing content in Kentico Kontent. It provides read/write access to your Kentico Kontent projects.

You can use the SDK in the form of a NuGet package to migrate existing content into your Kentico Kontent project or update content in your content items. You can import content items, their language variants, and assets.

The Management SDK does not provide any content filtering options and is not optimized for content delivery. If you need to deliver larger amounts of content we recommend using the Delivery SDK instead.

You can head over to our Developer Hub for the complete Management API Reference.

Prerequisites

To manage content in a Kentico Kontent project via the Management API, you first need to activate the API for the project. See our documentation on how you can activate the Management API.

You also need to prepare the structure of your Kentico Kontent project before importing your content. This means defining the Content types of the items you want to import. You might also need to set up your Languages, Taxonomy or Sitemap locations (if you plan to use them).

Using the ManagementClient

The ManagementClient class is the main class of the SDK. Using this class, you can import, update, view and delete content items, language variants, and assets in your Kentico Kontent projects.

To create an instance of the class, you need to provide a project ID and a valid Management API Key.

ManagementOptions options = new ManagementOptions
{
    ProjectId = "bb6882a0-3088-405c-a6ac-4a0da46810b0",
    ApiKey = "ew0...1eo"
};

// Initializes an instance of the ManagementClient client
ManagementClient client = new ManagementClient(options);

Once you create a ManagementClient, you can start managing content in your project by calling methods on the client instance. See Importing content items for details.

Codename vs. ID vs. External ID

Most methods of the SDK accept an Identifier object that specifies which content item, language variant, or asset you want to perform the given operation on. There are 3 types of identification you can use to create the identifier:

ContentItemIdentifier identifier = ContentItemIdentifier.ByCodename("on_roasts");
ContentItemIdentifier identifier = ContentItemIdentifier.ById(Guid.Parse("8ceeb2d8-9676-48ae-887d-47ccb0f54a79"));
ContentItemIdentifier identifier = ContentItemIdentifier.ByExternalId("Ext-Item-456-Brno");
  • Codenames are generated automatically by Kentico Kontent based on the object's name. They can make your code more readable but are not guaranteed to be unique. Use them only when there is no chance of naming conflicts.
    • Unless set while creating a content item, the codename is initially generated from the item's name. When updating an item without specifying its codename, the codename gets autogenerated based on the name's value.
  • (internal) IDs are random GUIDs assigned to objects by Kentico Kontent at the moment of import/creation. They are unique and generated by the system for existing objects. This means you cannot use them to refer to content that is not imported yet.
  • External IDs are string-based custom identifiers defined by you. Use them when importing a batch of cross-referencing content. See Importing linked content for more details.

Strongly-typed models of your content

The ManagementClient also supports working with strongly-typed models. You can generate strongly-typed models from your content types using the Kentico Kontent model generator utility.

// Elements to update
ArticleModel stronglyTypedElements = new ArticleModel
{
    Title = "On Roasts",
    PostDate = new DateTime(2017, 7, 4)
};

// Upserts a language variant of a content item
ContentItemVariantModel<ArticleModel> response = await client.UpsertContentItemVariantAsync<ArticleModel>(identifier, stronglyTypedElements);

You can also use anonymous objects to achieve the same result:

// Elements to update
var elements = new
{
    title = "On Roasts",
    post_date = new DateTime(2017, 7, 4)
};
ContentItemVariantUpsertModel upsertModel = new ContentItemVariantUpsertModel() { Elements = elements };

// Upserts a language variant of a content item
ContentItemVariantModel<CafeModel> response = await client.UpsertContentItemVariantAsync<CafeModel>(identifier, upsertModel);

However, we encourage you to use strongly-typed models for their convenience and type safety. Examples in this document use strongly-typed models where possible.

Quick start

Importing content items

Importing content items is a 2 step process, using 2 separate methods:

  1. Creating an empty content item which serves as a wrapper for your content.
  2. Adding content inside a language variant of the content item.

Each content item can consist of several localized variants. The content itself is always part of a specific language variant, even if your project only uses one language. See our tutorial on Importing to Kentico Kontent for a more detailed explanation.

1. Creating a content item

// Creates an instance of the Management client
ManagementClient client = new ManagementClient(options);

// Defines the content item to import
ContentItemCreateModel item = new ContentItemCreateModel()
{
    CodeName = "on_roasts", // When not specified, the codename gets autogenerated based on the name's value
    Name = "On Roasts",
    Type = ContentTypeIdentifier.ByCodename("article"),
    SitemapLocations = new[] { SitemapNodeIdentifier.ByCodename("articles") }
};

// Adds the content item to your project in Kentico Kontent
ContentItemModel response = await client.CreateContentItemAsync(item);

Kentico Kontent will generate an internal ID for the (new and empty) content item and include it in the response. If you do not specify a codename, it will be generated based on name. In the next step, we will add the actual (localized) content.

2. Adding language variants

To add localized content, you have to specify:

  • The content item you are importing into.
  • The language variant of the content item.
  • The content elements of the language variant you want to add or update. Omitted elements will remain unchanged.
// Defines the content elements to update
ArticleModel stronglyTypedElements = new ArticleModel
{
    Title = "On Roasts",
    PostDate = new DateTime(2017, 7, 4),
    BodyCopy = @"
        <h1>Light Roasts</h1>
        <p>Usually roasted for 6 - 8 minutes or simply until achieving a light brown color. This method
        is used for milder coffee  varieties and for coffee tasting. This type of roasting allows the natural
        characteristics of each coffee to show. The aroma of coffees produced from light roasts is usually
        more intense.The cup itself is more acidic and the concentration of caffeine is higher.</p>
    ",
    RelatedArticles = new [] { ContentItemIdentifier.ByCodename("which_brewing_fits_you_") },
    UrlPattern = "on-roasts",
    Personas = new [] { TaxonomyTermIdentifier.ByCodename("barista") }
};

// Specifies the content item and the language variant
ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByCodename("on_roasts");
LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US");
ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier);

// Upserts a language variant of your content item
ContentItemVariantModel<ArticleModel> response = await client.UpsertContentItemVariantAsync<ArticleModel>(identifier, stronglyTypedElements);

Importing assets

Importing assets using the Management API is usually a 2-step process:

  1. Uploading a file to Kentico Kontent.
  2. Creating a new asset using the given file reference.

This SDK, however, simplifies the process and allows you to upload assets using a single method:

// Defines the description of an asset
AssetDescription assetDescription = new AssetDescription
{
    Description = "Description of the asset in English Language",
    Language = LanguageIdentifier.ByCodename("en-US")
};

IEnumerable<AssetDescription> descriptions = new [] { assetDescription };

// Title of a new asset
string title = "Asset title";

// Defines the asset to upsert
AssetUpdateModel asset = new AssetUpdateModel
{
    Descriptions = descriptions,
    Title = title
};

string filePath = "‪C:\\Users\\Kentico\\Desktop\\puppies.png";
string contentType = "image/png";

// Uploads the file and links it to a new asset
AssetModel response = await client.CreateAssetAsync(new FileContentSource(filePath, contentType), asset);

Importing linked content

The content you are importing will often contain references to other pieces of imported content. A content item can reference assets, link to other content items in the Linked items or Rich Text element, and contain hypertext links in the rich text editor. To avoid having to import objects in a specific order (and solve problems with cyclical dependencies), you can use external IDs to reference non-existent (not imported yet) content:

  1. Define external IDs for all content items and assets you want to import in advance.
  2. When referencing another content item or asset, use its external ID.
  3. Import your content using the upsert methods with external ID. The system will resolve all references.

This way, you can import your content in any order and run the import process repeatedly to keep your project up to date. In the example below, we import an asset and a content item that uses it:

// Upserts an asset, assuming you already have the fileResult reference to the uploaded file
AssetUpsertModel asset = new AssetUpsertModel
{
    FileReference = fileResult
};

string assetExternalId = "Ext-Asset-123-png";
AssetModel assetResponse = await client.UpsertAssetByExternalIdAsync(assetExternalId, asset);

// Upserts a content item
ContentItemUpsertModel item = new ContentItemUpsertModel
{
    CodeName = "brno", // When not specified, the codename gets autogenerated based on the name's value
    Name = "Brno",
    Type = ContentTypeIdentifier.ByCodename("cafe")
};

string itemExternalId = "Ext-Item-456-Brno";
ContentItemModel itemResponse = await client.UpsertContentItemByExternalIdAsync(itemExternalId, item);

// Upsert a language variant which references the asset using external ID
CafeModel stronglyTypedElements = new CafeModel
{
    Picture = new [] { AssetIdentifier.ByExternalId(assetExternalId) },
    City = "Brno",
    Country = "Czech Republic"
};

ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByExternalId(itemExternalId);
LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US");
ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier);

ContentItemVariantModel<CafeModel> variantResponse = await client.UpsertContentItemVariantAsync<CafeModel>(identifier, stronglyTypedElements);

Content item methods

Upserting a content item by external ID

// Defines a content item to upsert
ContentItemUpsertModel item = new ContentItemUpsertModel
{
    CodeName = "new_or_updated_codename" // When not specified, the codename gets autogenerated based on the name's value
    Name = "New or updated name",
    Type = ContentTypeIdentifier.ByCodename("cafe"),
    SitemapLocations = new[] { SitemapNodeIdentifier.ByCodename("cafes") }
};

string itemExternalId = "Ext-Item-456-Brno";

// Upserts a content item by external ID
ContentItemModel response = await client.UpsertContentItemByExternalIdAsync(itemExternalId, item);

Adding a content item

// Defines a content item to add
ContentItemCreateModel item = new ContentItemCreateModel
{
    CodeName = "brno", // When not specified, the codename gets autogenerated based on the name's value
    Name = "Brno",
    Type = ContentTypeIdentifier.ByCodename("cafe"),
    SitemapLocations = new[] { SitemapNodeIdentifier.ByCodename("cafes") }
};

// Creates a content item
ContentItemModel response = await client.CreateContentItemAsync(item);

Viewing a content item

ContentItemIdentifier identifier = ContentItemIdentifier.ByCodename("brno");
// ContentItemIdentifier identifier = ContentItemIdentifier.ByExternalId("Ext-Item-456-Brno");
// ContentItemIdentifier identifier = ContentItemIdentifier.ById(Guid.Parse("8ceeb2d8-9676-48ae-887d-47ccb0f54a79"));

// Retrieves a content item
ContentItemModel response = await client.GetContentItemAsync(identifier);

Listing content items

// Retrieves a list of content items
ListingResponseModel<ContentItemModel> response = await client.ListContentItemsAsync();

while (true)
{
    foreach (var item in response)
    {
        // use your content item
    }

    if (!response.HasNextPage())
    {
        break;
    }

    response = await response.GetNextPage();
}

Updating a content item

ContentItemIdentifier identifier = ContentItemIdentifier.ByCodename("brno");
// ContentItemIdentifier identifier = ContentItemIdentifier.ById(Guid.Parse("8ceeb2d8-9676-48ae-887d-47ccb0f54a79"));

// Defines new properties of a content item
ContentItemUpdateModel item = new ContentItemUpdateModel
{
    CodeName = "new_codename", // When not specified, the codename gets autogenerated based on the name's value
    Name = "New name",
    SitemapLocations = new[] {
        SitemapNodeIdentifier.ByCodename("cafes"),
        SitemapNodeIdentifier.ByCodename("europe")
    }
};

// Updates a content item
ContentItemModel reponse = await client.UpdateContentItemAsync(identifier, item);

Deleting a content item

ContentItemIdentifier identifier = ContentItemIdentifier.ByCodename("brno");
// ContentItemIdentifier identifier = ContentItemIdentifier.ByExternalId("Ext-Item-456-Brno");
// ContentItemIdentifier identifier = ContentItemIdentifier.ById(Guid.Parse("8ceeb2d8-9676-48ae-887d-47ccb0f54a79"));

// Deletes a content item
client.DeleteContentItemAsync(identifier);

Language variant methods

Upserting a language variant

// Defines the elements to update
CafeModel stronglyTypedElements = new CafeModel
{
    Street = "Nove Sady 25",
    City = "Brno",
    Country = "Czech Republic"
};

ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByCodename("brno");
// ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ById(Guid.Parse("8ceeb2d8-9676-48ae-887d-47ccb0f54a79"));
// ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByExternalId("Ext-Item-456-Brno");

LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US");
// LanguageIdentifier languageIdentifier = LanguageIdentifier.ById(Guid.Parse("00000000-0000-0000-0000-000000000000"));

// Combines item and language identifiers into one
ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier);
// Upserts a language variant of a content item
ContentItemVariantModel<CafeModel> response = await client.UpsertContentItemVariantAsync<CafeModel>(identifier, stronglyTypedElements);

Viewing a language variant

ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByCodename("brno");
// ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ById(Guid.Parse("8ceeb2d8-9676-48ae-887d-47ccb0f54a79"));
// ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByExternalId("Ext-Item-456-Brno");

LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US");
// LanguageIdentifier languageIdentifier = LanguageIdentifier.ById(Guid.Parse("00000000-0000-0000-0000-000000000000"));

// Combines item and language identifiers into one
ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier);
// Retrieves a language variant of a content item
ContentItemVariantModel<CafeModel> response = await client.GetContentItemVariantAsync<CafeModel>(identifier);

Listing language variants

ContentItemIdentifier identifier = ContentItemIdentifier.ByCodename("brno");
// ContentItemIdentifier identifier = ContentItemIdentifier.ById(Guid.Parse("8ceeb2d8-9676-48ae-887d-47ccb0f54a79"));
// ContentItemIdentifier identifier = ContentItemIdentifier.ByExternalId("Ext-Item-456-Brno");

// Retrieves language variants of a content item
IEnumerable<ContentItemVariantModel<CafeModel>> response = await client.ListContentItemVariantsAsync<CafeModel>(identifier);

Deleting a language variant

ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByCodename("brno");
// ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ById(Guid.Parse("8ceeb2d8-9676-48ae-887d-47ccb0f54a79")));
// ContentItemIdentifier itemIdentifier = ContentItemIdentifier.ByExternalId("Ext-Item-456-Brno");

LanguageIdentifier languageIdentifier = LanguageIdentifier.ByCodename("en-US");
// LanguageIdentifier languageIdentifier = LanguageIdentifier.ById(Guid.Parse("00000000-0000-0000-0000-000000000000"));

// Combines item and language identifiers into one
ContentItemVariantIdentifier identifier = new ContentItemVariantIdentifier(itemIdentifier, languageIdentifier);
// Deletes a language variant of a content item
await client.DeleteContentItemVariantAsync(identifier);

Asset methods

Uploading a file

Upload a file to Kentico Kontent.

MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("Hello world from CM API .NET SDK"));
string fileName = "Hello.txt";
string contentType = "text/plain";

// Returns a reference that you can later use to create an asset
FileReference fileResult = await client.UploadFileAsync(new FileContentSource(stream, fileName, contentType));

Upserting an asset using external ID

Update or create an asset using a fileResult reference to a previously uploaded file. You can specify an asset description for each language in your Kentico Kontent project. Specifying title of a new asset is optional. Use the asset title to better identify and filter your assets in the UI.

AssetDescription assetDescription = new AssetDescription
{
    Description = "Description of the asset in English Language",
    Language = LanguageIdentifier.ByCodename("en-US")
};
IEnumerable<AssetDescription> descriptions = new [] { assetDescription };

// Title of a new asset
string title = "Asset title";

// Defines the asset to upsert
AssetUpsertModel asset = new AssetUpsertModel
{
    FileReference = fileResult,
    Descriptions = descriptions,
    Title = title
};

string externalId = "Ext-Asset-123-png";

// Upserts an asset by external ID
AssetModel response = await client.UpsertAssetByExternalIdAsync(externalId, asset);

Uploading an asset from a file system in a single step

Import the asset file and its descriptions using a single method. Specifying title of an asset is optional. You can use the asset title to better identify and filter your assets in the UI.

AssetDescription assetDescription = new AssetDescription
{
    Description = "Description of the asset in English Language",
    Language = LanguageIdentifier.ByCodename("en-US")
};

IEnumerable<AssetDescription> descriptions = new [] { assetDescription };

// Title of a new asset
string title = "Asset title";

// Defines the asset to update
AssetUpdateModel asset = new AssetUpdateModel
{
    Descriptions = descriptions,
    Title = title
};

string filePath = "‪C:\Users\Kentico\Desktop\puppies.png";
string contentType = "image/png";

// Creates a new asset using the given file and its descriptions
AssetModel response = await client.CreateAssetAsync(new FileContentSource(filePath, contentType), asset);

Viewing an asset

AssetIdentifier identifier = AssetIdentifier.ByExternalId("Ext-Asset-123-png");
// AssetIdentifier identifier = AssetIdentifier.ById(Guid.Parse("fcbb12e6-66a3-4672-85d9-d502d16b8d9c"));

// Retrieves an asset
AssetModel response = await client.GetAssetAsync(identifier);

Listing assets

// Retrieves a list of all assets
ListingResponseModel<AssetModel> response = await client.ListAssetsAsync();

while (true)
{
    foreach (var asset in response)
    {
        // use your asset
    }

    if (!response.HasNextPage())
    {
        break;
    }

    response = await response.GetNextPage();
}

Deleting an asset

AssetIdentifier identifier = AssetIdentifier.ByExternalId("Ext-Asset-123-png");
// AssetIdentifier identifier = AssetIdentifier.ById(Guid.Parse("fcbb12e6-66a3-4672-85d9-d502d16b8d9c"));

// Deletes an asset
client.DeleteAssetAsync(identifier);

Helper Methods

Methods for building links to content items and their elements in Kentico Kontent. Available as a separate NuGet package.

Getting an edit link for a content item

var options = new ManagementHelpersOptions
{
    ProjectId = "bb6882a0-3088-405c-a6ac-4a0da46810b0",
};

string itemId = "8ceeb2d8-9676-48ae-887d-47ccb0f54a79";
string languageCodename = "en-US";

var linkBuilder = new EditLinkBuilder(options);
var result = linkBuilder.BuildEditItemUrl(languageCodename, itemId);

// Result is "https://app.kontent.ai/goto/edit-item/project/bb6882a0-3088-405c-a6ac-4a0da46810b0/
// variant-codename/en-US/item/8ceeb2d8-9676-48ae-887d-47ccb0f54a79"

Getting an edit link for a specific content element

var options = new ManagementHelpersOptions
{
    ProjectId = "bb6882a0-3088-405c-a6ac-4a0da46810b0",
};

string itemId = "8ceeb2d8-9676-48ae-887d-47ccb0f54a79";
string languageCodename = "en-US";
var elementIdentifier = new ElementIdentifier(itemId, "single-Element-Codename");

var linkBuilder = new EditLinkBuilder(options);
var result = linkBuilder.BuildEditItemUrl(languageCodename, elementIdentifier);

// Result is "https://app.kontent.ai/goto/edit-item/project/bb6882a0-3088-405c-a6ac-4a0da46810b0/
// variant-codename/en-US/item/8ceeb2d8-9676-48ae-887d-47ccb0f54a79/element/single-Element-Codename"

Getting an edit link for multiple content elements

var options = new ManagementHelpersOptions
{
    ProjectId = "bb6882a0-3088-405c-a6ac-4a0da46810b0",
};

string languageCodename = "en-US";
var elements = new ElementIdentifier[]
{
    new ElementIdentifier("76c06b74-bae9-4732-b629-1a59395e893d", "some-Element-Codename-1"),
    new ElementIdentifier("326c63aa-ae71-40b7-a6a8-56455b0b9751", "some-Element-Codename-2"),
    new ElementIdentifier("ffcd0436-8274-40ee-aaae-86fee1966fce", "some-Element-Codename-3"),
    new ElementIdentifier("d31d27cf-ddf6-4040-ab67-2f70edc0d46b", "some-Element-Codename-4"),
};

var linkBuilder = new EditLinkBuilder(options);
var result = linkBuilder.BuildEditItemUrl(languageCodename, elements);

// Result is "https://app.kontent.ai/goto/edit-item/"
//    project/bb6882a0-3088-405c-a6ac-4a0da46810b0/variant-codename/en-US/
//    item/76c06b74-bae9-4732-b629-1a59395e893d/element/some-Element-Codename-1/
//    item/326c63aa-ae71-40b7-a6a8-56455b0b9751/element/some-Element-Codename-2/
//    item/ffcd0436-8274-40ee-aaae-86fee1966fce/element/some-Element-Codename-3/
//    item/d31d27cf-ddf6-4040-ab67-2f70edc0d46b/element/some-Element-Codename-4"

How to use SourceLink for debugging

This repository is configured to generate SourceLink tag in the Nuget package that allows to debug this repository source code when it is referenced as a Nuget package. Source code is downloaded directly from github to the Visual Studio.

How to configure Source Link

  1. Open a solution with a project referencing the Kentico.Kontent.Delivery (or Kentico.Kontent.Delivery.RX) Nuget package.
  2. Open Tools -> Options -> Debugging -> General.
    • Clear Enable Just My Code.
    • Select Enable Source Link Support.
    • (Optional) Clear Require source files to exactly match the original version.
  3. Build your solution.
  4. Add a symbol server https://symbols.nuget.org/download/symbols
  • Add a symbol server in VS
  1. Run a debugging session and try to step into the Kentico.Kontent.Delivery code.
  2. Allow Visual Studio to download the source code from GitHub.
  • SourceLink confirmation dialog

Now you are able to debug the source code of our library without needing to download the source code manually!

Further information

For more developer resources, visit the Kentico Kontent Developer Hub at https://docs.kontent.ai.

Building the sources

Prerequisites:

Required: .NET Core SDK.

Optional:

Tests

Tests can run against Live endpoint or mocked filesystem. TestUtils.TestRunType specifies target environemnt for tests. Commit always with TestRunType.MockFromFileSystem. For updating mocked data use TestUtils.TestRunType.LiveEndPoint_SaveToFileSystem and before commit update Data directory with the content from \Kentico.Kontent.Management.Tests\bin\Debug\net5.0\Data\. When using TestRunType.MockFromFileSystem, at the build time, data from Data directory are copied to bin and tests are running against mocked data.

For internal maintainers

Note source project's got the Backup environment with the copy of the project. It might be used to restore if the source environment is corrupted. When changing project data, do not forget to update. or even better create a new environment with commit/issue identifier timestamp.

For external contributors

Exported backup of the project created by Template manager is stored in import-package.zip. Note, items, language variants, sitemaps, assets, might be imported with different Ids so it might be needed to update tests.

Creating a new release

Release & version management

Feedback & Contributing

Check out the contributing page to see the best places to file issues, start discussions, and begin contributing.

About

Kentico Kontent Management .NET SDK

https://kontent.ai

License:MIT License


Languages

Language:C# 100.0%