如何在 Ektron 中显示 SmartForms 列表

How do I show a list of SmartForms in Ektron

如何在 Ektron 版本:9.00 SP3(内部版本 9.0.0.249)中显示使用 SmartForm 创建的项目列表?

我创建了一个 SmartForm,用户可以在其中输入可用职位的详细信息。现在我想在列表中显示这些职位,即可用的职位。然后,当他们单击 link 时,系统会将他们带到该工作的详细信息。

我对 Ektron 的经验为零,所以我希望这不是一个愚蠢的问题,我需要将内容分解到非常基本的水平。

编辑: 您可以在 using strongly typed objects with Ektron Smart Forms at Thomas Higginbotham's blog.

上找到此信息和更多 in-depth 信息

POST:

我在下面创建的示例用于输出列表中的图像,就好像我要制作一个画廊一样。请记住,在 实际上 创建画廊时我还会做其他事情,因此这绝不是这方面的完整代码。然而,这是一个非常好的和简单的说明,说明如何以尽可能最 .NET 的方式从 Ektron 检索和处理内容(没有更多的关注点分离)。

首先,我有一些推荐读物给你。 Ken McAndrew(long-time Ektron 开发人员和现在的 Sitecore 开发人员)将 a nice set of posts about a class you can use to obtain Ektron's Smart Form content as .NET objects 放在一起,使它们更易于使用。我建议使用 Ken 的 class 版本(link 在博客 post 中)。

还有 a hangout featuring Bill Cava, original author of the method, Ken, and myself 谈论这种方法,这可能会有所帮助。

我看了看,我认为原来的网络研讨会已被删除。

无论如何,一旦您准备好内容类型代码,它就相对简单了。你想要:

  1. 为您的智能表单创建 .NET 模型
  2. 获取物品
  3. 将它们映射到 ViewModel(可选)
  4. 将它们数据绑定到 ListView 或 Repeater 控件

为智能表单创建 .NET 模型

首先,我为新闻照片创建了一个相当简单的智能表单。它包含三个字段:

  1. 姓名
  2. 描述(标题)
  3. 图像(为简单起见存储为 URL 字符串,而不是 <img /> 标签)

接下来,单击 SmartForm 数据设计视图中的“XSD”按钮(add/manage 字段)并复制所有 XSD 代码。 (您会在下面的屏幕截图中看到 XSD 按钮,就在模态上方。)

将其保存到文件中。我在 C:\ContentTypes 创建了一个文件夹,并将文件添加为 PressPhoto.xsd

您需要 运行 file called XSD.exe(由 Microsoft 作为大多数 Visual Studio 安装的一部分提供和记录)。这将使用 XSD 并为您生成一个 C# class 文件 - 一个有助于将智能表单 XML 反序列化为 .NET object.

的文件

在这里可以找到它(包含在 Visual Studio 2015 中):

但是,运行从命令行启动它是一件令我痛苦的事情,因为我总是忘记它的相当冗长的路径。所以我创建了一个批处理文件来为我完成大部分脏活。我写的是接受三个参数,顺序是:

  1. XSD 文件的路径
  2. 我想存储生成的 class 文件的输出路径
  3. 我想要为生成的 class 命名空间(提示:我总是将命名空间设置为包含智能表单的名称 - 如果您为多个生成 classes,这可以防止冲突智能表格)

这是执行它的代码和命令的屏幕截图。

@ECHO OFF

SET xsdExePath="C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe"
SET xsdFilePath="%~f1"
SET outFolderPath="%~f2"
SET ns=%3

%xsdExePath% /c %xsdFilePath% /o:%outFilePath% /l:CS /n:%ns%

这会在我的 C:\ContentTypes\ 目录中生成一个名为 PressPhoto.cs 的文件。

进入我的 Ektron 项目,我将在我的 App_Code 目录中添加 Ken 的 SmartForm class(在他的博客中引用)和我的新 PressPhoto class文件夹。像这样(App_Code/CSCode 下的文件夹结构由您决定,但为了您自己的理智,请保持井井有条):

