HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideLegal TermsGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Media types and templates

Explains the concept of media types in Optimizely Content Management System (CMS), and describes how to define specialized media types when setting up an Optimizely website.

📘

Note

Media types must first be defined in your content model or from Settings, in order for built-in asset functionality such as file upload or drag-and-drop to work in edit view. It is recommended that you create specific content types for media of type images, video, and anything generic other than video or image, such as text or PDF files.

Media and media types work as follows:

  • A media type defines a set of properties.
  • A media is an instance of the .NET class that defines the media type.
  • When an editor creates or uploads media, values are assigned to the properties defined by the media type.
  • When a visitor requests an instance of media, the default media handler sends the binary data to the visitor.

During initialization, assemblies in the bin folder are scanned for .NET classes decorated with the [ContentType] attribute, and inheriting from MediaData. Metadata for media is defined as properties on the media class, and can be edited from edit view.

Generic media type

You can use the Visual Studio extension to create media types. The example below shows a generic media type for handling files, inheriting from MediaData, with a description property available under the Content tab in edit view.

using System;
using System.ComponentModel.DataAnnotations;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAnnotations;
using EPiServer.Framework.DataAnnotations;

namespace MyOptimizelySite.Models.Media {
  [ContentType(DisplayName = "GenericMedia", GUID = "89761dbb-bf22-4cee-93c7-9d661d75cad8", Description = "Used for generic file types such as Word or PDF documents.")]
  [MediaDescriptor(ExtensionString = "pdf,doc,docx")]
  public class GenericMedia: MediaData {
    [CultureSpecific]
    [Editable(true)]
    [Display(
      Name = "Description",
      Description = "Add a description of the content.",
      GroupName = SystemTabNames.Content,
      Order = 1)]
    public virtual String Description {
      get;
      set;
    }
  }
}

MediaDescriptor attribute

The MediaDescriptor attribute defines a list of file extensions and associates specific file types with a given content type, creating content of the correct type when a user uploads media through the user interface.

By not specifying a list of file extensions, the content type gets mapped towards a wildcard that is valid for file extensions. If you must limit what file extensions can be used, then use the MediaDescriptor attribute to lock down the content types properly.

When you create media content from the server side, you also can have this same content type resolve by using the ContentMediaResolver.

Specialized media types

ImageData and VideoData are specialized base classes distinguishing images and videos from other generic media to apply special handling in edit view. For example, media types inheriting ImageData display image thumbnails when listing images in the Media folder in edit view. ImageData and VideoData inherit from MediaData.

media types inheriting ImageData display image thumbnails when listing images in the Media folder in edit view

Example: Media type for images, inheriting from ImageData, and with properties for copyright and a description.

namespace MyOptimizelySite.Models.Media {
  [ContentType(DisplayName = "ImageFile", GUID = "875b3b51-e0a7-412c-8f56-44f59c184440", Description = "Used for images of different file formats.")]
  [MediaDescriptor(ExtensionString = "jpg,jpeg,jpe,ico,gif,bmp,png")]
  public class ImageFile: ImageData {
    //// <summary>
    /// Gets or sets the copyright.
    /// </summary>
    public virtual string Copyright {
      get;
      set;
    }
    public virtual String Description {
      get;
      set;
    }
  }
}

The UIHint property attribute is used to select editor, renderer, or both by defining a hint string. You can use EPiServer.Web.UIHint for known types in the system, for instance UIHint.Image.

Example: Media type for videos, inheriting from VideoData, and with properties for copyright and link to video preview image.

namespace MyOptimizelySite.Models.Media {
  [ContentType(DisplayName = "VideoFile", GUID = "f2285e5a-be15-47b6-8952-e3c61deaefd2", Description = "Used for specific video file formats.")]
  [MediaDescriptor(ExtensionString = "flv,mp4,webm")]
  public class VideoFile: VideoData {
    /// <summary>
    /// Gets or sets the copyright.
    /// </summary>
    public virtual string Copyright {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the URL to the preview image.
    /// </summary>
    [UIHint(UIHint.Image)]
    public virtual ContentReference PreviewImage {
      get;
      set;
    }
  }
}

ImageDescriptor attribute

The ImageDescriptor attribute automatically generates scaled images. When you route to a BLOB-type property, Optimizely determines whether the BLOB is null and the property has an ImageDescriptor attribute. If both are true, a scaled image is automatically generated from IBinaryStorable.BinaryData.

Example: The ImageData.Thumbnail property with an ImageDescriptor attribute.

/// <summary>
/// Base class for content types which should be handled as images by the system.
/// </summary>
public class ImageData: MediaData {
  /// <summary>
  /// Gets or sets the generated thumbnail for this media.
  /// </summary>
  [ImageDescriptor(Width = 48, Height = 48)]
  public override Blob Thumbnail {
    get {
      return base.Thumbnail;
    }
    set {
      base.Thumbnail = value;
    }
  }
}

Media structure

Media is structured using folders. A folder in the media structure can have other folders or media as children, but a media instance cannot have any children. You can set access rights on folders to control availability for editors.

The following criteria define the media structure:

