您现在的位置是:首页 > cms教程 > Discuz教程Discuz教程
Discuz!NT模板机制详细分析
盼曼2025-07-11Discuz教程已有人查阅
导读作为产品中的一大特色,模板机制一经推出,就引来了大家特别是站长们的关注。但它所饱受的风风 雨雨也成了那时不少人关注的话题。而今天本人将结合在产品组中的开发经历
作为产品中的一大特色,模板机制一经推出,就引来了大家特别是站长们的关注。但它所饱受的风风 雨雨也成了那时不少人关注的话题。而今天本人将结合在产品组中的开发经历,介绍一下模板机制在设计 使用时的一些体会心得。希望借此陋文,使模板机制揭开“神秘”面纱,为大家在实际设计中提供一些有 价值的参考和建议。
好了,开始今天的话题:)
首先阐述一下模板设计的目标,因为这对于它最终要实现的功能非常重要。考虑到国内大部分站长基 本上都不具备.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):
.....命名空间和类的引用
因为我们在开始设计模板机制时就想到要简化模板代码并提升可重用性,因此要支持子模板机制。 这就类似于设计网页时的页首和页尾,我们在网页引用时,只需要include进来即可,而当修改页首和 页尾时,只须变动相应文件即可。
这里不妨再打开_header.htm(注意子模板名称要用下划线开头),发现内容如下:
即然都走到这一步,不妨再打开_pageheader子模板,正所谓“不撞南墙不回头”嘛:)
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):
.....命名空间和类的引用
1<script runat="server">
2overrideprotectedvoid OnInit(EventArgs e)
3{
4
5 base.OnInit(e);
6
7 templateBuilder.Append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN
8 \" \"");
9 templateBuilder.Append("<html xmlns=\"http:// .w3.org/1999/xhtml\">\r\n");
10 templateBuilder.Append("<head>\r\n");
11 templateBuilder.Append("<meta http-equiv=\"Content-Type\" content=\"text/html;
12 charset=utf-8\" />\r\n");
13 templateBuilder.Append(""+ meta.ToString() +"\r\n");
14 templateBuilder.Append("<title>"+ pagetitle.ToString() +""+
15 config.Seotitle.ToString().Trim() +" - "+
16 config.Forumtitle.ToString().Trim() +" - Powered by Discuz!NT
17 </title>\r\n");
18 templateBuilder.Append("<link rel=\"icon\" href=\"favicon.ico\"
19 type=\"image/x-icon\"/>\r\n");
20 templateBuilder.Append("<link rel=\"shortcut icon\" href=\"favicon.ico\"
21 type=\"image/x-icon\"/>\r\n");
22 templateBuilder.Append("<!-- 调用样式表 -->\r\n");
23 templateBuilder.Append("<link rel=\"stylesheet\" href=\"templates/" +
24 templatepath.ToString() +"/dnt.css\"
25 type=\"text/css\" media=\"all\"/>\r\n");
26 templateBuilder.Append(""+ link.ToString() +"\r\n");
27 templateBuilder.Append("<script type=\"text/javascript\" src=\"templates/" +
28 templatepath.ToString() +"/report.js\"></" + "script>\r\n");
29 templateBuilder.Append("<script type=\"text/javascript\" src=\"templates/" +
30 templatepath.ToString() +"/common.js\"></" + "script>\r\n");
31 templateBuilder.Append("<script type=\"text/javascript\" src=\"editor/common.js\">
32 </" + "script>\r\n");
33 templateBuilder.Append("<script type=\"text/javascript\" src=\"editor/menu.js\">
34 </" + "script>\r\n");
35 templateBuilder.Append(""+ script.ToString() +"\r\n");
36 templateBuilder.Append("</head>\r\n");
相信大家看到这样的aspx页面都会晕上一阵子,直接修改的想法已变得非常不现实了,简直是“不 可能完成的任务”。而实际上,我们并不希望大家或站长来完成这项工作。因为这是系统自动生成的。 而生成的前提就是在template/下的模板“目录”中的HTM文件。还是借用上面的logout,只是这里要看 的是模板目录下同名的logout.htm模板文件。它的内容如下:
1<%template _header%>
2<div id="foruminfo">
3<div class="userinfo">
4<h2><a href="{config.forumurl}">{config.forumtitle}</a><strong>用户退出</strong></h2>
5</div>
6</div>
7<!--TheCurrent end-->
8<%template _msgbox%>
9</div>
10<%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">
有意思,又是一个“子模板”出现在了第一行。不错,我们的机制允许模板被嵌套使用,这样会 使页面的“组装”更加灵活多样。即然都走到这一步,不妨再打开_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:// .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"
4author="Discuz!NT"
5createdate ="2007-11-12"
6ver="1.1112"
7fordntver="2.0"
8copyright="Copyright 2007 Comsenz Inc."/>
9</about>
注: 上图中的那个“乐队演出”图片其实是模板目录下的about.png文件,它相当于一张预览图。需要说明的是上图中不是所有模板都能在前台使用,而是当被标记为“已入库”才可在前台使用, 而入库即数据库,下面就是数据库中的截图:
而接下来要说的,就是模板列表中每个模板后面的“生成”链接所要干的活了。
如果大家手头上有reflector的话,请使用这个工具加载我们官方提供的产品目录下的bin文件夹 中的discuz.common.dll文件,找到 PageTemplate这个类。这里为了便于说明,将反射所得到的代码 加上注释贴出来:
1publicabstractclass PageTemplate
2 {
3publicstatic Regex[] r =new Regex[21];
4
5static PageTemplate()
6{
7
8RegexOptions options = Utils.GetRegexCompiledOptions();
9
10 r[0] =new Regex(@"<%template ([^\[\]\{\}\s]+)%>", options);
11
12 r[1] =new Regex(@"<%loop ((\(([a-zA-Z]+)\) )?)([^\[\]\{\}\s]+) ([^\[\]\{\}\s]+)%>", options);
13
14 r[2] =new Regex(@"<%\/loop%>", options);
15
16 r[3] =new Regex(@"<%while ([^\[\]\{\}\s]+)%>", options);
17
18 r[4] =new Regex(@"<%\/while ([^\[\]\{\}\s]+)%>", options);
19
20 r[5] =new Regex(@"<%if (?:\s*)(([^\s]+)((?:\s*)(\|\||\&\&)(?:\s*)([^\s]+))?)(?:\s*)%>", options);
21
22 r[6] =new Regex(@"<%else(( (?:\s*)if (?:\s*)(([^\s]+)((?:\s*)(\|\||\&\&)(?:\s*)([^\s]+))?))?)(?:\s*)%>", options);
23
24 r[7] =new Regex(@"<%\/if%>", options);
25
26 //解析{var.a}
27 r[8] =new Regex(@"(\{strtoint\(([^\s]+?)\)\})", options);
28
29 //解析{request[a]}
30 r[9] =new Regex(@"(<%urlencode\(([^\s]+?)\)%>)", options);
31
32 //解析{var[a]}
33 r[10] =new Regex(@"(<%datetostr\(([^\s]+?),(.*?)\)%>)", options);
34 r[11] =new Regex(@"(\{([^\.\[\]\{\}\s]+)\.([^\[\]\{\}\s]+)\})", options);
35
36 //解析普通变量{}
37 r[12] =new Regex(@"(\{request\[([^\[\]\{\}\s]+)\]\})", options);
38
39 //解析==表达式
40 r[13] =new Regex(@"(\{([^\[\]\{\}\s]+)\[([^\[\]\{\}\s]+)\]\})", options);
41
42 //解析==表达式
43 r[14] =new Regex(@"({([^\[\]/\{\}='\s]+)})", options);
44
45 //解析普通变量{}
46 r[15] =new Regex(@"({([^\[\]/\{\}='\s]+)})", options);
47
48 //解析==表达式
49 r[16] =new Regex(@"(([=|>|<|!]=)\\"+"\"" + @"([^\s]*)\\" + "\")", options);
50
51 //命名空间
52 r[17] =new Regex(@"<%namespace ([^\[\]\{\}\s]+)%>", options);
53
54 //C#代码
55 r[18] =new Regex(@"<%csharp%>([\s\S]+?)<%/csharp%>", options);
56
57 //set标签
58 r[19] =new Regex(@"<%set ((\(([a-zA-Z]+)\))?)(?:\s*)\{([^\s]+)\}(?:\s*)=(?:\s*)(.*?)(?:\s*)%>", options);
59
60r[20] =new Regex(@"(<%getsubstring\(([^\s]+?),(.\d*?),(.\d*?),([^\s]+?)\)%>)", options);
61}
62
63
64///<summary>
65/// 获得模板字符串. 首先查找缓存. 如果不在缓存中则从设置中的模板路径来读取模板文件.
66/// 模板文件的路径在Web.config文件中设置.
67/// 如果读取文件成功则会将内容放于缓存中.
68///</summary>
69///<param name="skinName">模板名</param>
70///<param name="templateName">模板文件的文件名称, 也是缓存中的模板名称.</param>
71///<param name="nest">嵌套次数</param>
72///<param name="templateid">模板id</param>
73///<returns>string值,如果失败则为"",成功则为模板内容的string</returns>
74publicvirtualstring GetTemplate(string forumpath,string skinName, string templateName, int nest,int templateid)
75{
76 StringBuilder strReturn =new StringBuilder();
77 if (nest <1)
78 {
79nest =1;
80 }
81 elseif (nest >5)
82 {
83return"";
84 }
85
86
87 string extNamespace ="";
88 string pathFormatStr ="{0}{1}{2}{3}{4}.htm";
89string filePath =string.Format(pathFormatStr, Utils.GetMapPath(forumpath +"templates"), System.IO.Path.DirectorySeparatorChar, skinName, System.IO.Path.DirectorySeparatorChar, templateName);
90
91 //如果指定风格的模板文件不存在
92 if (!System.IO.File.Exists(filePath))
93 {
94//默认风格的模板是否存在
95filePath =string.Format(pathFormatStr, Utils.GetMapPath(forumpath +"templates"), System.IO.Path.DirectorySeparatorChar, "default", System.IO.Path.DirectorySeparatorChar, templateName);
96if (!System.IO.File.Exists(filePath))
97{
98 return"";
99}
100 }
101 using(System.IO.StreamReader objReader =new System.IO.StreamReader(filePath, Encoding.UTF8))
102 {
103System.Text.StringBuilder textOutput =new System.Text.StringBuilder();
104
105textOutput.Append(objReader.ReadToEnd());
106objReader.Close();
107
108//处理命名空间
109if (nest ==1)
110{
111 //命名空间
112 foreach (Match m in r[17].Matches(textOutput.ToString()))
113 {
114extNamespace +="\r\n<%@ Import namespace=\"" + m.Groups[1].ToString() + "\" %>";
115textOutput.Replace(m.Groups[0].ToString(), string.Empty);
116 }
117
118}
119//处理Csharp语句
120foreach (Match m in r[18].Matches(textOutput.ToString()))
121{
122 //csharpCode += "\r\n" + m.Groups[1].ToString() + "\r\n";
123 textOutput.Replace(m.Groups[0].ToString(), m.Groups[0].ToString().Replace("\r\n", "\r\t\r"));
124}
125
126textOutput.Replace("\r\n", "\r\r\r");
127textOutput.Replace("<%", "\r\r\n<%");
128textOutput.Replace("%>", "%>\r\r\n");
129
130textOutput.Replace("<%csharp%>\r\r\n", "<%csharp%>").Replace("\r\r\n<%/csharp%>", "<%/csharp%>");
131
132
133string[] strlist = Utils.SplitString(textOutput.ToString(), "\r\r\n");
134int count = strlist.GetUpperBound(0);
135
136for (int i =0; i <= count; i++)
137{
138 strReturn.Append(ConvertTags(nest,forumpath, skinName, strlist, templateid));
139}
140 }
141 if (nest ==1)
142 {
143string template =string.Format("<%@ Page language=\"c#\" Codebehind=\"{0}.aspx.cs\" AutoEventWireup=\"false\" EnableViewState=\"false\" Inherits=\"Discuz.ForumPage.{0}\" %>\r\n<%@ Import namespace=\"System.Data\" %>\r\n<%@ Import namespace=\"Discuz.Common\" %>\r\n<%@ Import namespace=\"Discuz.Forum\" %>\r\n<%@ Import namespace=\"Discuz.Entity\" %>\r\n{1}\r\n<script runat=\"server\">\r\noverride protected void OnInit(EventArgs e)\r\n{{\r\n\r\n\t/* \r\n\t\tThis page was created by Discuz!NT Template Engine at {2}.\r\n\t\t本页面代码由Discuz!NT模板引擎生成于 {2}. \r\n\t*/\r\n\r\n\tbase.OnInit(e);\r\n{3}\r\n\tResponse.Write(templateBuilder.ToString());\r\n}}\r\n</script>\r\n", templateName, extNamespace, DateTime.Now.ToString(), strReturn.ToString());
144
145string pageDir = Utils.GetMapPath(forumpath +"aspx\\"+ templateid.ToString() +"\\");
146if (!Directory.Exists(pageDir))
147{
148 Utils.CreateDir(pageDir);
149}
150
151string outputPath = pageDir+ templateName +".aspx";
152
153
154
155using (FileStream fs =new FileStream(outputPath, FileMode.Create,FileAccess.ReadWrite, FileShare.ReadWrite))
156{
157 Byte[] info = System.Text.Encoding.UTF8.GetBytes(template);
158 fs.Write(info, 0, info.Length);
159 fs.Close();
160}
161
162 }
163 return strReturn.ToString();
164}
165
166///<summary>
167/// 转换标签
168///</summary>
169///<param name="nest">深度</param>
170///<param name="skinName">模板名称</param>
171///<param name="inputStr">模板内容</param>
172///<param name="templateid">模板id</param>
173///<returns></returns>
174privatestring ConvertTags(int nest,string forumpath, string skinName, string inputStr, int templateid)
175{
176 string strReturn ="";
177 bool IsCodeLine;
178 string strTemplate;
179 strTemplate = inputStr.Replace("\\", "\\\\");
180 strTemplate = strTemplate.Replace("\"", "\\\"");
181 strTemplate = strTemplate.Replace("</script>", "</\"+ \"script>");
182 IsCodeLine =false;
183
184
185 foreach (Match m in r[0].Matches(strTemplate))
186 {
187IsCodeLine =true;
188strTemplate = strTemplate.Replace(m.Groups[0].ToString(), "\r\n"+ GetTemplate(forumpath,skinName, m.Groups[1].ToString(), nest +1, templateid) +"\r\n");
189 }
190
191 foreach (Match m in r[1].Matches(strTemplate))
192 {
193IsCodeLine =true;
194if (m.Groups[3].ToString() =="")
195{
196 strTemplate = strTemplate.Replace(m.Groups[0].ToString(),
197string.Format("\r\n\tint {0}__loop__id=0;\r\n\tforeach(DataRow {0} in {1}.Rows)\r\n\t{{\r\n\t\t{0}__loop__id++;\r\n", m.Groups[4].ToString(), m.Groups[5].ToString()));
198}
199else
200{
201 strTemplate = strTemplate.Replace(m.Groups[0].ToString(),
202string.Format("\r\n\tint {1}__loop__id=0;\r\n\tforeach({0} {1} in {2})\r\n\t{{\r\n\t\t{1}__loop__id++;\r\n", m.Groups[3].ToString(), m.Groups[4].ToString(), m.Groups[5].ToString()));
203}
204 }
205
206
207
208
209
210
211 if (IsCodeLine)
212 {
213strReturn = strTemplate +"\r\n";
214 }
215 else
216 {
217if (strTemplate.Trim() !="")
218{
219 StringBuilder sb =new StringBuilder();
220 foreach (string temp in Utils.SplitString(strTemplate,"\r\r\r"))
221 {
222if (temp.Trim() =="")
223 continue;
224sb.Append("\ttemplateBuilder.Append(\"" + temp + "\\r\\n\");\r\n");
225 }
226 strReturn = sb.ToString();
227}
228 }
229 return strReturn;
230}
231
232
233
234///<summary>
235/// 解析特殊变量
236///</summary>
237///<returns></returns>
238publicabstractstring ReplaceSpecialTemplate(string forumpath,string skinName,string strTemplate);
239 }
基本上都是对正则式的使用,因为本人不是这方面的高手,所以就不多说了,相信开源之后大家拿 源码和注释一看便知:)这里需要说明的就是ReplaceSpecialTemplate(string forumpath,string skinName,....) 这个函 数,它的实现我们要到discuz.forum.dll中去找,这里为了方便,直接就将反射出来的代码加上注释贴 出来,大家一看便知:
publicclass ForumPageTemplate : PageTemplate
2{
3
4 ///<summary>
5 /// 解析特殊变量
6 ///</summary>
7 ///<param name="skinName">皮肤名</param>
8 ///<param name="strTemplate">模板内容</param>
9 ///<returns></returns>
10publicoverridestring ReplaceSpecialTemplate(string forumpath,string skinName,string strTemplate)
11 {
12Regex r;
13Match m;
14
15StringBuilder sb =new StringBuilder();
16sb.Append(strTemplate);
17r =new Regex(@"({([^\[\]/\{\}='\s]+)})", RegexOptions.IgnoreCase|RegexOptions.Multiline|RegexOptions.Compiled);
18for (m = r.Match(strTemplate); m.Success; m = m.NextMatch())
19{
20 if (m.Groups[0].ToString() =="{forumversion}")
21 {
22sb = sb.Replace(m.Groups[0].ToString(), Utils.GetAssemblyVersion());
23 }
24 elseif (m.Groups[0].ToString() =="{forumproductname}")
25 {
26sb = sb.Replace(m.Groups[0].ToString(), Utils.GetAssemblyProductName());
27 }
28}
29
30foreach(DataRow dr in GetTemplateVarList(forumpath,skinName).Rows)
31{
32 sb = sb.Replace(dr["variablename"].ToString().Trim(), dr["variablevalue"].ToString().Trim());
33}
34return 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>
46publicoverridestring GetTemplate(string forumpath,string skinName, string templateName, int nest,int templateid)
47 {
48returnbase.GetTemplate(forumpath,skinName,templateName,nest,templateid);
49 }
50
51 ///<summary>
52 /// 获得模板变量列表
53 ///</summary>
54 ///<param name="skinName">皮肤名</param>
55 ///<returns></returns>
56publicstatic DataTable GetTemplateVarList(string forumpath,string skinName)
57 {
58Discuz.Cache.DNTCache cache = Discuz.Cache.DNTCache.GetCacheService();
59DataTable dt = cache.RetrieveSingleObject("/Forum/"+ skinName +"/TemplateVariable") as DataTable;
60
61if(dt !=null)
62{
63 return dt;
64}
65else
66{
67 DataSet dsSrc =new DataSet("template");
68 string[] filename =newstring[1] {Utils.GetMapPath(forumpath +"templates/"+ skinName +"/templatevariable.xml")};
69
70 if (Utils.FileExists(filename[0]))
71 {
72 dsSrc.ReadXml(filename[0]);
73
74if (dsSrc.Tables.Count ==0)
75{
76
77}
78 }
79 else
80 {
81
82 }
83
84cache.AddSingleObject("/Forum/"+ skinName +"/TemplateVariable", dsSrc.Tables[0], filename);
85 return dsSrc.Tables[0];
86}
87 }
88}
相信看到这里,熟悉设计模式的朋友会看出来,这里用到了"Template Method"模式,因为这 种模式很简单,就不多做介绍了,相关信息可以看一下GOF的那本书或到网上一搜便知。下面要说的就是上面的这个 ForumPageTemplate类目前所要实现的功能。因为模板中要被订制 的东西有很多,而我们目前所搭建的功能只是为了生成和转换时使用,当用户有要替换的特殊变量 就会出现无法订制的情况。所以才提供了这个类以便实现与模板有关的用户订制需求。当然目录所 提供的功能只是简单的替换而已,但并不排除以后随着用户口味的挑剔而进行升级扩展的可能。
而用户进行特殊变量定制也非常简单,只要在上面所贴的后台“模板列表”图中的后面点击相 应的“管理”链接之后就会看到下面的页面,只要再点击右下方的“模板变量列表”,即可以进入定制模板变量的页面
本文标签:
很赞哦! ()
图文教程
配置Discuz与微信小程序开发环境的步骤介绍
在现代网络开发中,Discuz与微信小程序的结合是一个非常有趣的组合。它们可以让你的社区更加活跃,用户体验更为流畅。那么,如何配置Discuz与微信小程序开发环境呢?
Discuz的NT模板机制是怎么样的
作为产品中的一大特色,模板机制一经推出,就引来了大家特别是站长们的关注。但它所饱受的风风雨雨也成了那时不少人关注的话题。而今天本人将结合在产品组中的开发经历
C#版本的discuz authcode函数代码
根据网上流传甚广的一个版本修改,修正了设置加密串过期时间expiry没有效果的问题。usingSystem;using System.Collections.Generic;
百度PING快速实现百度秒收录discuz的方法
一、利用百度搜索引擎的PING RPC2服务功能来达到搜索引擎快速收录帖子(推荐)实现原理:根据百度站长平台的提示,网站程序可以通过PING RPC2服务自动提醒百度搜索引擎收录你的网
相关源码
-
(PC+WAP)生活资讯百科新闻门户类pbootcms网站模板为生活资讯、百科门户类企业打造的高性能网站模板,基于PbootCMS开源内核开发,采用HTML5响应式架构,PC与手机端实时数据同步,覆盖全终端用户浏览场景。查看源码 -
自适应LED照明外贸灯具灯泡灯具英文网站模板该外贸灯具网站模板专为LED照明、灯具出口企业定制,采用PbootCMS内核开发,提供高效建站方案。通过响应式设计和SEO优化能力,帮助企业低成本构建专业外贸展示平台。查看源码 -
(自适应)蓝色英文外贸电子科技产品带三级栏目网站模板为外贸企业设计的英文网站模板,基于PbootCMS系统开发。突出多语言支持和国际化布局,三级栏目结构清晰展示产品分类,响应式设计确保更好客户在移动端和PC端获得一致的专业体验。查看源码 -
(自适应响应式)房产合同知识产权企业管理pbootcms模板下载本模板基于PbootCMS系统开发,为知识产权服务、法律咨询及企业合同管理等行业设计。采用严谨专业的布局风格,突出法律文书与知识产权服务行业特色,适合展示各类法律服务和知识产权相关内容。查看源码 -
帝国CMS7.5H5小游戏模板游戏攻略下载网整站源码本模板基于帝国CMS系统开发,为H5小游戏和APP应用资讯类网站设计。模板架构针对小游戏行业特点优化,支持游戏发布、资讯分享、应用推荐等功能,满足各类小游戏门户网站的建设需求。查看源码 -
(PC+WAP)蓝色电缆桥架五金钢结构机械PbootCMS模板下载采用PC与WAP双端适配设计,满足桌面设备和移动端访问需求。专注服务于电缆桥架、钢结构及五金机械制造领域,通过结构化布局展示产品特性与技术参数,后台数据一体化管理提升内容维护效率。查看源码
| 分享笔记 (共有 篇笔记) |
