Limyee 电商平台允许您创建自己的内容和应用程序类型。
为什么要创建自定义应用程序和内容?
如果您正在编写新的应用程序或有要与 Limyee 电商平台的核心服务进行交互的内容(内部或外部),则在平台内创建自定义内容和应用程序可能会有所帮助。平台内的内容类型可以与核心服务交互。点赞,评级,评论,审核,提及和主题标签是您的内容在平台注册后可以访问的一些服务。
在内容模型文章中,有关于内容和应用程序如何适应平台的更深入讨论。
内容和内容类型
平台中提供了两个接口,用于标识内容类型和单个内容实体。IContentType 接口用于标识内容类型,IContent 接口用于表示各个内容实体。这两个接口都可以在 Limyee.Core.dll 中找到。
应用程序和应用程序类型
与内容类似,该平台提供 IApplicationType 来标识应用程序的类型,而 IApplication 是表示单个应用程序实体。这些接口也可以在 Limyee.Core.dll 中找到。
创建应用程序和内容
对于我们的应用程序示例,我们将创建一个链接应用程序。链接应用程序将是指向网站链接的集合。
首先,让我们概述一下我们的实体。
LinksApplication 实体
我们的 LinksApplication 实体将实现 IApplication 接口,并表示链接应用程序的一个实例。
ApplicationTypeId 是一个唯一的 ID,它表示平台中的一种应用程序类型,您可以自行定义此值。在这个例子中,我们有一个静态类,我们在其中定义了我们的 Id,因此可以从多个位置引用它。ApplicationId 是平台中单个应用程序的唯一 ID。我们也将向平台提供此功能。
public Guid ApplicationId { get; set; } public Guid ApplicationTypeId { get { return ContentTypes.LinksApplicationId; } }
IApplication 界面不需要“名称”和“描述”字段,我们使用它来存储基本数据。然后,HtmlName 和 HtmlDescription 方法用于根据目标提供输出。这些允许针对这些目标定制输出,例如,如果您的目标是电子邮件,您可能不希望在内容中内联显示图像。我们的示例数据仅使用文本作为名称和描述,无需针对不同的目标定制输出,而是仅返回所有目标的文本。
public string Name { get; set; } public string Description { get; set; } public string HtmlName(string target) { return Name; } public string HtmlDescription(string target) { return Description; }
对于此示例,我们不允许禁用应用程序,因此 IsEnabled 属性将始终返回 true。我们不会将应用程序存储在不同的组中,而是将它们全部存储在站点根组中,因此我们将始终返回应用程序容器的站点组。我们的应用程序没有用于查看它的 URL或与之关联的头像,因此,AvatarUrl 和 Url 都只会返回 null。
public bool IsEnabled { get { return true; } } public IContainer Container { get { return Apis.Get<IGroups>().Root; } } string IApplication.AvatarUrl { get { return null; } } string IApplication.Url { get { return null; } }
以下是已完成的链接应用程序源代码:
using System; using Limyee.Extensibility; using Limyee.Extensibility.Api.Entities.Version1; using Limyee.Extensibility.Api.Version1; using Limyee.Extensibility.Content.Version1; namespace Samples.Links { public class LinksApplication : ApiEntity, IApplication { public string Name { get; set; } public string Description { get; set; } #region IApplication Members public Guid ApplicationId { get; set; } public Guid ApplicationTypeId { get { return ContentTypes.LinksApplicationId; } } public string HtmlName(string target) { return Name; } public string HtmlDescription(string target) { return Description; } public bool IsEnabled { get { return true; } } public IContainer Container { get { return Apis.Get<IGroups>().Root; } } string IApplication.AvatarUrl { get { return null; } } string IApplication.Url { get { return null; } } #endregion } }
LinkItem 实体
我们的 LinkItem 实体将实现 IContent 接口,并在链接应用程序中表示单个链接项。
ContentTypeId 是一个唯一的 ID,表示平台中的一种内容类型,您可以在创建内容类型时自行定义此值。该示例使用定义 ApplicationTypeId 的同一静态类来定义此 Id。ContentId 是平台中单个内容项的唯一 Id。我们也将向平台提供此功能。
public Guid ContentId { get; set; } public Guid ContentTypeId { get { return ContentTypes.LinksItemId; } }
每条内容都与平台中的一个应用程序相关,我们已经定义了一个属性来存储我们内容的应用程序 ApplicationId。当我们在 IContent 接口上实现 Application 属性时,使用此值返回应用程序的实例。
public Guid ApplicationId { get; set; } public IApplication Application { get { return LinksData.GetApplication(ApplicationId); } }
IContent 接口不需要“名称”和“描述”字段,我们使用它来存储基本数据。然后,HtmlName 和 HtmlDescription 方法用于根据目标提供输出。这些允许针对这些目标定制输出,例如,如果您的目标是电子邮件,您可能不希望在内容中内联显示图像。
public string Name { get; set; } public string Description { get; set; } public string HtmlName(string target) { return Name; } public string HtmlDescription(string target) { return Description; }
每个链接项都有一个 Url,IContent 接口需要一个 Url,因此我们将使用该属性来存储 Url 并满足接口要求。
public string Url { get; set; }
CreateDate、IsEnabled、AvatarUrl 和 CreatedByUserId 属性是接口所必需的,但在此示例中将是静态的或不需要的,因此我们将实现这些属性,但它们不提供任何值。如果这是一个实现平台核心服务或其他功能的全功能示例,则这些值对于正确填充将变得非常重要。
public DateTime CreatedDate { get; set; } public bool IsEnabled { get { return true; } } string IContent.AvatarUrl { get { return null; } } int? IContent.CreatedByUserId { get { return null; } }
以下是已完成的 LinkItem 实体源代码:
using System; using Limyee.Extensibility.Api.Entities.Version1; using Limyee.Extensibility.Content.Version1; namespace Samples.Links { public class LinkItem : ApiEntity, IContent { public Guid ApplicationId { get; set; } public string Name { get; set; } public string Description { get; set; } #region IContent Members public Guid ContentId { get; set; } public Guid ContentTypeId { get { return ContentTypes.LinksItemId; } } public IApplication Application { get { return LinksData.GetApplication(ApplicationId); } } public string HtmlName(string target) { return Name; } public string HtmlDescription(string target) { return Description; } public string Url { get; set; } public DateTime CreatedDate { get; set; } public bool IsEnabled { get { return true; } } string IContent.AvatarUrl { get { return null; } } int? IContent.CreatedByUserId { get { return null; } } #endregion } }
LinksApplicationType 实体
IPlugin 和 ITranslatablePlugin 接口在其他文章中讨论过,我们将跳过这些接口的实现解释,而专注于 IApplicationType 实现。
ApplicationTypeId 与定义 IApplication 实体时使用的 ID 相同。应用程序名称是将在平台中显示的应用程序的名称。该示例使用翻译,以便可以为平台支持的每种语言翻译应用程序名称。
Guid IApplicationType.ApplicationTypeId { get { return ContentTypes.LinksApplicationId; ; } } string IApplicationType.ApplicationTypeName { get { return _translations.GetLanguageResourceValue("ApplicationTypeName"); } }
AttachChangeEvents 方法提供了 IApplicationStateChanges 对象。应用程序类型使用此对象来通知平台对应用程序的更改。例如,当您的应用程序被删除时,通知平台删除与该应用程序关联的数据。
void IApplicationType.AttachChangeEvents(IApplicationStateChanges stateChanges) { _applicationState = stateChanges; }
ContainerTypes 属性应返回可以保存此应用程序的所有容器类型。在这种情况下,唯一的容器是组,因此返回 Id。
Guid[] IApplicationType.ContainerTypes { get { return new Guid[] { Apis.Get<IGroups>().ContentTypeId }; } }
最后,Get 方法应返回 LinksApplication 实体的实例。
IApplication IApplicationType.Get(Guid applicationId) { return LinksData.GetApplication(applicationId); }
以下是已完成的链接应用程序类型源代码:
using System; using Limyee.Extensibility; using Limyee.Extensibility.Api.Version1; using Limyee.Extensibility.Content.Version1; using Limyee.Extensibility.Version1; namespace Samples.Links { public class LinksApplicationType : IApplicationType, ITranslatablePlugin { IApplicationStateChanges _applicationState = null; ITranslatablePluginController _translations = null; #region IPlugin Members string IPlugin.Name { get { return "Links"; } } string IPlugin.Description { get { return "Collections of Links"; } } void IPlugin.Initialize() { } #endregion #region IApplicationType Members Guid IApplicationType.ApplicationTypeId { get { return ContentTypes.LinksApplicationId; ; } } string IApplicationType.ApplicationTypeName { get { return _translations.GetLanguageResourceValue("ApplicationTypeName"); } } void IApplicationType.AttachChangeEvents(IApplicationStateChanges stateChanges) { _applicationState = stateChanges; } Guid[] IApplicationType.ContainerTypes { get { return new Guid[] { Apis.Get<IGroups>().ContentTypeId }; } } IApplication IApplicationType.Get(Guid applicationId) { return LinksData.GetApplication(applicationId); } #endregion #region ITranslatablePlugin Members Translation[] ITranslatablePlugin.DefaultTranslations { get { var t = new Translation("en-US"); t.Set("ApplicationTypeName", "Links Application"); return new Translation[] { t }; } } void ITranslatablePlugin.SetController(ITranslatablePluginController controller) { _translations = controller; } #endregion } }
LinkItemContentType 实体
同样,IPlugin 和 ITranslatablePlugin 接口在其他文章中讨论过,我们将跳过对这些实现的解释,并专注于IContentType实现。
ContentTypeId 与定义 IContent 实体时使用的 ID 相同。内容名称是将在平台中显示的内容名称。该示例使用翻译,以便可以为平台支持的每种语言翻译内容名称。
Guid IContentType.ContentTypeId { get { return ContentTypes.LinksItemId; } } string IContentType.ContentTypeName { get { return _translations.GetLanguageResourceValue("ContentTypeName"); } }
AttachChangeEvents 方法提供了 IContentStateChanges 对象。您的内容类型使用此对象来通知平台您的内容所做的更改。例如,当您的内容被删除时,通知平台删除与该内容关联的数据。
void IContentType.AttachChangeEvents(IContentStateChanges stateChanges) { _contentState = stateChanges; }
ApplicationTypes 属性应返回可以保存此内容的所有应用程序类型。在这种情况下,唯一的应用程序是我们的 LinksApplication,因此返回 Id。
Guid[] IContentType.ApplicationTypes { get { return new Guid[] { ContentTypes.LinksApplicationId }; } }
最后,Get 方法应返回 LinkItem 实体的实例。
IContent IContentType.Get(Guid contentId) { return LinksData.GetLink(contentId); }
以下是已完成的 LinkItemContentType 实体源代码:
using System; using Limyee.Extensibility.Content.Version1; using Limyee.Extensibility.Version1; using IContent = Limyee.Extensibility.Content.Version1.IContent; namespace Samples.Links { public class LinkItemContentType : IContentType, ITranslatablePlugin { IContentStateChanges _contentState = null; ITranslatablePluginController _translations = null; #region IPlugin Members string IPlugin.Name { get { return "Link Items"; } } string IPlugin.Description { get { return "Items in a Links collection"; } } void IPlugin.Initialize() { } #endregion #region IContentType Members Guid[] IContentType.ApplicationTypes { get { return new Guid[] { ContentTypes.LinksApplicationId }; } } void IContentType.AttachChangeEvents(IContentStateChanges stateChanges) { _contentState = stateChanges; } Guid IContentType.ContentTypeId { get { return ContentTypes.LinksItemId; } } string IContentType.ContentTypeName { get { return _translations.GetLanguageResourceValue("ContentTypeName"); } } IContent IContentType.Get(Guid contentId) { return LinksData.GetLink(contentId); } #endregion #region ITranslatablePlugin Members Translation[] ITranslatablePlugin.DefaultTranslations { get { var t = new Translation("en-US"); t.Set("ContentTypeName", "Link Item"); return new Translation[] { t }; } } void ITranslatablePlugin.SetController(ITranslatablePluginController controller) { _translations = controller; } #endregion } }
自定义内容和应用程序插件现已完成,但我们可以添加一些附加功能,使其更易于在平台站点中使用。
小组件扩展
到目前为止,我们将无法使用 velocity 小组件与我们的新应用程序或内容进行交互,当您实现了 IScriptedContentWidgetExtensionsionsions, 就可以公开我们的数据方法,以便可以使用 velocity 调用它们。我们将添加一个名为 custom_v1_links 的实现来扩展 LinksApplication,以及一个名为 custom_v1_linkitem 的实现来扩展 LinkItem。
我们的 custom_v1_links 实现扩展了 LinksApplicationMethods 类,并公开该类的方法。$custom_v1_links.List() 将返回所有链接应用程序的列表,$custom_v1_links.Get(Guid applicationId) 将返回一个链接应用程序。
custom_v1_links 扩展的源代码:
using System; using Limyee.Extensibility.Api.Entities.Version1; using Limyee.Extensibility.UI.Version1; namespace Samples.Links.WidgetApi { public class LinksApplicationWidgetExtension : IScriptedContentWidgetExtension { #region IScriptedContentWidgetExtension Members public string ExtensionName { get { return "custom_v1_links"; } } public object Extension { get { return new LinksApplicationMethods(); } } #endregion #region IPlugin Members public string Name { get { return "Links Extension (custom_v1_links)"; } } public string Description { get { return "Enables widgets to work with Links Applications."; } } public void Initialize() { } #endregion } public class LinksApplicationMethods { public ApiList<LinksApplication> List() { return new ApiList<LinksApplication>(LinksData.ListApplications()); } public LinksApplication Get(Guid applicationId) { return LinksData.GetApplication(applicationId); } } }
我们的 custom_v1_linkitem 实现扩展了 LinkItemMethods 类,并公开该类的方法。$custom_v1_linkitem.List(Guid applicationId) 将返回列表,$custom_v1_linkitem.Get(Guid contentId) 将返回 LinkItem 的单个实例。
custom_v1_linkitem 扩展的源代码:
using System; using Limyee.Extensibility.Api.Entities.Version1; using Limyee.Extensibility.UI.Version1; namespace Samples.Links.WidgetApi { public class LinkItemWidgetExtension : IScriptedContentWidgetExtension { #region IScriptedContentWidgetExtension Members public string ExtensionName { get { return "custom_v1_linkitem"; } } public object Extension { get { return new LinkItemMethods(); } } #endregion #region IPlugin Members public string Name { get { return "LinkItem Extension (custom_v1_linkitem)"; } } public string Description { get { return "Enables widgets to work with Link Items."; } } public void Initialize() { } #endregion } public class LinkItemMethods { public ApiList<LinkItem> List(Guid applicationId) { return new ApiList<LinkItem>(LinksData.ListLinks(applicationId)); } public LinkItem Get(Guid contentId) { return LinksData.GetLink(contentId); } } }
插件组
我们可以按原样部署它,但是您需要在管理面板中搜索以启用这4个插件,更不用说这些插件彼此依赖,并且不应彼此独立使用。我们可以使用 IPluginGroup 创建一个入口来同时启用或禁用所有相关插件,以解决这些问题。
以下是我们插件组的完整源代码:
using System; using System.Collections.Generic; using Samples.Links.WidgetApi; using Limyee.Extensibility.Version1; namespace Samples.Links.Plugins { public class LinksPluginGroup : IPluginGroup { #region IPlugin Members string IPlugin.Name { get { return "Links Application"; } } string IPlugin.Description { get { return "Defines the plugins required by the links application"; } } void IPlugin.Initialize() { } #endregion #region IPluginGroup Members IEnumerable<Type> IPluginGroup.Plugins { get { return new[] { typeof (LinksApplicationType), typeof (LinkItemContentType), typeof (LinkItemWidgetExtension), typeof (LinksApplicationWidgetExtension) }; } } #endregion } }
示例小组件
现在,我们可以将包含链接应用程序的 dll 部署到平台,并启用链接应用程序插件组。我们可以使用小组件来测试我们的应用程序是否正常工作,并且我们的内容是否可以使用核心服务,在本例中为“点赞”的服务。
该小组件将列出所有链接应用程序。对于每个应用程序,它将列出所有链接项,并且这些项将分别显示一个“点赞”按钮。
该小组件的源代码如下所示:
#set($linkapps = $custom_v1_links.List()) #foreach ($linkapp in $linkapps) #beforeall <ul class="content-list simple"> #each <li class="content-item simple"> <h4 style="margin-bottom: 5px;">$linkapp.Name</h4> <div class="description" style="margin: 5px 0;">$linkapp.Description</div> #set($links = $custom_v1_linkitem.List($linkapp.ApplicationId)) #foreach ($link in $links) #beforeall <ul class="links"> #each <li class="link-item" style="margin-bottom: 5px;"> #if ($limyee_v1_user.IsRegistered($limyee_v1_user.Accessing.Id)) #set ($likeFormat = '{toggle} <span class="count"><span class="icon"></span>{count}</span>') #else #set ($likeFormat = '<span class="count"><span class="icon"></span>{count}</span>') #end <a href="$link.Url">$link.Name</a> - $limyee_v1_ui.Like($link.ContentId, $link.ContentTypeId, "%{ Format = $likeFormat, IncludeTip = 'true' }") <div class="description">$link.Description</div> </li> #afterall </ul> #end </li> #afterall </ul> #end
将小组件添加到页面后,您应该会看到以下内容:
可下载源
现在,我们已经有了基本级别的应用程序和内容类型,我们可以开始向应用程序添加对核心服务的支持。有关向内容添加核心服务支持,请查阅其他文章。