立即注册 登录
彼岸网 返回首页

天香公主的个人空间 https://www.bian-wang.com/discuz/?10005 [收藏] [复制] [分享] [RSS] https://github.com/txgz999/discuz/wiki

日志

Discuz的DIY功能分析

热度 3已有 1628 次阅读2018-11-22 12:06 AM |个人分类:Discuz

本文讨论下Discuz里的DIY功能是如何实现的。DIY功能分两部分。一是由站长设置网页上的DIY,我们称之为门户DIY。二是由各用户自己设置的个人空间首页上的DIY。我们这里主要讨论前者。后者较前者而言相对简单。一个空白的门户页的模板内容异常的简单 (template/default/portal/index.htm): <!--{template common/header}--> <style id="diy_style" type="text/css"></style> <div class="wp"> <!--[diy=diy1]--><div id="diy1" class="area"></div><!--[/diy]--> </div> <script src="misc.php?mod=diyhelp&action=get&type=index&diy=yes&r={echo random(4)}" type="text/javascript"></script> <!--{template common/footer}--> 当我们在它上面加了框架和模块后,它的模板就变成类似下面的内容: (data/diy/template/default/portal/index.htm ) <!--{template common/header}--> <style id="diy_style" type="text/css"></style> <div class="wp"> <!--[diy=diy1]--> <div id="diy1" class="area"> <div id="frame897w19" class="frame move-span cl frame-1-1-1"> <div class="title frame-title"> <span class="titletext">1-1-1框架</span> </div> <div id="frame897w19_left" class="column frame-1-1-1-l"> <div id="frame897w19_left_temp" class="move-span temp"></div> <!--{block/36}--> <!--{block/35}--> </div> <div id="frame897w19_center" class="column frame-1-1-1-c"> <div id="frame897w19_center_temp" class="move-span temp"></div> </div> <div id="frame897w19_right" class="column frame-1-1-1-r"> <div id="frame897w19_right_temp" class="move-span temp"></div> </div> </div> </div> <!--[/diy]--> </div> <!--{template common/footer}--> 在这个例子里,我们在门户页上加了一个1-1-1框架,并在该框架的左列加了两个模块:

这个DIY的设置保存在数据库的数表 common_diy_data 里,而其中每个模板的设置都存在数表 common_block 里。加上DIY设置内容的模板保存在文件 data/diy/template/default/portal/index.htm里,它对应的模板缓存文件是data/template/1_diy_portal_index.tpl.php里,两者的内容非常类似。 <?php if(!defined('IN_DISCUZ')) exit('Access Denied'); hookscriptoutput('index'); block_get('36,35'); ?> <?php include template('common/header'); ?> <style id="diy_style" type="text/css"></style> <div class="wp"> <!--[diy=diy1]--> <div id="diy1" class="area"> <div id="frame897w19" class="frame move-span cl frame-1-1-1"> <div class="title frame-title"> <span class="titletext">1-1-1框架</span> </div> <div id="frame897w19_left" class="column frame-1-1-1-l"> <div id="frame897w19_left_temp" class="move-span temp"></div> <?php block_display('36');?> <?php block_display('35');?> </div> <div id="frame897w19_center" class="column frame-1-1-1-c"> <div id="frame897w19_center_temp" class="move-span temp"></div> </div> <div id="frame897w19_right" class="column frame-1-1-1-r"> <div id="frame897w19_right_temp" class="move-span temp"></div> </div> </div> </div> <!--[/diy]--> </div> <?php include template('common/footer'); ?>

从DOM元素结构看: area frame move-span title frame-title column move-span temp move-span block move-span block column move-span temp column move-span temp 从界面上可以看出DIY功能只能在有area风格类的div元素内使用。为了叙述的方便,我们称有area风格的元素为DIY区域,有frame风格类的元素为框架,有column类的元素为列,有block风格类的元素为模块。那么一个DIY区域里从上往下可以有多个框架,一个框架里从左到右可以有多个列,一个列里从上到下可以有多个框架或模块。

DIY功能由三个文件提供common_diy.js, portal_diy.js, 和space_diy.js。前者提供DIY的基础包括如何拖曳来调整DIY区域里的框架和模板的位置,后两者在此基础上分别提供门户和个人空间的DIY功能包括如何添加新框架和新模板到DIY区域里去。

common_diy主要提供了两个类: DIY和Drag。DIY提供的是关于DIY的功能,而Drag提供与拖曳有关的功能。在portal_diy和space_diy里分别造了一个Drag类的对象drag,和一个DIY类的对象spaceDiy。

