自定义功能可能需要定义 URL,该 URL 可由代码引用以启用特定工作流(编辑内容、查看内容、列出内容等)。可以在自定义路由 URL 的代码中声明新页面,以便为自定义插件和 UI 扩展的特定功能提供可靠的 URL。
路由页面与自定义页面
自定义页面对于特定的商店、博客、群组或网站的自定义 UI 非常有用,这些商店、博客、群组或网站不需要作为大型应用程序的一部分。当页面/URL 必须存在才能启用工作流时,路由页面可能是更好的选择。下表显示了路由网页和自定义网页之间的所有差异:
|
路由页面 |
自定义页面 |
支持将 URL 解析为编程上下文 |
是 |
否 |
特定主题实例(商店、博客、群组和网站等)中定义 |
否 |
是 |
出厂默认实现 |
是 |
否 |
代码中创建 |
是 |
否 |
用户界面中创建 |
否 |
是 |
在 URL 路径中需要 /p/ |
否 |
是 |
定义网站页面
INavigable 插件类型用于定义路由站点页面。除了基本的 IPlugin 成员外,INavigable 还添加了以下内容:
void RegisterUrls(IUrlController controller);
RegisterUrls 方法提供对 IUrl 控制器的引用。控制器允许您向平台注册 URL。
示例 1:网站页面
网站页面示例将在网站的根目录下创建一个空白页面,其 URL 为“custom”。例如,如果您的平台网址 http://mysite,则新网页地址是:http://mysite/custom。
using Limyee.Extensibility.Urls.Version1; namespace Limyee.Examples { public class CustomSitePageExample : INavigable { void INavigable.RegisterUrls(IUrlController controller) { controller.AddPage("page-custom", "custom", null, null, "page-custom", new PageDefinitionOptions()); } public string Description { get { return "定义自定义网站页面。"; } } public void Initialize() { } public string Name { get { return "自定义网站页面示例"; } } } }
我们使用 controller.AddPage 方法添加新的小组件页面。有关 IUrlController 及其方法的其他信息,请参阅 IUrlController 文档。
示例 2:会员页面
会员页面为站点上的每个用户添加一个页面,地址是:http://mysite/{username}/custompage。
using System; using System.Web; using Limyee.Extensibility; using Limyee.Extensibility.Api.Version1; using Limyee.Extensibility.Urls.Version1; using Limyee.Extensibility.Version1; namespace Limyee.Examples { public class CustomPageExamples : INavigable, ITranslatablePlugin { private ITranslatablePluginController _translationController; void INavigable.RegisterUrls(IUrlController controller) { object userNameConstraints = new { UserName = @"^[a-zA-Z0-9\-\._]+$" }; controller.AddPage("page-members-custom", "members/{UserName}/custompage", null, userNameConstraints, "page-members-custom", new PageDefinitionOptions() { DefaultPageXml = @"<contentWidgetPage pageName=""page-members-custom"" isCustom=""false"" layout=""Content"" themeType=""0c647246-6735-42f9-875d-c8b991fe739b""> <regions> <region regionName=""Content""> <contentWidgets> <contentWidget type=""Limyee.ScriptedContentWidgets.ScriptedContentWidget, Limyee.ScriptedContentWidgets::7cccad3e01e343a2b7cf65c41bd7d72a"" showHeader=""False"" cssClassAddition=""no-wrapper with-spacing responsive-1"" isLocked=""False"" configuration="""" /> </contentWidgets> </region> </regions> <contentWidgetTabs /> </contentWidgetPage>", TitleFunction = () => _translationController.GetLanguageResourceValue("page-members-custom"), DescriptionFunction = () => _translationController.GetLanguageResourceValue("page-members-custom-description"), ParseContext = ParseUserContext }); } private void ParseUserContext(PageContext context) { var userName = context.GetTokenValue("UserName"); if (userName != null) { var usersApi = Apis.Get<IUsers>(); string decodedUserName = Apis.Get<IUrl>().DecodeFileComponent(userName.ToString()); var user = usersApi.Get(new UsersGetOptions() { Username = decodedUserName }); ContextItem contextItem = null; if (user != null && !user.HasErrors()) { contextItem = new ContextItem() { TypeName = "User", ApplicationId = user.ContentId, ApplicationTypeId = usersApi.ContentTypeId, ContainerId = user.ContentId, ContainerTypeId = usersApi.ContentTypeId, ContentId = user.ContentId, ContentTypeId = usersApi.ContentTypeId, Id = user.Id.ToString() }; } else { if (usersApi.AccessingUser.IsSystemAccount.Value) { var url = Apis.Get<ICoreUrls>().LogIn(new CoreUrlLoginOptions() { ReturnToCurrentUrl = true }); HttpContext.Current.Response.Redirect(url); } else { throw new CustomException(string.Format(_translationController.GetLanguageResourceValue("UserNotFoundException"), decodedUserName)); } } if (contextItem != null) context.ContextItems.Put(contextItem); } } Translation[] ITranslatablePlugin.DefaultTranslations { get { var enUs = new Translation("en-us"); enUs.Set("page-members-custom", "Sample User Page"); enUs.Set("page-members-custom-description", "Shows a page at the url members/{username}/custompage."); enUs.Set("UserNotFoundException", "User {0} not found."); var zhCn = new Translation("zh-cn"); zhCn.Set("page-members-custom", "用户页面示例"); zhCn.Set("page-members-custom-description", "显示页面使用以下网址:members/{username}/custompage."); zhCn.Set("UserNotFoundException", "未找到用户:{0}。"); return new Translation[] { enUs, zhCn }; } } public void SetController(ITranslatablePluginController controller) { _translationController = controller; } public string Description { get { return "定义自定义用户页面。"; } } public void Initialize() { } public string Name { get { return "自定义用户页面示例"; } } } public class CustomException : Exception, IUserRenderableException { public CustomException() { } public CustomException(string message) : base(message) { } public string GetUserRenderableMessage() { return this.Message; } } }
在此示例中,Url 参数被定义为“members/{UserName}/custompage”,new { UserName = @“^[a-zA-Z0-9\-\._]+$” } 约束应用于 UserName 变量。仅当请求的 URL 与页面时定义的格式和约束匹配时,URL 才会响应。
该示例还使用了 PageDefinitionOptions 参数中的一些可用选项。DefaultPageXml 用于定义此页面的出厂默认小组件集。在主题编辑面板中处理页面时,TitleFunction 和 DescriptionFunction 选项确定页面的名称和描述。ParseContext 定义了用于检查 Url 并确定此页上下文项目的方法。
示例中的 ParseContext 方法执行几个函数。首先,我们尝试从 URL 加载用户名。如果有效,我们会将该用户添加到页面的 ContextItems 集合中。这将启用了平台中的其他方法。例如,在 ContextItems 集合中有用户将允许 $limyee_v1_user.Current 调用并返回该用户。如果用户无效,我们要么重定向用户登录页面(如果他们当前未登录),要么我们抛出一个异常,将用户名标识为无效。
定义应用程序页面
IApplicationNavigable 插件类型用于通过添加新的路由页面来扩展现有应用程序(或群组)。除了基本的 IPlugin 成员外,IApplicationNavigable 还添加了以下内容:
void RegisterUrls(IUrlController controller);
RegisterUrls 方法提供了对 IUrlController 的引用。控制器允许您向平台注册 URL。
Guid ApplicationTypeId { get; }
ApplicationTypeId 属性指定将显示的新页面的应用程序类型。
示例 3:博客原生页面
此示例将 url 添加到由 IHttpHandler 处理的博客。示例中定义的页面将具有以下 URL 模式:http://mysite/{grouppath}/{b}/{blogappkey}/raw。
using System; using System.Linq; using System.Web; using Limyee.Extensibility; using Limyee.Extensibility.Api.Version1; using Limyee.Extensibility.Urls.Version1; namespace Limyee.Examples { public class CustomBlogRawExample : IApplicationNavigable { Guid IApplicationNavigable.ApplicationTypeId { get { return Apis.Get<IBlogs>().ApplicationTypeId; } } void IApplicationNavigable.RegisterUrls(IUrlController controller) { controller.AddRaw("blog-raw", "{WeblogApp}/raw", null, null, (a, p) => { var handler = new CustomBlogRawHttpHandler(); handler.ProcessRequest(a.ApplicationInstance.Context); }, new RawDefinitionOptions() { ParseContext = ParseBlogContext }); } private void ParseBlogContext(PageContext context) { var appKey = context.GetTokenValue("WeblogApp"); var blogsApi = Apis.Get<IBlogs>(); var groupsApi = Apis.Get<IGroups>(); if (appKey != null) { var groupItem = context.ContextItems.GetAllContextItems().FirstOrDefault(a => a.ContentTypeId == groupsApi.ContentTypeId); if (groupItem != null) { var blog = blogsApi.Get(new BlogsGetOptions() { GroupId = int.Parse(groupItem.Id), Key = appKey.ToString() }); if (blog != null) { var contextItem = new ContextItem() { TypeName = "Blog", ApplicationId = blog.ApplicationId, ApplicationTypeId = blogsApi.ApplicationTypeId, ContainerId = blog.Group.ApplicationId, ContainerTypeId = groupsApi.ContentTypeId, ContentId = blog.ApplicationId, ContentTypeId = blogsApi.ApplicationTypeId, Id = blog.Id.ToString() }; context.ContextItems.Put(contextItem); } } } } public string Description { get { return "定义自定义博客原始网址。"; } } public void Initialize() { } public string Name { get { return "自定义博客页面"; } } } public class CustomBlogRawHttpHandler : IHttpHandler { public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { var blog = Apis.Get<IUrl>().CurrentContext.ContextItems.GetAllContextItems() .FirstOrDefault(b => b.ContentTypeId == Apis.Get<IBlogs>().ContentTypeId); if (blog != null) { var posts = Apis.Get<IBlogPosts>().List(new BlogPostsListOptions() { BlogId = int.Parse(blog.Id) }); foreach (var post in posts) context.Response.Write(post.Title + "<br />"); } context.Response.End(); } } }
URL 被定义为 {WeblogApp}/raw,因为我们要向博客应用程序添加一个页面,URL 中包括博客应用程序标识符('/b/')的部分。您只需要定义群组或应用程序内的 URL 部分。
添加小组件页面和添加原生页面有什么区别?
IUrlController 提供了添加小组件页面或原生页面的选项。小组件页面是支持基于小组件的布局的主题页面。在定义 Url 时,原生页面可以完全控制输出。原生页面的典型用例是 RSS 源、文件下载或 Xml。