为每个要通过 API 提取的智能表单添加一个 class(对我来说,就是 所有 智能表单)。

我知道这似乎需要大量准备工作,与其他一些更现代的系统相比,确实如此。但是一旦你养成习惯,这将对你有很大帮助。

获取项目(并映射到 ViewModel)

现在您基本上已经创建了自己的 APIs 来获取项目(替代方法是 XML 转换,顺便说一句),您可以使用它们了。

我更喜欢创建自己的“管理器”class 以及 ViewModel。如果您更喜欢不同的方法,那么这至少会为您提供示例代码,让您朝着自己的方向前进。

我正在对代码发表评论 in-line 以便您可以在上下文中阅读它们。

查看模型 - 非常基础

namespace MyProject.ViewModels
{
    /// <summary>
    /// Provides the fields necessary to display a PressPhoto Smart Form to the site.
    /// </summary>
    public class PressPhotoViewModel
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public string ImageUrl { get; set; }
        public string ContentUrl { get; set; }
        public long ContentId { get; set; }
        public PressPhotoViewModel()
        {

        }
    }
}

和“经理”class 所以我在 code-behinds 中放入了尽可能少的代码。我所有 PressPhoto 逻辑的一个很好的集中位置。

using Ektron.Cms;
using Ektron.Cms.Content;
using Ektron.Cms.Common;
using Ektron.Cms.Framework.Custom;
using MyProject.SmartForms.PressPhoto;
using MyProject.ViewModels;
using System.Collections.Generic;
using System.Linq;

namespace MyProject.Managers
{
    /// <summary>
    /// Provides CRUD operations for managing PressPhoto objects within the CMS.
    /// </summary>
    public class PressPhotoManager
    {
        /* 
         * "root" is the default root element of the Smart Form XML. 
         * 
         * If you've changed this, your code will be different. Most people don't, 
         * so I'm going with the lowest-common-denominator example.
         * 
         * I normally set the "root" to something else, and thus give it a more 
         * meaningful class name. I also tend to load this manager via something more
         * similar to a singleton, but for simplicity's sake...
         */
        private SmartFormManager<root> pressPhotoManager = new SmartFormManager<root>();

        public PressPhotoManager()
        {
            // Nothing needs done here for this example.
        }

        /*
         * Usually, I'm not interested in writing information back to the DB, so
         * I'm only going to include samples for GetItem and GetList (by folder) here.
         */

        /// <summary>
        /// Retrieves a PressPhoto item by its Ektron Content ID
        /// </summary>
        /// <param name="ContentId">Ektron Smart Form Content Id</param>
        /// <returns>Press Photo ViewModel for display</returns>
        public PressPhotoViewModel GetItem(long ContentId)
        {
            /*
             * Get items - this returns an object that is the amalgamation of the standard 
             * Ektron ContentData object and the deserialized Smart Form information.
             * 
             * The format is:
             * * systemObject.Content = standard ContentData fields
             * * systemOjbect.SmartForm = Smart Form fields
             * 
             * For some reason, the returned object in the custom class inherits from ContentData.
             * This inheritance is probably not necessary and is also likely confusing. So only try 
             * to use the fields within the containers listed above.
             */
            var systemObject = pressPhotoManager.GetItem(ContentId, false);
            if (systemObject == null) return null;


            /*
             * I often want to map both Smart Form and ContentData fields to my output. This is where
             * a ViewModel comes in handy - it normalizes the data to be rendered. So then I'm not 
             * dealing with object.Content.* and object.SmartForm.* during rendering.
             * 
             * Another note: You might consider putting this mapping into a constructor in
             * the ViewModel in order to avoid the repetition you'll find in the GetList method
             * below.
             */
            return new PressPhotoViewModel()
            {
                ContentId = systemObject.Content.Id,
                ContentUrl = systemObject.Content.Quicklink,
                Title = systemObject.SmartForm.Name,
                Description = systemObject.SmartForm.Caption,
                ImageUrl = systemObject.SmartForm.Photo
            };
        }