  • The global media root folder is set as GlobalAssetsRoot, defining media available for content on sites in a multi-site scenario.
  • A site-specific media root folder is set as SiteAssetsRoot, defining media only available for a specific site in a multi-site scenario. In a single-site scenario, the GlobalAssetsRoot and SiteAssetRoot will typically point to the same folder.
  • A folder is an instance of ContentFolder used to structure content. A content folder has no associated rendering and will not display on the site.

See  SiteDefinition  in the Optimizely CMS class library.

Change the maximum upload file size

The maximum size of uploaded files is set on UploadOptions.FileSizeLimit, and the default value is 4 MB. The value can be set (in bytes), for example, from appSettings.json as:

"EPiServer": {
  "CmsUI": {
    "Upload": {
      "FileSizeLimit": 104857600
    }
  }
}

Change the default AllowedList of allowed upload file extensions

You can define UploadOptions.AllowedFileExtensions from the appSetting.js.

  • type – string
  • default value – *
"EPiServer": {  
  "CmsUI": {  
    "Upload": {  
      //example of the AllowedList  
      "AllowedFileExtensions": "jpg,jpeg,jpe,ico,gif,bmp,png,tiff",  
    }  
  }  
}

Customize the media handler

There is a built-in endpoint template registered for media requests. However, media uses the same templating system as other content types in CMS,  which means you can use any template for media; you are not limited to an endpoint that delivers media as binary data.

If you need to extend the default media handling, for example, to set response headers, register an implementation of IStaticFilePreProcessor.

Example: Below is an example of a custom pre-processor that adds a header to the response for PDF requests.

public class PdfStaticFilePreProcessor: IStaticFilePreProcessor {
  //The built-in pre processor that handles MediaOptions has order 0, run after that
  public int Order => 10;

  public void PrepareResponse(StaticFileResponseContext staticFileResponseContext) {
    if (staticFileResponseContext.Context.Response.ContentType == "application/pdf") {
      staticFileResponseContext.Context.Response.Headers.Add("Content-Disposition", "attachment");
    }
  }
}

The static file pre-processor is registered using MediaOptions, like:

services.Configure<MediaOptions>(o => o.AddPreProcessor<PdfStaticFilePreProcessor>());

You can also add a custom media template. If the template is based on an endpoint, then it is necessary to add a ContentActionDescriptor to the endpoint metadata. It is also recommended to add the policies for CmsPolicyNames.Read and CmsPolicyNames.Preview to ensure access rights are checked.

Example: Shows a custom endpoint template for PDF files that adds a header to the response.

[TemplateDescriptor(Inherited = true, TemplateTypeCategory = TemplateTypeCategories.HttpHandler)]
public class PdfEndpoint: Endpoint, IRenderTemplate<PdfFile> {
  private readonly static ContentActionDescriptor _mediaContentActionDescriptor = new ContentActionDescriptor {
    Inherited = true, ModelType = typeof (PdfFile)
  };
  private static readonly AuthorizeAttribute _authorizeAttribute = new AuthorizeAttribute(CmsPolicyNames.Read);
  private static readonly AuthorizeAttribute _previewAttribute = new AuthorizeAttribute(CmsPolicyNames.Preview);

  public PdfEndpoint(IBlobHttpHandler blobHttpHandler): base(context => ProcessRequest(blobHttpHandler, context),
    new EndpointMetadataCollection(_mediaContentActionDescriptor, _authorizeAttribute, _previewAttribute), nameof(PdfEndpoint)) {}

  private static Task ProcessRequest(IBlobHttpHandler blobHttpHandler, HttpContext context) {
    context.Response.Headers.Add("Content-Disposition", "attachment");
    return blobHttpHandler.Invoke(context);
  }
}