common_diy.js里定义了 Frame框架类,Column列类,和Block模块类

Drag应该是common_diy.js里最主要的类。它里面含有很多成员:
  • data:记载顶层框架的数组,该数组的key是一个DIY区域的ID,值是该区域内顶层框架数组
  • overObj: 鼠标所在的框架或模块DOM元素,或着更准确的说是和鼠标所在位置最近的框架或模块DOM元素
  • dragObj: 当前拖曳的DOM元素
  • tmpBoxElement: 这个DOM元素代表了在拖曳过程中如果拖曳就此结束的话,被拖曳元素会落到的位置


它还跟踪和处理了各种鼠标事件,拖曳的功能和效果就是对这些事件的处理产生的。
  • onmouseover => initDragObj:如果鼠标位于一个可移动物体上,将鼠标显示为move形状
  • onmousedown => dragStart:按鼠标的位置决定被拖曳的物件
  • onmousemove => drag:在拖曳过程中,不断更新被拖曳物体的位置和tmpBoxElement的位置
  • onmouseup => dragEnd:将被拖曳的物体放入tmpBoxElement的位置


门户DIY的特有功能
门户DIY的一个特有功能是用户可以选择要添加的框架和模板的类型,然后将选中的类型拖到DIY区域里。这个拖曳过程和在DIY区域里通过拖曳来调整它上面的框架和模块的位置的过程有所不同。所以在portal_diy.js里给Drag物体添加了一个叫createObj的函数,在开始拖曳时它产生了一个dragObj 来代表着被拖曳的东西, 其内容取决于被拖东西的类别:框架,tab框架,还是模块,由下面三个函数提供:
  • getFrameHtml
  • getTabHtml
  • getBlockHtml

