Example: How to create a contentitem (Reusable) in code (for deployment in NuGet)

2025/12/05 8:03 AM

I could't find a working example on how to create a reusable contentitem online. It was something I needed to include in a NuGet package. I think this might be helpfull for others.

First create your contenttype in the UI, so you can generate the class, and have the CI file as a reference.

Next implement a class like the example below. To have your new contenttype created, add a module, and call your installer using ApplicationEvents.Initialized.Execute.

using CMS.ContentEngine;
using CMS.DataEngine;
using CMS.FormEngine;

namespace MyProject.FaqContent.Admin;

internal interface IFaqContentInstaller
{
    void Install();
}

internal class FaqContentInstaller(IContentTypeManager contentTypeManager) : IFaqContentInstaller
{
    private const string DISPLAY_NAME = "FAQ";

    public void Install()
    {
        EnsureContentType();
    }

    private void EnsureContentType()
    {
        if (ContentTypeExists())
        {
            return;
        }

        var contentType = ContentTypeInfo.New(ContentTypeInfo.OBJECT_TYPE_CONTENTTYPE);

        contentType.ClassName = FaqContent.CONTENT_TYPE_NAME;
        contentType.ClassDisplayName = DISPLAY_NAME;
        contentType.ClassTableName = FaqContent.CONTENT_TYPE_NAME.Replace(".", "_");
        contentType.ClassType = ClassType.CONTENT_TYPE;
        contentType.ClassContentTypeType = ClassContentTypeType.REUSABLE;
        contentType.ClassWebPageHasUrl = false;
        contentType.ClassHasUnmanagedDbSchema = false;
        contentType.ClassIconClass = "xp-question-circle";
        contentType.ClassShortName = "FaqContent";

        contentTypeManager.Initialize(contentType);

        var formInfo = GetClassFormInfo(contentType.ClassFormDefinition, GetFormFields());
        contentType.ClassFormDefinition = formInfo.GetXmlDefinition();

        contentType.Insert();

        contentTypeManager.CreateTableStructures(contentType.ClassTableName);
    }

    private static FormInfo GetClassFormInfo(string contentTypeDefinition, IEnumerable<FormFieldInfo> fields)
    {
        var formInfo = new FormInfo(contentTypeDefinition);

        foreach (var field in fields)
        {
            formInfo.AddFormItem(field);
        }
        return formInfo;
    }

    private static IEnumerable<FormFieldInfo> GetFormFields()
    {
        var fields = new List<FormFieldInfo>();

        // Add FaqContentQuestion field (Text, Required)
        var questionField = new FormFieldInfo
        {
            Name = "FaqContentQuestion",
            AllowEmpty = false,
            DataType = FieldDataType.Text,
            Size = 200,
            Enabled = true,
            Visible = true,
            Caption = "Resource Key",
            DefaultValue = string.Empty,
        };
        QuestionField.SetPropertyValue(FormFieldPropertyEnum.FieldCaption, "Question");
        QuestionField.SetPropertyValue(FormFieldPropertyEnum.FieldDescription, "Enter the question");
        QuestionField.SetPropertyValue(FormFieldPropertyEnum.ExplanationTextAsHtml, "False");
        QuestionField.SetPropertyValue(FormFieldPropertyEnum.FieldDescriptionAsHtml, "False");
        QuestionField.Settings["controlname"] = "Kentico.Administration.TextInput";
        fields.Add(questionField);

        // Add FaqContentText field (Long Text, Required)
        var textField = new FormFieldInfo
        {
            Name = "FaqContentAnswer",
            AllowEmpty = false,
            DataType = FieldDataType.LongText,
            Enabled = true,
            Visible = true,
            Caption = "Localized Text",
            DefaultValue = string.Empty
        };
        textField.SetPropertyValue(FormFieldPropertyEnum.FieldCaption, "Answer");
        textField.SetPropertyValue(FormFieldPropertyEnum.FieldDescription, "Give the answer");
        textField.SetPropertyValue(FormFieldPropertyEnum.ExplanationTextAsHtml, "False");
        textField.SetPropertyValue(FormFieldPropertyEnum.FieldDescriptionAsHtml, "False");
        textField.Settings["controlname"] = "Kentico.Administration.TextArea";
        textField.Settings["MinRowsNumber"] = "3";
        textField.Settings["MaxRowsNumber"] = "5";
        fields.Add(textField);

        return fields;
    }

    internal bool ContentTypeExists()
    {
        var contentType = DataClassInfoProvider.GetDataClassInfo(FaqContent.CONTENT_TYPE_NAME);
        return contentType != null;
    }

}

Environment

  • Xperience by Kentico

Answers

2025/12/05 2:36 PM

Yup, this is what we did a lot at the beginning (my Baseline has the same thing to create it's classes), there are some nuances that you need to be careful for (like making sure if it exists you don't overwrite it, or pulling in 'new' custom fields before saving in case the user ever wants to customize it), however there is a newer way using the CI/CD processes. I haven't done it myself, Sean may need to chime in or one of the others...

2025/12/05 7:43 PM

The approach shown above looks correct, to the extent that it accomplishes what you want. While the APIs are supported, this isn't a scenario we designed them for.

We don't have a way to "package" content and object types in NuGet packages at the moment, but it's something we've prototyped internally. The idea would include using the CI process to "adopt" schema changes, defined in CI .xml files embedded in NuGet packages, by running a command before starting up your app.

But, since we don't actually have this feature yet, you can give us some feedback about your scenarios on our roadmap if it would help you.

To response this discussion, you have to login first.