        /// <summary>
        /// Retrieves a list of PressPhoto by the containing Ektron Folder Id (non-recursive)
        /// </summary>
        /// <param name="FolderId">Ektron Folder Id</param>
        /// <returns>Enumerable of Press Photo ViewModel for display</returns>
        public IEnumerable<PressPhotoViewModel> GetList(long FolderId)
        {
            /*
             * There are several "Criteria" objects. This is the simplist, but they also exist
             * for retrieving items from a Collection, from Taxonomy, or given a certain 
             * Metadata property value.
             */
            var criteria = new ContentCriteria();

            // Filter tells the API which folder to retrieve content from.
            criteria.AddFilter(ContentProperty.FolderId, CriteriaFilterOperator.EqualTo, FolderId);

            // Don't check sub-folders.
            criteria.FolderRecursive = false;

            /*
             * Retrieve only 12. The default is 50. Get in the habit of setting this so you 
             * don't grab 50 when you only need one.
             */
            criteria.PagingInfo = new PagingInfo(12);

            // Only return metadata when you need it, for performance. Default here is false.
            criteria.ReturnMetadata = false;

            // Ordering FTW! Hopefully self-explanatory.
            criteria.OrderByField = ContentProperty.Title;
            criteria.OrderByDirection = EkEnumeration.OrderByDirection.Ascending;

            // Same as above... 
            var systemObjectList = pressPhotoManager.GetList(criteria);
            if (systemObjectList == null || !systemObjectList.Any()) return null;

            return systemObjectList.Select(p => new PressPhotoViewModel()
            {
                ContentId = p.Content.Id,
                ContentUrl = p.Content.Quicklink,
                Title = p.SmartForm.Name,
                Description = p.SmartForm.Caption,
                ImageUrl = p.SmartForm.Photo
            });
        }
    }
}

列表控件的数据绑定

为简单起见,我将把这段代码放入 .NET 用户控件中。

标记:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Gallery.ascx.cs" Inherits="Components_Controls_Gallery" %>
<asp:ListView ID="uxPhotoGallery" runat="server" ItemPlaceholderID="itemPlaceholder">
    <LayoutTemplate>
        <ul>
            <asp:PlaceHolder ID="itemPlaceholder" runat="server" />
        </ul>
    </LayoutTemplate>
    <ItemTemplate>
        <li>
            <%-- 
                I'm mixing up two different ways of referencing the incoming data. One is by casting
                the DataItem to the incoming type, which gives you intellisense access to the properties.

                The other is more of a dictionary approach in which you have to type out the property name 
                as a string.

                I really like the casting approach, but it's mega-wordy.
                 --%>
            <a href="<%#((MyProject.ViewModels.PressPhotoViewModel)Container.DataItem).ImageUrl %>">
                <img src="<%#((MyProject.ViewModels.PressPhotoViewModel)Container.DataItem).ImageUrl %>" alt="<%#Eval("Description") %>" />
                <div><%#Eval("Description") %></div>
            </a>
        </li>
    </ItemTemplate>
</asp:ListView>

代码:

using MyProject.Managers;
using System;
using System.Linq;

public partial class Components_Controls_Gallery : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var pressPhotoManager = new PressPhotoManager();

        // Whichever folder Id... 
        var photos = pressPhotoManager.GetList(75);

        if (photos != null && photos.Any())
        {
            uxPhotoGallery.DataSource = photos;
            uxPhotoGallery.DataBind();
        }
    }
}

而且...应该可以了。无论如何,这是我知道的最好方法。当然有一些方法需要更少的代码和准备工作,但到目前为止这是我的首选,因为它让我尽可能多地使用真正的 .NET objects。尽管您可以使用 LinqToXML 或其他技术,但我宁愿不使用它们,因为它需要您自己开发很多 XML 和从来没有读过这个,imo。

编码愉快。