以最后一个函数为例: getBlockHtml : function () { var id = 'block'+Util.getRandom(6); var str = '<div id="'+id+'" class="block move-span"></div>'; str += '</div>'; return str; } 另外createObj还给各种鼠标事件添加了Drag类提供的处理程序。它还指明当拖曳结束后要调用函数 getBlockData。 } else if (objType == 'block') { html = this.getBlockHtml(contentType); offWidth = 200; this.fn = function (e) {drag.getBlockData(contentType);}; 当拖曳结束后,dragObj 里的内容就加到了拖曳到的位置里。然后调用的getBlockData打开了一个对话窗,让用户设置要添加的新模块。 showWindow('showblock', 'portal.php?mod=portalcp&ac=block&op=block&classname='+blockname+'&bid='+bid+'&eleid='+eleid+'&tpl='+document.diyform.template.value,'get',-1); 在服务器端这个请求由portalcp_block.php来处理,对话窗里里第一个TAB"编辑模块"的内容是function_block.php中的函数 block_fetch_content 提供的,而模板 portalcp_block.htm 不止包含里该内容,还包含了一个函数: function succeedhandle_showblock (url, message, values) { var bid = values['bid']; var eleid = typeof values['eleid'] == 'undefined' ? 0 : values['eleid']; var x = new Ajax(); var openTitle = 0; drag.setClose(); x.get('portal.php?mod=portalcp&ac=block&op=getblock&forceupdate=1&bid='+bid+'&tpl=portal/index&inajax=1', function(s) { var obj = document.createElement('div'); bid = 'portal_block_'+bid; obj.innerHTML = s; if ($(bid) != null) { drag.stopSlide(bid); if($(bid+'_content')) $(bid+'_content').parentNode.removeChild($(bid+'_content')); $(bid).innerHTML = obj.childNodes[0].innerHTML; if(s.indexOf('runslideshow();') > 0) { runslideshow(); } } else { $(eleid).parentNode.replaceChild(obj.childNodes[0],$(eleid)); openTitle = 1; } drag.initPosition(); evalscript(s); if (openTitle == 1) drag.openTitleEdit(bid); }); hideWindow('showblock'); } 当用户设置完模块点击提交按钮后,网页向服务器发送了类似下面的一个ajax post请求: portal.php?mod=portalcp&ac=block&op=block&classname=space_doing&bid=&blocktype=0&eleid=blockHULUdI&tpl=portal/index 那时服务器产生了一个新模块,它的回复包含了新模块的ID,并调用了函数succeedhandle_showblock: <script type="text/javascript" reload="1">if(typeof succeedhandle_showblock=='function') {succeedhandle_showblock('portal.php?mod=portalcp&ac=block&op=block&bid=30', '操作成功 ', {'bid':'30','eleid':'blockPLD4hR'});}</script> 这个函数向服务器发送了类似下面的一个ajax get请求: portal.php?mod=portalcp&ac=block&op=getblock&forceupdate=1&bid=30&tpl=portal/index&inajax=1 得到的回复是这个新模块里的内容,所以加到了这个新模块内。接着它又调用了openTitleEdit函数来得到对话窗里下一个TAB"编辑模块标题"里的内容。

发表评论 评论 (27 个评论)

回复 天香公主 2019-5-17 12:03 PM
carry0987: 仔細研究了一週,這個Discuz的DIY功能真的不得不說是相當不錯,容易理解又不需要學習 html 與 css, 但是真的相當難以分離出這個功能....牽涉到太多東西了, 包括 ...
嗯,这个功能完全是通过处理鼠标数据和DOM来实现的,现在用新工具和新功能实现起来也许容易些。
回复 天香公主 2019-5-17 11:59 AM
carry0987: 對了天香,關於XML的部分,目前好像更加推薦使用json,不知道您覺得如何? 貌似是json的處理機制在速度上是優於xml的解析? ...
在我工作中,客户端和服务器端的数据交流都用json格式,两端的代码都是内部的,双方用json格式传点数据就足够了。xml我好久没用了,好象只在服务器端用过,比如xml内容存在文件或数据库里,然后在服务器端把它取出来或存进去。我想如果交流数据的双方都是服务器,一方是你的,另一方不是你的,那也许用xml格式也有好处,比如可以用xsd来规定xml内容结构,用xpath来寻找特定内容,用xslt来转换内容。不知道哪个处理起来快,我看重的主要是哪个在编程时更容易使用些。
回复 carry0987 2019-5-17 09:55 AM
對了天香,關於XML的部分,目前好像更加推薦使用json,不知道您覺得如何? 貌似是json的處理機制在速度上是優於xml的解析?
回复 carry0987 2019-5-17 09:54 AM
仔細研究了一週,這個Discuz的DIY功能真的不得不說是相當不錯,容易理解又不需要學習 html 與 css, 但是真的相當難以分離出這個功能....牽涉到太多東西了, 包括對於拖放的處理、xml的儲存與解析、模板引擎的diy模塊.....最後還要將DIY的結構儲存至數據庫,實在太複雜了......我試試看能不能以jQuery-UI的 Drag&Drop 功能以及jQuery的AJAX 仿造出差不多的東西...
回复 carry0987 2019-4-17 10:11 AM
天香公主:    我没这精力,看你的了。

以前听你说过GitHub,现在我也开始喜欢它了。
喔喔! 太好啦,這樣就可以很方便的交流代碼囉~
回复 天香公主 2019-4-17 10:06 AM
carry0987: 不知道是否可能將這個DIY的功能分離出來,我試試看用之前的模板引擎再搭配這個博文的代碼,應該與模板引擎分離的方式差距不大... ...
   我没这精力,看你的了。

以前听你说过GitHub,现在我也开始喜欢它了。
回复 carry0987 2019-4-17 05:09 AM
不知道是否可能將這個DIY的功能分離出來,我試試看用之前的模板引擎再搭配這個博文的代碼,應該與模板引擎分離的方式差距不大...
回复 天香公主 2019-1-12 03:46 PM
cphinix: 谢谢天香,我是不知道如何从数据库中找到当前用户的好友申请数目;想要在会员的主页显示这个数字,提醒会员还有几个未读的好友申请。哈哈,你太谦虚了,你写的东 ...
新年好!

好友申请都存在数表home_notification(加上你建站时用的前缀)里,表列new值为1代表是新的,表列from_idtype值为friendrequest代表是好友申请,表列from_id和uid的值分别代表申请发出方和接收方的用户ID。
回复 天香公主 2018-12-27 10:30 AM
cphinix: 嗯对,我只是随便填写一个数字,应该是取决于实际申请数
你是不知道如何从数据库里找到当前用户的好友申请数目,还是已经得到这个数字,但不知如何把用户带到批准申请的网页?如果你能详细写写上下文,说说你的目标,已经做了什么,还缺什么,卡在哪里,我再帮你考虑考虑。

不瞒你说,除了我发文讨论过的内容外Discuz的其它东东,我都不熟悉甚至一无所知。
回复 天香公主 2018-12-26 10:10 AM
cphinix: 天香,我在写一个模板,假设要放一个:好友申请(6),要怎么才能显示申请后面那个数字?我试过<!--{if $_G[member][newprompt]}-->($_G[member][newprompt])<!--{ ...
不知这个6代表什么?有6个好友申请?
回复 天香公主 2018-12-23 01:49 AM
cphinix: 就是板块ID 是 1,然后 第一页,然后一张链接图片置于该页顶部,这若用DIY非常容易,直接那页放个静态模板写个html就好,但我要弄的是手机版面,没法DIY,所以只 ...
<!--{if $_G['fid'] == 1 && $_G['page'] == 1 }-->  
....
<!--{/if}-->
回复 天香公主 2018-12-23 01:11 AM
cphinix: 那变量怎么写呢,例如FID1版的第一页~
我不知道什么是FID1,也不明白你需要写什么变量
回复 天香公主 2018-12-22 11:51 PM
cphinix: 一定要使用java不能只利用变量名+div实现么?能否举一个简单完整的语句例子谢谢
我给你的链接里有好几个完整的例子,如Daniel Vassallo和user2886138的答复
回复 天香公主 2018-12-22 08:07 AM
cphinix: 天香,请问你一个问题~要如何写一段代码,指定在某版的某页出现指定图片?
这个问题太一般了。估计你得写段javascript来加图片,参见 https://stackoverflow.com/questions/2735881/adding-images-to-the-html-with-javascript,这段代码应该加在某个template文件里吧。
然后你要仅在某版块某页执行这段代码的话,可以在该template文件里加个合适的if预处理语句,版块名和页数应该都可以从整体变量$_G里找到。
回复 天香公主 2018-12-22 07:37 AM
cphinix: 我指的是DIY,例如首页DIY一个内建的帖子模块,选择一个投票帖,确定后首页就会显示投票内容,点击投票会转进投票帖子,投后回首页,那DIY模块依旧显示投票选项 ...
明白了,也许是出于performance的考虑,所有DIY模块的内容都是缓存的(你在加模块时会看到一个多久更新一次的选项),这些内容都存在一个数表里,不到更新时间不会重新从数据来源获取,其后果是它的内容对所有用户都是相同的。
回复 天香公主 2018-12-21 08:16 AM
cphinix: 那一定是我论坛的问题,谢谢天香,麻烦你了
投票网页和结果网页是同一个url只是按当前用户是否投过票来决定显示哪种样式吧?你跳转后看到的是投票网页的话,请问你1)那时你是否还处于登录状态?2)你在那里还能投票吗?

