Get Composer content from another page as a control

Today I needed to display some Composer content from one page on another page in EPiServer, a task not to easy to accomplish when I started to dig into it.

So I came up with a function that takes three arguments, PageData page, Page thePage, string ContentAreaId (The contentAreaId is optional since I used it mostly for a Composer area named MainArea). The function then returns a Control with the content from the ContentAreaId.

        public static Control GetRenderedComposerContent(PageData page, Page thePage, string ContentAreaId = "MainArea")
        {
            Control renderControl = new Control();
            ExtensionPageData structure = PageDataManager.LoadPageStruct(page.PageLink);
            ContentAreaData contentArea = structure.GetContentAreaById(ContentAreaId);
            if (contentArea != null)
            {
                for (var i = 0; i < contentArea.ContentFunctions.Count; i++)
                {
                    ContentFunctionData data = contentArea.ContentFunctions[i];
                    BaseContentFunction contentFunction = PageDataManager.InitContentFunctionControl(ExtensionGeneric.ViewMode.ExtensionNormalMode, thePage, data);
                    renderControl.Controls.Add(contentFunction);
                    Development.UI.Hardox.Masterpages.Hardox.IListItemPosition element = contentFunction as Development.UI.Hardox.Masterpages.Hardox.IListItemPosition;
                    if (element == null)
                    {
                        continue;
                    }
                    element.IsLastElement = IsLastElement(i, contentArea);
                    element.IsFirstElement = IsFirstElement(i);
                }
            }
            return renderControl;
        }

        private static bool IsFirstElement(int i)
        {
            return i == 0;
        }
        private static bool IsLastElement(int i, ContentAreaData contentArea)
        {
            return i == contentArea.ContentFunctions.Count - 1;
        }

I used this function to generate a page with tabs and all the content is rendered on Page_Load with all the content from different pages using a repeater.

So Default.aspx have the following code, notice the placeholder that I use in the repeater to inject the Composer control into.

    <asp:Repeater runat="server" ID="TabsContent" OnItemDataBound="repeaterDataBound">
        <HeaderTemplate>
            <ul>
        </HeaderTemplate>
        <ItemTemplate>
            <li>
                <asp:PlaceHolder runat="server" ID="TheContent"></asp:PlaceHolder>
            </li>
        </ItemTemplate>
        <FooterTemplate>
            </ul>
        </FooterTemplate>
    </asp:Repeater>

Code behind for Default.aspx (Default.aspx.cs):

        protected void repeaterDataBound(object source, RepeaterItemEventArgs e)
        {
            if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType != ListItemType.AlternatingItem)
            {
                return;
            }
            PageData p = e.Item.DataItem as PageData;
            e.Item.FindControl("TheContent").Controls.Add(GetRenderedComposerContent(p, Page));
        }

PTB and ImageVault

Today I had a problem using a ImageVaultproperty with PageTypeBuilder.

Earlier I used EPiServers own image handling for every property but this client wanted to use ImageVault instead.

First I just changed the typeof(PropertyImageUrl) to PropertyImageVaultFile but that didnt work so I also changed the return type from string to int since PropertyImageVaultFile returns the ID of the selected file. So in short:

From:

        [PageTypeProperty(
            EditCaption = "Image",
            HelpText = "Sets the puffs image",
            UniqueValuePerLanguage = true,
            Searchable = false,
            SortOrder = 125,
            Type = typeof(PropertyImageUrl),
            Tab = typeof(ComposerTab))]
        public virtual string ImageUrl { get; set; }

To:

        [PageTypeProperty(
            EditCaption = "Image",
            HelpText = "Sets the puffs image",
            UniqueValuePerLanguage = true,
            Searchable = false,
            SortOrder = 125,
            Type = typeof(PropertyImageVaultFile),
            Tab = typeof(ComposerTab))]
        public virtual int ImageUrl { get; set; }

So what I get in return from that property is the Id of the image and then I just used the following code to create a IVFileData object, to note here is that I using Composer aswell and the ContentFunctionData[string PropertyName] is always an object if the property exists, so a additional null check is needed before we try to create and IVFileData object from the property. Since the ContentFunctionData[string PropertyName] returns a string for the value, I needed to parse the string aswell.

        public static IVFileData GetIVFileDataFromData(ContentFunctionData content, string PropertyName = "Image")
        {
            if (content != null && content[PropertyName] != null) 
            {
                string stringFileId = content[PropertyName].ToString();
                int fileId;
                if (int.TryParse(stringFileId, out fileId))
                {
                    IVFileData image = IVDataFactory.GetFile(fileId);
                    return image;
                }
            }
            return null;
        }