自定义功能可能需要定义 URL,该 URL 可由代码引用以启用特定工作流(编辑内容、查看内容、列出内容等)。可以在自定义路由 URL 的代码中声明新页面,以便为自定义插件和 UI 扩展的特定功能提供可靠的 URL。
自定义页面对于特定的商店、博客、群组或网站的自定义 UI 非常有用,这些商店、博客、群组或网站不需要作为大型应用程序的一部分。当页面/URL 必须存在才能启用工作流时,路由页面可能是更好的选择。下表显示了路由网页和自定义网页之间的所有差异:
|
|
路由页面 |
自定义页面 |
|
支持将 URL 解析为编程上下文 |
是 |
否 |
|
特定主题实例(商店、博客、群组和网站等)中定义 |
否 |
是 |
|
出厂默认实现 |
是 |
否 |
|
代码中创建 |
是 |
否 |
|
用户界面中创建 |
否 |
是 |
|
在 URL 路径中需要 /p/ |
否 |
是 |
INavigable 插件类型用于定义路由站点页面。除了基本的 IPlugin 成员外,INavigable 还添加了以下内容:
void RegisterUrls(IUrlController controller);
RegisterUrls 方法提供对 IUrl 控制器的引用。控制器允许您向平台注册 URL。
网站页面示例将在网站的根目录下创建一个空白页面,其 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 文档。
会员页面为站点上的每个用户添加一个页面,地址是: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 属性指定将显示的新页面的应用程序类型。
此示例将 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。