如果是登录状态的话就有点奇怪了,要不你再装个新站测试下?
回复 牛肉炖土豆 2018-12-21 03:10 AM
cphinix: 那一定是我论坛的问题,谢谢天香,麻烦你了
请问你是如何实现投票之后不自动跳转的?
回复 天香公主 2018-12-20 09:24 PM
cphinix: 请问天香,DIY选择投票帖,用内建模板,会显示投票主题与选项,但是投票后不会自动转到投票结果,请问什么代码可以实现DIY投票帖投票后自动转结果页面,不会每次 ...
对不起,我不办网站也没用过论坛,所以对那里的功能知之甚少。我刚在3.4里试了试,加了个帖子模块,选择的显示样式是"[内置]投票主题专用样式",发现投票后自动转到投票结果网页了,所以没能重复出你说的问题。
回复 carry0987 2018-12-1 12:11 PM
天香公主: 对,看看source/function/functions_core.php里函数template对以diy:为开始的变量值的处理就明白了。要不改模板文件位置的话,就要想个新语法如pdiy:还得改tem ...
我想也不用改吧?畢竟template本來就是存放模板的地方,還是維持template好了
回复 天香公主 2018-12-1 11:10 AM
carry0987: 這麼做的原因是不是因為diy所調用的模板都在template的緣故,因此需要將plugin的頁面放置於template文件夾下?
对,看看source/function/functions_core.php里函数template对以diy:为开始的变量值的处理就明白了。要不改模板文件位置的话,就要想个新语法如pdiy:还得改template函数。
12下一页

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 立即注册

小黑屋|Archiver|彼岸网  

Powered by Discuz! X3.1 © 2001-2014 Comsenz Inc.
GMT-4, 2026-7-14 11:41 AM , Processed in 0.037901 second(s), 21 queries.

返回顶部