Discuz!NT 模板机制分析

article/2025/11/8 9:19:38

作为产品中的一大特色,模板机制一经推出,就引来了大家特别是站长们的关注。但它所饱受的风风
雨雨也成了那时不少人关注的话题。而今天本人将结合在产品组中的开发经历,介绍一下模板机制在设计
使用时的一些体会心得。希望借此陋文,使模板机制揭开“神秘”面纱,为大家在实际设计中提供一些有
价值的参考和建议。

好了,开始今天的话题:)

首先阐述一下模板设计的目标,因为这对于它最终要实现的功能非常重要。考虑到国内大部分站长基
本上都不具备.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 < scriptrunat = " server " >
2 override protected void OnInit(EventArgse)
3 {
4
5base.OnInit(e);
6
7templateBuilder.Append("<!DOCTYPEhtmlPUBLIC/"-//W3C//DTDXHTML1.0Transitional//EN
8/"/"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd/">/r/n");
9templateBuilder.Append("<htmlxmlns=/"http://www.w3.org/1999/xhtml/">/r/n");
10templateBuilder.Append("<head>/r/n");
11templateBuilder.Append("<metahttp-equiv=/"Content-Type/"content=/"text/html;
12charset=utf-8/"/>/r/n");
13templateBuilder.Append(""+meta.ToString()+"/r/n");
14templateBuilder.Append("<title>"+pagetitle.ToString()+""+
15config.Seotitle.ToString().Trim()+"-"+
16config.Forumtitle.ToString().Trim()+"-PoweredbyDiscuz!NT
17</title>/r/n");
18templateBuilder.Append("<linkrel=/"icon/"href=/"favicon.ico/"
19type=/"image/x-icon/"/>/r/n");
20templateBuilder.Append("<linkrel=/"shortcuticon/"href=/"favicon.ico/"
21type=/"image/x-icon/"/>/r/n");
22templateBuilder.Append("<!--调用样式表-->/r/n");
23templateBuilder.Append("<linkrel=/"stylesheet/"href=/"templates/"+
24templatepath.ToString()+"/dnt.css/"
25type=/"text/css/"media=/"all/"/>/r/n");
26templateBuilder.Append(""+link.ToString()+"/r/n");
27templateBuilder.Append("<scripttype=/"text/javascript/"src=/"templates/"+
28templatepath.ToString()+"/report.js/"></"+"script>/r/n");
29templateBuilder.Append("<scripttype=/"text/javascript/"src=/"templates/"+
30templatepath.ToString()+"/common.js/"></"+"script>/r/n");
31templateBuilder.Append("<scripttype=/"text/javascript/"src=/"editor/common.js/">
32</"+"script>/r/n");
33templateBuilder.Append("<scripttype=/"text/javascript/"src=/"editor/menu.js/">
34</"+"script>/r/n");
35templateBuilder.Append(""+script.ToString()+"/r/n");
36templateBuilder.Append("</head>/r/n");
37
38
39

相信大家看到这样的aspx页面都会晕上一阵子,直接修改的想法已变得非常不现实了,简直是“不
可能完成的任务”。而实际上,我们并不希望大家或站长来完成这项工作。因为这是系统自动生成的。
而生成的前提就是在template/下的模板“目录”中的HTM文件。还是借用上面的logout,只是这里要看
的是模板目录下同名的logout.htm模板文件。它的内容如下:

1 <% template_header %>
2 < divid = " foruminfo " >
3 < div class = " userinfo " >
4 < h2 >< ahref = " {config.forumurl} " > {config.forumtitle} </ a > < strong > 用户退出 </ strong ></ h2 >
5 </ div >
6 </ div >
7 <!-- TheCurrentend -->
8 <% template_msgbox %>
9 </ div >
10 <% template_footer %>
11


大家可能会说,难道就是这几行就实现了上面aspx页面的内容吗?当然不是了,请大家注意:

1 <% template_header %>
2

这一行,其实就是告诉模板页面生成器: 这是一个子模板。

因为我们在开始设计模板机制时就想到要简化模板代码并提升可重用性,因此要支持子模板机制。
这就类似于设计网页时的页首和页尾,我们在网页引用时,只需要include进来即可,而当修改页首和
页尾时,只须变动相应文件即可。

这里不妨再打开_header.htm(注意子模板名称要用下划线开头),发现内容如下:

1 <% template_pageheader %>
2 < body >
3 < divid = " append_parent " ></ div >
4 < divid = " container " >
5 <!-- headerstart -->
6 < divid = " header " >
7 .
8
9

有意思,又是一个“子模板”出现在了第一行。不错,我们的机制允许模板被嵌套使用,这样会
使页面的“组装”更加灵活多样。

即然都走到这一步,不妨再打开_pageheader子模板,正所谓“不撞南墙不回头”嘛:)

1 <! DOCTYPEhtmlPUBLIC " -//W3C//DTDXHTML1.0Transitional//EN " " http://www.w3.org/TR
2 / xhtml1 / DTD / xhtml1 - transitional.dtd " >
3 < htmlxmlns = " http://www.w3.org/1999/xhtml " >
4 < head >
5 < metahttp - equiv = " Content-Type " content = " text/html;charset=utf-8 " />
6 {meta}
7 < title > {pagetitle} {config.seotitle} - {config.forumtitle} - PoweredbyDiscuz ! NT </ title >
8 < linkrel = " icon " href = " favicon.ico " type = " image/x-icon " />
9 < linkrel = " shortcuticon " href = " favicon.ico " type = " image/x-icon " />
10 <!-- 调用样式表 -->
11 < linkrel = " stylesheet " href = " templates/{templatepath}/dnt.css " type = " text/css " media = " all " />
12 {link}
13 < scripttype = " text/javascript " src = " templates/{templatepath}/report.js " ></ script >
14 < scripttype = " text/javascript " src = " templates/{templatepath}/common.js " ></ script >
15 < scripttype = " text/javascript " src = " editor/common.js " ></ script >
16 < scripttype = " text/javascript " src = " editor/menu.js " ></ script >
17 {script}
18 </ head >
19
20

折腾了一圈,到这里出现了上面aspx页中的对应内容,有意思吧,不过里面的{pagetitle}和{
config.seotitle}以及{config.forumtitle}这样的东东又是什么呢? 其实非常简单,这就是按照模
板语法格式所书写的代码,因为这两处在模板生成之后会变成

1 templateBuilder.Append( " <title> " + pagetitle.ToString() + " " +
2 config.Seotitle.ToString().Trim() + " - " +
3 config.Forumtitle.ToString().Trim() + " -PoweredbyDiscuz!NT
4 </ title > /r/n " );

好了,到了这里我们应该清楚了,以后要修改前台页面的一个标准流程:

1.按模板语法修改相应的模板文件夹下的模板文件;
2.在后台生成或使用官方的模板生成器生成相应aspx页面即可;

其实流程非常简单,相信即使不懂aspx开发的朋友也会很快适应并上手。前提就是要了解模板语
法,除了上面所说的以外,还有一些常用的语法如下图:


这里不妨引用官方文档中的链接,里面的说明会更清楚:)

相关链接如下:http://nt.discuz.net/download/doc/dnt_2_skindoc.zip

好了,目前我们只是知道了如使使用和修改它,但所谓的“模板生成”机制又是个什么样子呢!
必定到这里我们只走完了一半旅途,下面将会介绍模板的生成机制。


首先要看一下后台的模板(列表)管理界面,如下图:

从上图可知道,模板是按名称(目录)来进行管理的,而每个模板都有名称,存放路径,版权,
作者等相关信息。而这此信息都是来自于每个模板(目录)下的about.xml文件,这里将它的内容贴
出来:

1 <? xmlversion = " 1.0 " encoding = " utf-8 " ?>
2 < about >
3 < templatename = " basic "
4 author = " Discuz!NT "
5 createdate = " 2007-11-12 "
6 ver = " 1.1112 "
7 fordntver = " 2.0 "
8 copyright = " Copyright2007ComsenzInc. " />
9 </ about >
10
11

注: 上图中的那个“乐队演出”图片其实是模板目录下的about.png文件,它相当于一张预览图。

需要说明的是上图中不是所有模板都能在前台使用,而是当被标记为“已入库”才可在前台使用,
而入库即数据库,下面就是数据库中的截图:



而接下来要说的,就是模板列表中每个模板后面的“生成”链接所要干的活了。

如果大家手头上有reflector的话,请使用这个工具加载我们官方提供的产品目录下的bin文件夹
中的discuz.common.dll文件,找到 PageTemplate这个类。这里为了便于说明,将反射所得到的代码
加上注释贴出来:

Code
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->1publicabstractclassPageTemplate
2{
3publicstaticRegex[]r=newRegex[21];
4
5staticPageTemplate()
6{
7
8RegexOptionsoptions=Utils.GetRegexCompiledOptions();
9
10r[0]=newRegex(@"<%template([^/[/]/{/}/s]+)%>",options);
11
12r[1]=newRegex(@"<%loop((/(([a-zA-Z]+)/))?)([^/[/]/{/}/s]+)([^/[/]/{/}/s]+)%>",options);
13
14r[2]=newRegex(@"<%//loop%>",options);
15
16r[3]=newRegex(@"<%while([^/[/]/{/}/s]+)%>",options);
17
18r[4]=newRegex(@"<%//while([^/[/]/{/}/s]+)%>",options);
19
20r[5]=newRegex(@"<%if(?:/s*)(([^/s]+)((?:/s*)(/|/||/&/&)(?:/s*)([^/s]+))?)(?:/s*)%>",options);
21
22r[6]=newRegex(@"<%else(((?:/s*)if(?:/s*)(([^/s]+)((?:/s*)(/|/||/&/&)(?:/s*)([^/s]+))?))?)(?:/s*)%>",options);
23
24r[7]=newRegex(@"<%//if%>",options);
25
26//解析{var.a}
27r[8]=newRegex(@"(/{strtoint/(([^/s]+?)/)/})",options);
28
29//解析{request[a]}
30r[9]=newRegex(@"(<%urlencode/(([^/s]+?)/)%>)",options);
31
32//解析{var[a]}
33r[10]=newRegex(@"(<%datetostr/(([^/s]+?),(.*?)/)%>)",options);
34r[11]=newRegex(@"(/{([^/./[/]/{/}/s]+)/.([^/[/]/{/}/s]+)/})",options);
35
36//解析普通变量{}
37r[12]=newRegex(@"(/{request/[([^/[/]/{/}/s]+)/]/})",options);
38
39//解析==表达式
40r[13]=newRegex(@"(/{([^/[/]/{/}/s]+)/[([^/[/]/{/}/s]+)/]/})",options);
41
42//解析==表达式
43r[14]=newRegex(@"({([^/[/]//{/}='/s]+)})",options);
44
45//解析普通变量{}
46r[15]=newRegex(@"({([^/[/]//{/}='/s]+)})",options);
47
48//解析==表达式
49r[16]=newRegex(@"(([=|>|<|!]=)//"+"/""+@"([^/s]*)//"+"/")",options);
50
51//命名空间
52r[17]=newRegex(@"<%namespace([^/[/]/{/}/s]+)%>",options);
53
54//C#代码
55r[18]=newRegex(@"<%csharp%>([/s/S]+?)<%/csharp%>",options);
56
57//set标签
58r[19]=newRegex(@"<%set((/(([a-zA-Z]+)/))?)(?:/s*)/{([^/s]+)/}(?:/s*)=(?:/s*)(.*?)(?:/s*)%>",options);
59
60r[20]=newRegex(@"(<%getsubstring/(([^/s]+?),(./d*?),(./d*?),([^/s]+?)/)%>)",options);
61}

62
63
64/**////<summary>
65///获得模板字符串.首先查找缓存.如果不在缓存中则从设置中的模板路径来读取模板文件.
66///模板文件的路径在Web.config文件中设置.
67///如果读取文件成功则会将内容放于缓存中.
68///</summary>
69///<paramname="skinName">模板名</param>
70///<paramname="templateName">模板文件的文件名称,也是缓存中的模板名称.</param>
71///<paramname="nest">嵌套次数</param>
72///<paramname="templateid">模板id</param>
73///<returns>string值,如果失败则为"",成功则为模板内容的string</returns>

74publicvirtualstringGetTemplate(stringforumpath,stringskinName,stringtemplateName,intnest,inttemplateid)
75{
76StringBuilderstrReturn=newStringBuilder();
77if(nest<1)
78{
79nest=1;
80}

81elseif(nest>5)
82{
83return"";
84}

85
86
87stringextNamespace="";
88stringpathFormatStr="{0}{1}{2}{3}{4}.htm";
89stringfilePath=string.Format(pathFormatStr,Utils.GetMapPath(forumpath+"templates"),System.IO.Path.DirectorySeparatorChar,skinName,System.IO.Path.DirectorySeparatorChar,templateName);
90
91//如果指定风格的模板文件不存在
92if(!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{
98return"";
99}

100}

101using(System.IO.StreamReaderobjReader=newSystem.IO.StreamReader(filePath,Encoding.UTF8))
102{
103System.Text.StringBuildertextOutput=newSystem.Text.StringBuilder();
104
105textOutput.Append(objReader.ReadToEnd());
106objReader.Close();
107
108//处理命名空间
109if(nest==1)
110{
111//命名空间
112foreach(Matchminr[17].Matches(textOutput.ToString()))
113{
114extNamespace+="/r/n<%@Importnamespace=/""+m.Groups[1].ToString()+"/"%>";
115textOutput.Replace(m.Groups[0].ToString(),string.Empty);
116}

117
118}

119//处理Csharp语句
120foreach(Matchminr[18].Matches(textOutput.ToString()))
121{
122//csharpCode+="/r/n"+m.Groups[1].ToString()+"/r/n";
123textOutput.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");
134intcount=strlist.GetUpperBound(0);
135
136for(inti=0;i<=count;i++)
137{
138strReturn.Append(ConvertTags(nest,forumpath,skinName,strlist[i],templateid));
139}

140}

141if(nest==1)
142{
143stringtemplate=string.Format("<%@Pagelanguage=/"c#/"Codebehind=/"{0}.aspx.cs/"AutoEventWireup=/"false/"EnableViewState=/"false/"Inherits=/"Discuz.ForumPage.{0}/"%>/r/n<%@Importnamespace=/"System.Data/"%>/r/n<%@Importnamespace=/"Discuz.Common/"%>/r/n<%@Importnamespace=/"Discuz.Forum/"%>/r/n<%@Importnamespace=/"Discuz.Entity/"%>/r/n{1}/r/n<scriptrunat=/"server/">/r/noverrideprotectedvoidOnInit(EventArgse)/r/n{{/r/n/r/n/t/*/r/n/t/tThispagewascreatedbyDiscuz!NTTemplateEngineat{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
145stringpageDir=Utils.GetMapPath(forumpath+"aspx//"+templateid.ToString()+"//");
146if(!Directory.Exists(pageDir))
147{
148Utils.CreateDir(pageDir);
149}

150
151stringoutputPath=pageDir+templateName+".aspx";
152
153
154
155using(FileStreamfs=newFileStream(outputPath,FileMode.Create,FileAccess.ReadWrite,FileShare.ReadWrite))
156{
157Byte[]info=System.Text.Encoding.UTF8.GetBytes(template);
158fs.Write(info,0,info.Length);
159fs.Close();
160}

161
162}

163returnstrReturn.ToString();
164}

165
166/**////<summary>
167///转换标签
168///</summary>
169///<paramname="nest">深度</param>
170///<paramname="skinName">模板名称</param>
171///<paramname="inputStr">模板内容</param>
172///<paramname="templateid">模板id</param>
173///<returns></returns>

174privatestringConvertTags(intnest,stringforumpath,stringskinName,stringinputStr,inttemplateid)
175{
176stringstrReturn="";
177boolIsCodeLine;
178stringstrTemplate;
179strTemplate=inputStr.Replace("//","");
180strTemplate=strTemplate.Replace("/"","///"");
181strTemplate=strTemplate.Replace("</script>","<//"+/"script>");
182IsCodeLine=false;
183
184
185foreach(Matchminr[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
191foreach(Matchminr[1].Matches(strTemplate))
192{
193IsCodeLine=true;
194if(m.Groups[3].ToString()=="")
195{
196strTemplate=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{
201strTemplate=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
211if(IsCodeLine)
212{
213strReturn=strTemplate+"/r/n";
214}

215else
216{
217if(strTemplate.Trim()!="")
218{
219StringBuildersb=newStringBuilder();
220foreach(stringtempinUtils.SplitString(strTemplate,"/r/r/r"))
221{
222if(temp.Trim()=="")
223continue;
224sb.Append("/ttemplateBuilder.Append(/""+temp+"//r//n/");/r/n");
225}

226strReturn=sb.ToString();
227}

228}

229returnstrReturn;
230}

231
232
233
234/**////<summary>
235///解析特殊变量
236///</summary>
237///<returns></returns>

238publicabstractstringReplaceSpecialTemplate(stringforumpath,stringskinName,stringstrTemplate);
239}

240
241


基本上都是对正则式的使用,因为本人不是这方面的高手,所以就不多说了,相信开源之后大家拿
源码和注释一看便知:)

这里需要说明的就是ReplaceSpecialTemplate(string forumpath,string skinName,....) 这个函
数,它的实现我们要到discuz.forum.dll中去找,这里为了方便,直接就将反射出来的代码加上注释贴
出来,大家一看便知:

1 public class ForumPageTemplate:PageTemplate
2 {
3
4/**////<summary>
5///解析特殊变量
6///</summary>
7///<paramname="skinName">皮肤名</param>
8///<paramname="strTemplate">模板内容</param>
9///<returns></returns>

10publicoverridestringReplaceSpecialTemplate(stringforumpath,stringskinName,stringstrTemplate)
11{
12Regexr;
13Matchm;
14
15StringBuildersb=newStringBuilder();
16sb.Append(strTemplate);
17r=newRegex(@"({([^/[/]//{/}='/s]+)})",RegexOptions.IgnoreCase|RegexOptions.Multiline|RegexOptions.Compiled);
18for(m=r.Match(strTemplate);m.Success;m=m.NextMatch())
19{
20if(m.Groups[0].ToString()=="{forumversion}")
21{
22sb=sb.Replace(m.Groups[0].ToString(),Utils.GetAssemblyVersion());
23}

24elseif(m.Groups[0].ToString()=="{forumproductname}")
25{
26sb=sb.Replace(m.Groups[0].ToString(),Utils.GetAssemblyProductName());
27}

28}

29
30foreach(DataRowdrinGetTemplateVarList(forumpath,skinName).Rows)
31{
32sb=sb.Replace(dr["variablename"].ToString().Trim(),dr["variablevalue"].ToString().Trim());
33}

34returnsb.ToString();
35}

36
37
38/**////<summary>
39///获取模板内容
40///</summary>
41///<paramname="skinName">皮肤名</param>
42///<paramname="templateName">模板名</param>
43///<paramname="nest">嵌套次数</param>
44///<paramname="templateid">皮肤id</param>
45///<returns></returns>

46publicoverridestringGetTemplate(stringforumpath,stringskinName,stringtemplateName,intnest,inttemplateid)
47{
48returnbase.GetTemplate(forumpath,skinName,templateName,nest,templateid);
49}

50
51/**////<summary>
52///获得模板变量列表
53///</summary>
54///<paramname="skinName">皮肤名</param>
55///<returns></returns>

56publicstaticDataTableGetTemplateVarList(stringforumpath,stringskinName)
57{
58Discuz.Cache.DNTCachecache=Discuz.Cache.DNTCache.GetCacheService();
59DataTabledt=cache.RetrieveSingleObject("/Forum/"+skinName+"/TemplateVariable")asDataTable;
60
61if(dt!=null)
62{
63returndt;
64}

65else
66{
67DataSetdsSrc=newDataSet("template");
68string[]filename=newstring[1]{Utils.GetMapPath(forumpath+"templates/"+skinName+"/templatevariable.xml")};
69
70if(Utils.FileExists(filename[0]))
71{
72dsSrc.ReadXml(filename[0]);
73
74if(dsSrc.Tables.Count==0)
75{
76
77}

78}

79else
80{
81
82}

83
84cache.AddSingleObject("/Forum/"+skinName+"/TemplateVariable",dsSrc.Tables[0],filename);
85returndsSrc.Tables[0];
86}

87}

88}

89
90


相信看到这里,熟悉设计模式的朋友会看出来,这里用到了"Template Method"模式,因为这
种模式很简单,就不多做介绍了,相关信息可以看一下GOF的那本书或到网上一搜便知。

下面要说的就是上面的这个 ForumPageTemplate类目前所要实现的功能。因为模板中要被订制
的东西有很多,而我们目前所搭建的功能只是为了生成和转换时使用,当用户有要替换的特殊变量
就会出现无法订制的情况。所以才提供了这个类以便实现与模板有关的用户订制需求。当然目录所
提供的功能只是简单的替换而已,但并不排除以后随着用户口味的挑剔而进行升级扩展的可能。

而用户进行特殊变量定制也非常简单,只要在上面所贴的后台“模板列表”图中的后面点击相
应的“管理”链接之后就会看到下面的页面,如图:



只要再点击右下方的“模板变量列表”,即可以进入定制模板变量的页面,如图:


大家只要进行相应操作设置即可。


好了,关于模板机制的介绍,这里就先告一段落了。有问题的朋友可以在回复中进行交流和发
EMAIL给我(daizhj617595@126.com,daizhj@gmail.com,daizhj@discuz.com)。


http://chatgpt.dhexx.cn/article/hjWSdxCx.shtml

相关文章

华硕装鸿蒙系统,智能家居 篇八:解决华硕路由器设置不当造成传感器延迟

智能家居 篇八:解决华硕路由器设置不当造成传感器延迟 2020-04-11 16:20:42 8点赞 61收藏 16评论 2019年底,因为自己无意操作(自己有强迫症,遇到升级提醒必点),将手机上的“HomeAssistant”APP升级到2.0.0(69)版,导致的直接结果就是手机客户端无连接HomeAssistant系统的0.…

php如何改变导航栏颜色,怎么改变导航栏的颜色啊? - Discuz!-安装使用 - Discuz! 官方站 - Powered by Discuz!...

/* ---------------------------------- Main CSS file for Discuz! X (C) Comsenz Inc. http://www.comsenz.com Created & Modified by Lushnis, Pony, Dfox & DragonLee. ---------------------------------- 结构目录&#xff1a; 1. 重定义浏览器默认样式 2. 全局…

抑制广播风暴 各种发包

进入接口视图 system-view [Quidway] interface gigabitethernet 0/0/1 配置广播风暴控制 [Quidway-GigabitEthernet0/0/1] storm-control broadcast min-rate 1000 max-rate 2000 配置组播风暴控制 [Quidway-GigabitEthernet0/0/1] storm-control multicast min-rate 1000 max…

业务与信令-第3章数据业务的信令

为什么要重建E-RAB&#xff1f;重建E-RAB有哪些步骤&#xff1f;Service Request消息中有哪些内容&#xff1f;收到Service Request消息后&#xff0c;MME如何判断终端的身份&#xff1f;重建E-RAB过程与新建E-RAB过程有什么差别&#xff1f;重建E-RAB过程中S1承载是如何建立的…

HappyGBS GB28181信令服务 - 开篇

HappyGBS GB28181信令服务 - 开篇 HappyGBS GB28181信令服务 - 开篇 HappyGBS GB28181信令服务 - 运行 HappyGBS GB28181信令服务 - 文档 HappyGBS设计理念&#xff1a;大道至简&#xff0c;精益求精 HappyGBS服务&#xff0c;包含 信令服务(HappyCMS) 和 流媒体服务(ZLMedi…

信令和非信令的区别

信令和非信令的区别 信令与非信令模式区别&#xff1a; signaling mode:信令模式&#xff0c;就是用CMU200或8960模拟基站&#xff0c;和手机建立起链接,仪表发出各种信令&#xff0c;手机此时相当于联上了网络。 1&#xff09;手机此时既要发射信号&#xff0c;又要接收来自…

RabbitMQ:死信队列

✨ RabbitMQ&#xff1a;死信队列 1.死信队列1.1死信队列基本介绍1.2消息成为死信的三种情况1.3死信队列结构图1.4死信的处理方式 2.TTL消息过期时间2.1基本介绍2.2生产者2.3消费者12.4消费者22.5设置TTL的两种方式2.5.1队列设置TTL2.5.2消息设置TTL2.5.3区别 &#x1f4c3;个人…

广播风暴的成因以及如何判断、解决

广播风暴&#xff08;broadcast storm&#xff09;简单的讲是指当广播数据充斥网络无法处理&#xff0c;并占用大量网络带宽&#xff0c;导致正常业务不能运行&#xff0c;甚至彻底瘫痪&#xff0c;这就发生了“广播风暴”。一个数据帧或包被传输到本地网段 &#xff08;由广播…

信令详细解析

CSFB信令流程&#xff1a; 1、Extended service request 2、RRC Connection Request ue_Identity有两种类型s-TMSI和randomValue&#xff0c;若UE侧存在有效的s-TMSI&#xff0c;则使用s-TMSI&#xff0c;否则使用randomValue&#xff08;随机数&#xff09;。 建立原因&#…

信令服务和媒体服务

本节主要介绍WebRTC音视频服务端的处理 通过前面的例子我们知道运行WebRTCDemo即可看到P2P的效果&#xff0c;实际应用中我们不可能让用户自己去里面设置对方的IP和音视频端口&#xff0c; 而且即使设置了对方的IP和端口也不一定能运行起来&#xff0c;因为P2P如果双方不在同…

七号信令:信令网基本概念

今天在网上下载了些七号信令方面的资料&#xff0c;学习时记了些笔记&#xff0c;存放于此&#xff1a; 1. 信令: 在通信设备之间传递的各种控制信号&#xff0c;如占用、释放、设备忙闲状态、被叫用户号码等&#xff0c;都属于信令。 信令就是各个交换局在完成呼叫接续中的…

七号信令详细介绍

1、七号信令的基本术语 1.1 信令网 N0.7信令网是独立于电信网的支撑网&#xff0c;是电信网中用于传输No.7信令消息的专用数据网。信令网的三要素&#xff1a;信令点、信令转接点、信令链路。 1.2 信令点和信令转接点 信令点&#xff1a;信令网上产生和接收信令消息的节点&am…

谈论信令风暴

由于移动和腾讯微信负责争吵近期问题&#xff0c;很多混合“知道真相”的big mouth。商收费的问题。无厘头地作为电信运营商向用户收费而破口大骂。 信令风暴的问题在去年開始有接触。影响不是一般的大&#xff0c;对于扩容&#xff0c;有60%是因为信令过载引起的&#xff0c;全…

5G信令(就是用户身份信息)风暴——就是客户端通过公钥加密的消息(携带手机IMSI号)发给服务端,服务器需用私钥解密,这个解密比较消耗资源,如果短时间大量请求到来就会触发信令风暴...

信令&#xff1a;手机开机后&#xff0c;先从USIM中读取之前运营商分配的临时身份信息GUTI/TMSI&#xff0c;发送携带该身份信息的信令给基站&#xff0c;请求接入运营商网络。 如果每个设备的每条消息都需要单独认证&#xff0c;则网络侧安全信令的验证需要消耗大量资源。在传…

谈谈信令风暴

由于最近移动和腾讯就微信收费的问题争吵&#xff0c;夹杂大量“不明真相”的big mouth。将电信运营商就基础设施向互联网运营商收费的问题&#xff0c;无厘头地作为电信运营商向用户收费而破口大骂。信令风暴的问题在去年开始有接触&#xff0c;影响不是一般的大&#xff0c;对…

hisi mmz模块驱动讲解

一、概述 如图所示&#xff0c;在海思平台上将内存分为两个部分&#xff1a;os内存和mmz内存。os内存指&#xff1a;由linux操作系统管理的内存&#xff1b;mmz内存&#xff1a;由mmz驱动模块进行管理供媒体业务单独使用的内存&#xff0c;在驱动加载时可以指定该模块管理内存的…

【HISI系列】之HISI芯片码率控制使用说明

DATE: 2019-1-30 参考 HISI系列文档《芯片码率控制使用说明.pdf》 【Codec系列】之常用码率控制算法分析 摘要 1、CBR参数说明 2、VBR参数说明 3、宏块级码率控制参数说明 4、码率更稳定参数说明 5、图像质量提升参数说明 6、调节呼吸效应参数说明 7、限制 I 帧幅度参数说明…

HiSi 3516CV500 NNIE(Neural Network Inference Engine) 摸鱼记录(1) --- 环境搭建

#PS&#xff1a;要转载请注明出处&#xff0c;本人版权所有 #PS:这个只是 《 我自己 》理解&#xff0c;如果和你的 #原则相冲突&#xff0c;请谅解&#xff0c;勿喷 背景 深度学习的爆发期已经到了瓶颈了&#xff0c;为啥这样说&#xff0c;因为没有突破性的理论进展&…

HISI_3516_vpss

我这里只是想自己做一个总结&#xff0c;是收到大牛博客的启发&#xff0c;按顺序自己总结一遍&#xff0c;具体的一些解释请看此文章&#xff1a; 海思3518E开发笔记2.6——海思VPSS&#xff08;Video Process Sub-System&#xff09;模块详解_Spark&#xff01;的博客-CSDN博…

hisi mmz内存管理

一、概述 如图所示&#xff0c;在海思平台上将内存分为两个部分&#xff1a;os内存和mmz内存。os内存指&#xff1a;由linux操作系统管理的内存&#xff1b;mmz内存&#xff1a;由mmz驱动模块进行管理供媒体业务单独使用的内存&#xff0c;在驱动加载时可以指定该模块管理内存…