您现在的位置是:首页 > cms教程 > Discuz教程Discuz教程
Discuz的NT模板机制是怎么样的
孙剑佛2025-06-23Discuz教程已有人查阅
导读作为产品中的一大特色,模板机制一经推出,就引来了大家特别是站长们的关注。但它所饱受的风风雨雨也成了那时不少人关注的话题。而今天本人将结合在产品组中的开发经历
作为产品中的一大特色,模板机制一经推出,就引来了大家特别是站长们的关注。但它所饱受的风风雨雨也成了那时不少人关注的话题。而今天本人将结合在产品组中的开发经历,介绍一下模板机制在设计使用时的一些体会心得。希望借此陋文,使模板机制揭开“神秘”面纱,为大家在实际设计中提供一些有价值的参考和建议。
首先阐述一下模板设计的目标,因为这对于它最终要实现的功能非常重要。考虑到国内大部分站长基本上都不具备.net开发背景,而我们的模板就是要降低这个门槛,便于站长进行设计订制以及修改等。
而另一个目的就是要提升aspx页面的访问速度,所以我们并未在模板设计时引入(web)控件机制,因为如果使用.net控件,在windows的临时目录中会进行控件的订制生成(按用户设置的属性)。虽然在.net2.0使用了fastobjectfactory的机制来提升页面生成的效率,比如使用batch批量编译选项 (web.config
文件中配置)生成的DLL(这里的DLL也是在临时目录下生成的随机命名的DLL文件,且重复编译的情况在所难免)。但最终还是无法改变要生成服务器端控件的过程。
我们在设计模板本身所提供的语法时,尽可能逼近HTML的书写习惯,这样只要有HTML编写网页经验的人就会很容易适应这种书写方式。当然有 asp开发经验的站长也能很快上手,因为模板的语法非常类似于asp, 比如有<%if ...%>,<%else%>这样的写法等等。另外我们的模板语法也力求简练精悍,只需很少的语法规则就直接支持生成内容丰富且形式多样的页面。说了这些,相信大家已经有兴趣来一看究竟了。不忙,这里先要介绍一下如何使用模板机制来生成aspx页面。因为我有一位从事.net开发多年的朋友,在一次聊天时他说,修改我们的前台页面时要手工修改"aspx/.../"下的相应的aspx文件,而当他看到 aspx文件中的内容时大吃一惊,举个例子如下(aspx/1/logout.aspx):
.....命名空间和类的引用
可能完成的任务”。而实际上,我们并不希望大家或站长来完成这项工作。因为这是系统自动生成的。
而生成的前提就是在template/下的模板“目录”中的HTM文件。还是借用上面的logout,只是这里要看
的是模板目录下同名的logout.htm模板文件。它的内容如下:
因为我们在开始设计模板机制时就想到要简化模板代码并提升可重用性,因此要支持子模板机制。
这就类似于设计网页时的页首和页尾,我们在网页引用时,只需要include进来即可,而当修改页首和
页尾时,只须变动相应文件即可。
这里不妨再打开_header.htm(注意子模板名称要用下划线开头),发现内容如下:
1.按模板语法修改相应的模板文件夹下的模板文件;
2.在后台生成或使用官方的模板生成器生成相应aspx页面即可;
其实流程非常简单,相信即使不懂aspx开发的朋友也会很快适应并上手。前提就是要了解模板语法,除了上面所说的以外。
好了,目前我们只是知道了如使使用和修改它,但所谓的“模板生成”机制又是个什么样子呢!必定到这里我们只走完了一半旅途,下面将会介绍模板的生成机制。
首先要看一下后台的模板(列表)管理界面,从上图可知道,模板是按名称(目录)来进行管理的,而每个模板都有名称,存放路径,版权,作者等相关信息。而这此信息都是来自于每个模板(目录)下的about.xml文件,这里将它的内容贴
出来:
需要说明的是上图中不是所有模板都能在前台使用,而是当被标记为“已入库”才可在前台使用,而入库即数据库,下面就是数据库中而接下来要说的,就是模板列表中每个模板后面的“生成”链接所要干的活了。
如果大家手头上有reflector的话,请使用这个工具加载我们官方提供的产品目录下的bin文件夹中的discuz.common.dll文件,找到 PageTemplate这个类。这里为了便于说明,将反射所得到的代码
加上注释贴出来:
基本上都是对正则式的使用,因为本人不是这方面的高手,所以就不多说了,相信开源之后大家拿源码和注释一看便知:)
这里需要说明的就是ReplaceSpecialTemplate(string forumpath,string skinName,....) 这个函数,它的实现我们要到discuz.forum.dll中去找,这里为了方便,直接就将反射出来的代码加上注释贴
出来,大家一看便知:
下面要说的就是上面的这个 ForumPageTemplate类目前所要实现的功能。因为模板中要被订制的东西有很多,而我们目前所搭建的功能只是为了生成和转换时使用,当用户有要替换的特殊变量就会出现无法订制的情况。所以才提供了这个类以便实现与模板有关的用户订制需求。当然目录所
提供的功能只是简单的替换而已,但并不排除以后随着用户口味的挑剔而进行升级扩展的可能。
而用户进行特殊变量定制也非常简单,只要在上面所贴的后台“模板列表”图中的后面点击相应的“管理”链接之后就会看到下面的页面,只要再点击右下方的“模板变量列表”,即可以进入定制模板变量的页面,如图:大家只要进行相应操作设置即可。
首先阐述一下模板设计的目标,因为这对于它最终要实现的功能非常重要。考虑到国内大部分站长基本上都不具备.net开发背景,而我们的模板就是要降低这个门槛,便于站长进行设计订制以及修改等。
而另一个目的就是要提升aspx页面的访问速度,所以我们并未在模板设计时引入(web)控件机制,因为如果使用.net控件,在windows的临时目录中会进行控件的订制生成(按用户设置的属性)。虽然在.net2.0使用了fastobjectfactory的机制来提升页面生成的效率,比如使用batch批量编译选项 (web.config
文件中配置)生成的DLL(这里的DLL也是在临时目录下生成的随机命名的DLL文件,且重复编译的情况在所难免)。但最终还是无法改变要生成服务器端控件的过程。
我们在设计模板本身所提供的语法时,尽可能逼近HTML的书写习惯,这样只要有HTML编写网页经验的人就会很容易适应这种书写方式。当然有 asp开发经验的站长也能很快上手,因为模板的语法非常类似于asp, 比如有<%if ...%>,<%else%>这样的写法等等。另外我们的模板语法也力求简练精悍,只需很少的语法规则就直接支持生成内容丰富且形式多样的页面。说了这些,相信大家已经有兴趣来一看究竟了。不忙,这里先要介绍一下如何使用模板机制来生成aspx页面。因为我有一位从事.net开发多年的朋友,在一次聊天时他说,修改我们的前台页面时要手工修改"aspx/.../"下的相应的aspx文件,而当他看到 aspx文件中的内容时大吃一惊,举个例子如下(aspx/1/logout.aspx):
.....命名空间和类的引用
<script runat="server">
override protected void OnInit(EventArgs e)
{
base.OnInit(e);
templateBuilder.Append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN
\" \"http:// .w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n");
templateBuilder.Append("<html xmlns=\"http:// .w3.org/1999/xhtml\">\r\n");
templateBuilder.Append("<head>\r\n");
templateBuilder.Append("<meta http-equiv=\"Content-Type\" content=\"text/html;
charset=utf-8\" />\r\n");
templateBuilder.Append("" + meta.ToString() + "\r\n");
templateBuilder.Append("<title>" + pagetitle.ToString() + " " +
config.Seotitle.ToString().Trim() + " - " +
config.Forumtitle.ToString().Trim() + " - Powered by Discuz!NT
</title>\r\n");
templateBuilder.Append("<link rel=\"icon\" href=\"favicon.ico\"
type=\"image/x-icon\" />\r\n");
templateBuilder.Append("<link rel=\"shortcut icon\" href=\"favicon.ico\"
type=\"image/x-icon\" />\r\n");
templateBuilder.Append("<!-- 调用样式表 -->\r\n");
templateBuilder.Append("<link rel=\"stylesheet\" href=\"templates/" +
templatepath.ToString() + "/dnt.css\"
type=\"text/css\" media=\"all\" />\r\n");
templateBuilder.Append("" + link.ToString() + "\r\n");
templateBuilder.Append("<script type=\"text/javascript\" src=\"templates/" +
templatepath.ToString() + "/report.js\"></" + "script>\r\n");
templateBuilder.Append("<script type=\"text/javascript\" src=\"templates/" +
templatepath.ToString() + "/common.js\"></" + "script>\r\n");
templateBuilder.Append("<script type=\"text/javascript\" src=\"editor/common.js\">
</" + "script>\r\n");
templateBuilder.Append("<script type=\"text/javascript\" src=\"editor/menu.js\">
</" + "script>\r\n");
templateBuilder.Append("" + script.ToString() + "\r\n");
templateBuilder.Append("</head>\r\n");
相信大家看到这样的aspx页面都会晕上一阵子,直接修改的想法已变得非常不现实了,简直是“不可能完成的任务”。而实际上,我们并不希望大家或站长来完成这项工作。因为这是系统自动生成的。
而生成的前提就是在template/下的模板“目录”中的HTM文件。还是借用上面的logout,只是这里要看
的是模板目录下同名的logout.htm模板文件。它的内容如下:
<%template _header%>
<div id="foruminfo">
<div class="userinfo">
<h2><a href="{config.forumurl}">{config.forumtitle}</a> <strong>用户退出</strong></h2>
</div>
</div>
<!--TheCurrent end-->
<%template _msgbox%>
</div>
<%template _footer%>
大家可能会说,难道就是这几行就实现了上面aspx页面的内容吗?当然不是了,请大家注意:
<%template _header%>
这一行,其实就是告诉模板页面生成器: 这是一个子模板。因为我们在开始设计模板机制时就想到要简化模板代码并提升可重用性,因此要支持子模板机制。
这就类似于设计网页时的页首和页尾,我们在网页引用时,只需要include进来即可,而当修改页首和
页尾时,只须变动相应文件即可。
这里不妨再打开_header.htm(注意子模板名称要用下划线开头),发现内容如下:
1<%template _pageheader%>
2<body>
3<div id="append_parent"></div>
4<div id="container">
5<!--header start-->
6<div id="header">
7 .
有意思,又是一个“子模板”出现在了第一行。不错,我们的机制允许模板被嵌套使用,这样会使页面的“组装”更加灵活多样。即然都走到这一步,不妨再打开_pageheader子模板,正所谓“不撞南墙不回头”嘛:)
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:// .w3.org/TR
2/xhtml1/DTD/xhtml1-transitional.dtd">
3<html xmlns="http://www.w3.org/1999/xhtml">
4<head>
5<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6{meta}
7<title>{pagetitle} {config.seotitle} - {config.forumtitle} - Powered by Discuz!NT</title>
8<link rel="icon" href="favicon.ico" type="image/x-icon" />
9<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
10<!-- 调用样式表 -->
11<link rel="stylesheet" href="templates/{templatepath}/dnt.css" type="text/css" media="all"/>
12{link}
13<script type="text/javascript" src="templates/{templatepath}/report.js"></script>
14<script type="text/javascript" src="templates/{templatepath}/common.js"></script>
15<script type="text/javascript" src="editor/common.js"></script>
16<script type="text/javascript" src="editor/menu.js"></script>
17{script}
18</head>
折腾了一圈,到这里出现了上面aspx页中的对应内容,有意思吧,不过里面的{pagetitle}和{config.seotitle}以及{config.forumtitle}这样的东东又是什么呢? 其实非常简单,这就是按照模板语法格式所书写的代码,因为这两处在模板生成之后会变成
1 templateBuilder.Append("<title>" + pagetitle.ToString() + " " +
2 config.Seotitle.ToString().Trim() + " - " +
3 config.Forumtitle.ToString().Trim() + " - Powered by Discuz!NT
4 </title>\r\n");
好了,到了这里我们应该清楚了,以后要修改前台页面的一个标准流程:1.按模板语法修改相应的模板文件夹下的模板文件;
2.在后台生成或使用官方的模板生成器生成相应aspx页面即可;
其实流程非常简单,相信即使不懂aspx开发的朋友也会很快适应并上手。前提就是要了解模板语法,除了上面所说的以外。
好了,目前我们只是知道了如使使用和修改它,但所谓的“模板生成”机制又是个什么样子呢!必定到这里我们只走完了一半旅途,下面将会介绍模板的生成机制。
首先要看一下后台的模板(列表)管理界面,从上图可知道,模板是按名称(目录)来进行管理的,而每个模板都有名称,存放路径,版权,作者等相关信息。而这此信息都是来自于每个模板(目录)下的about.xml文件,这里将它的内容贴
出来:
1<?xml version="1.0" encoding="utf-8" ?>
2<about>
3 <template name="basic"
4 author="Discuz!NT"
5 createdate = "2007-11-12"
6 ver="1.1112"
7 fordntver="2.0"
8 copyright="Copyright 2007 Comsenz Inc." />
9</about>
注: 上图中的那个“乐队演出”图片其实是模板目录下的about.png文件,它相当于一张预览图。需要说明的是上图中不是所有模板都能在前台使用,而是当被标记为“已入库”才可在前台使用,而入库即数据库,下面就是数据库中而接下来要说的,就是模板列表中每个模板后面的“生成”链接所要干的活了。
如果大家手头上有reflector的话,请使用这个工具加载我们官方提供的产品目录下的bin文件夹中的discuz.common.dll文件,找到 PageTemplate这个类。这里为了便于说明,将反射所得到的代码
加上注释贴出来:
基本上都是对正则式的使用,因为本人不是这方面的高手,所以就不多说了,相信开源之后大家拿源码和注释一看便知:)
这里需要说明的就是ReplaceSpecialTemplate(string forumpath,string skinName,....) 这个函数,它的实现我们要到discuz.forum.dll中去找,这里为了方便,直接就将反射出来的代码加上注释贴
出来,大家一看便知:
1public class ForumPageTemplate : PageTemplate
2{
3
4 /// <summary>
5 /// 解析特殊变量
6 /// </summary>
7 /// <param name="skinName">皮肤名</param>
8 /// <param name="strTemplate">模板内容</param>
9 /// <returns></returns>
10 public override string ReplaceSpecialTemplate(string forumpath,string skinName,string strTemplate)
11 {
12 Regex r;
13 Match m;
14
15 StringBuilder sb = new StringBuilder();
16 sb.Append(strTemplate);
17 r = new Regex(@"({([^\[\]/\{\}='\s]+)})", RegexOptions.IgnoreCase|RegexOptions.Multiline|RegexOptions.Compiled);
18 for (m = r.Match(strTemplate); m.Success; m = m.NextMatch())
19 {
20 if (m.Groups[0].ToString() == "{forumversion}")
21 {
22 sb = sb.Replace(m.Groups[0].ToString(), Utils.GetAssemblyVersion());
23 }
24 else if (m.Groups[0].ToString() == "{forumproductname}")
25 {
26 sb = sb.Replace(m.Groups[0].ToString(), Utils.GetAssemblyProductName());
27 }
28 }
29
30 foreach(DataRow dr in GetTemplateVarList(forumpath,skinName).Rows)
31 {
32 sb = sb.Replace(dr["variablename"].ToString().Trim(), dr["variablevalue"].ToString().Trim());
33 }
34 return sb.ToString();
35 }
36
37
38 /// <summary>
39 /// 获取模板内容
40 /// </summary>
41 /// <param name="skinName">皮肤名</param>
42 /// <param name="templateName">模板名</param>
43 /// <param name="nest">嵌套次数</param>
44 /// <param name="templateid">皮肤id</param>
45 /// <returns></returns>
46 public override string GetTemplate(string forumpath,string skinName, string templateName, int nest,int templateid)
47 {
48 return base.GetTemplate(forumpath,skinName,templateName,nest,templateid);
49 }
50
51 /// <summary>
52 /// 获得模板变量列表
53 /// </summary>
54 /// <param name="skinName">皮肤名</param>
55 /// <returns></returns>
56 public static DataTable GetTemplateVarList(string forumpath,string skinName)
57 {
58 Discuz.Cache.DNTCache cache = Discuz.Cache.DNTCache.GetCacheService();
59 DataTable dt = cache.RetrieveSingleObject("/Forum/" + skinName + "/TemplateVariable") as DataTable;
60
61 if(dt != null)
62 {
63 return dt;
64 }
65 else
66 {
67 DataSet dsSrc = new DataSet("template");
68 string[] filename = new string[1] {Utils.GetMapPath(forumpath + "templates/" + skinName + "/templatevariable.xml")};
69
70 if (Utils.FileExists(filename[0]))
71 {
72 dsSrc.ReadXml(filename[0]);
73
74 if (dsSrc.Tables.Count == 0)
75 {
76
77 }
78 }
79 else
80 {
81
82 }
83
84 cache.AddSingleObject("/Forum/" + skinName + "/TemplateVariable", dsSrc.Tables[0], filename);
85 return dsSrc.Tables[0];
86 }
87 }
88}
相信看到这里,熟悉设计模式的朋友会看出来,这里用到了"Template Method"模式,因为这种模式很简单,就不多做介绍了,相关信息可以看一下GOF的那本书或到网上一搜便知。下面要说的就是上面的这个 ForumPageTemplate类目前所要实现的功能。因为模板中要被订制的东西有很多,而我们目前所搭建的功能只是为了生成和转换时使用,当用户有要替换的特殊变量就会出现无法订制的情况。所以才提供了这个类以便实现与模板有关的用户订制需求。当然目录所
提供的功能只是简单的替换而已,但并不排除以后随着用户口味的挑剔而进行升级扩展的可能。
而用户进行特殊变量定制也非常简单,只要在上面所贴的后台“模板列表”图中的后面点击相应的“管理”链接之后就会看到下面的页面,只要再点击右下方的“模板变量列表”,即可以进入定制模板变量的页面,如图:大家只要进行相应操作设置即可。
本文标签:
很赞哦! ()
相关教程
图文教程
discuz核心函数库function_core的函数注释
* 系统错误处理* @param$message 错误信息* @param $show 是否显示信息* @param $save 是否存入日志* @param $halt 是否中断访问
Discuz NT的URL地址重写(URLRewrite) 写法
在Discuz!NT中的前台页面访问(特别是aspx)是被HttpModule接管的,所以大家在Discuz.Web项目的目录下看到的唯一"aspx文件"是index.aspx,而所有其它前台页面都有
DISCUZX1.5主题分类和分类信息写进TITLE的方法
假如DISCUZ X1.5开启了主题分类和分类信息,你会发现主题分类和分类信息列表页的TITLE标题依然统一为“版块名称 - 论坛名称”,假如我要这样“主题分类 - 分类信息 - 版块名称
Discuz的NT模板机制是怎么样的
作为产品中的一大特色,模板机制一经推出,就引来了大家特别是站长们的关注。但它所饱受的风风雨雨也成了那时不少人关注的话题。而今天本人将结合在产品组中的开发经历
相关源码
-
(自适应响应式)法律咨询律师事务所法务pbootcms源码下载为律师事务所、法律咨询机构设计,特别适合展示法律服务、律师团队和成功案例。采用响应式技术,确保在不同设备上都能提供专业的法律信息展示和咨询服务。查看源码 -
(自适应HTML5)响应式智能设备人工智能机器pbootcms源码免费下载这款基于PbootCMS开发的网站模板为人工智能和智能设备行业设计,采用现代化科技风格,突出产品的智能特性和技术创新。模板架构合理,功能完善,能够有效展示各类智能产品的核心功能和解决方案。查看源码 -
帝国cms7.5手游评测资讯礼包合集游戏专区下载网站模板本模板基于帝国CMS系统开发,为手游门户网站设计。支持PC端与移动端同步生成HTML静态页面,内置多端同步生成功能。模板架构针对手业特点优化,满足APP下载、游戏资讯、攻略等内容发布需求。查看源码 -
(自适应响应式)html5文章资讯新闻博客pbootcms网站模板下载本模板基于PbootCMS系统开发,为新闻资讯、博客类网站设计,特别适合各类文章内容的发布与管理。采用响应式技术,确保在不同设备上都能获得良好的阅读体验。查看源码 -
(PC+WAP)餐饮奶茶美食小吃招商加盟pbootcms模板源码下载为茶饮烘焙、小吃快餐等餐饮品牌打造的招商加盟系统,助力品牌快速拓展市场;双端pc+wap设计呈现加盟政策对比表。支持后台实时更新菜品图片、加盟费用等关键信息。查看源码 -
(自适应)英文外贸电子设备网站模板三级子目录基于PbootCMS内核开发的响应式英文网站模板,为外贸企业打造,支持多行业快速适配。通过简洁高效的代码架构,帮助企业低成本构建专业海外形象,实现更好客户触达与订单转化。查看源码
| 分享笔记 (共有 篇笔记) |
