`

Discuz! X 中 SESSION 机制讲解

    博客分类:
  • PHP
阅读更多
 
  1. Discuz! X 中 SESSION 机制讲解   
  2.   
  3.   在Discuz! X中一如继往的,SESSION 并没有使用 PHP 自带的 SESSION 机制,而是系统的一套自带的机制。   
  4.   
  5.   在数据库中可以看到有两个 SESSION 表:   
  6.     一个是pre_common_adminsession,是管理员登录后台的 SESSION 表;   
  7.     另一个是 pre_common_session 表,是所有用户在前台浏览页面时的 SESSION 表。   
  8.   这两个表都是内存表(内存表的读写速度远高于 MYISAM 表及文本文件)。   
  9.   
  10.   在 Discuz! X 中 SESSION 与 COOKIE 是分不开的,因为 SESSION 就是从客户端读取的 COOKIE ,   
  11.   然后由浏览页面时触发相关的函数执行,再写入数据库 SESSION 表。   
  12.   
  13.   我以登录流程为例来讲解程序具体是如何执行的。   
  14.   在前台首页,点击登录后,弹出一个登录窗口,填写好数据后,提交。form表单提交的 URL 是:   
  15.   
  16. 1   <a href="http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1">http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1</a>   
  17.    
  18. 数据提交到了 member.php 文件中,在程序中可看到下面的代码:   
  19. 01  $mod = !in_array($discuz->var['mod'], $modarray) ? 'logging' : $discuz->var['mod'];   //mod的值即是接下来加载的php页面   
  20. 02  define('CURMODULE'$mod);   
  21. 03  $modcachelist = array('register' => array('modreasons''stamptypeid''fields_required''fields_optional''ipctrl'));   
  22. 04  $cachelist = array();   
  23. 05  if(isset($modcachelist[CURMODULE])) {   
  24. 06      $cachelist = $modcachelist[CURMODULE];   
  25. 07  }   
  26. 08  $discuz->cachelist = $cachelist;   
  27. 09  $discuz->init();   
  28. 10  runhooks();   
  29. 11  require DISCUZ_ROOT.'./source/module/member/member_'.$mod.'.php';  //完成程序的包含操作   
  30.   
  31. 打开source/module/member/member_logging.php文件,是一个类,在类的前面可看到下面三句代码:   
  32.   
  33.    
  34. $ctl_obj = new logging_ctl();   
  35. $method = 'on_'.$_G['gp_action'];  // $_G['gp_action'] 等于action的值即 login   
  36. $ctl_obj->$method();   //$ctl_obj->on_login();   
  37.   
  38. 在类中可找到login方法,在方法中,大约 56 行有下面一个判断语句:   
  39.   
  40.    
  41. if(!submitcheck('loginsubmit', 1, $seccodecheck)) {   
  42.   
  43. 判断语句是当游客浏览时,submitcheck 函数的返回值是假,取反,为真。   
  44. 当用户登录时,程序走的是else部分,在里面可看到下面五句代码:   
  45.   
  46.    
  47. else {   
  48.             $_G['uid'] = $_G['member']['uid'] = 0;   
  49.             $_G['username'] = $_G['member']['username'] = $_G['member']['password'] = '';    //变量赋值   
  50.             $result = userlogin($_G['gp_username'], $_G['gp_password'], $_G['gp_questionid'], $_G['gp_answer'], $_G['setting']['autoidselect'] ? 'auto' : $_G['gp_loginfield']);  //从数据库查询用户数据,并返回相应的信息   
  51.       
  52.             if($result['status'] > 0) {  //状态值大于 0 ,说明有此用户,可以登录   
  53.                 setloginstatus($result['member'], $_G['gp_cookietime'] ? 2592000 : 0);  //设置登录状态,即是写 COOKIE 操作,COOKIE 中的数据即是 SESSION 中相应的数据,但此函数并不负责写 SESSION 的操作   
  54.   
  55. 我们来看一下 source/function/function_login.php中的 setloginstatus 函数,是普通的写 COOKIE 操作,不再具体讲解:   
  56.   
  57.    
  58. function setloginstatus($member$cookietime) {   
  59.     global $_G;   
  60.     $_G['uid'] = $member['uid'];   
  61.     $_G['username'] = $member['username'];   
  62.     $_G['adminid'] = $member['adminid'];   
  63.     $_G['groupid'] = $member['groupid'];   
  64.     $_G['formhash'] = formhash();   
  65.     $_G['session']['invisible'] = getuserprofile('invisible');   
  66.     $_G['member'] = $member;   
  67.     $_G['core']->session->isnew = 1;   
  68.       
  69.     dsetcookie('auth', authcode("{$member['password']}\t{$member['uid']}"'ENCODE'), $cookietime, 1, true);   //authcode加密   
  70.     dsetcookie('loginuser');   
  71.     dsetcookie('activationauth');   
  72.     dsetcookie('pmnum');   
  73. }   
  74.   
  75. 到这里可以说是登录流程大部分已经走完,但是 COOKIE 不清除时,会一直存在于客户端,如果超时,程序中会在判断弃用此 COOKIE,并重新写入。   
  76.   
  77. 下面我们来看一下 DZX 中 SESSION 操作的类,在 source/class/calss_core.php 文件中:   
  78. 程序中每次请求都会加载 SESSION ,这是由核心类 discuz_core 中的 _init_session 方法来执行的,此方法被置于 类的 init方法中,说明每次加载类,会自动将 SESSION 写入。   
  79.   
  80.    
  81. function _init_session() {   
  82.     
  83.     $this->session = new discuz_session();   //创建 SESSION 类   
  84.     
  85.     if($this->init_session) {   
  86.         //从 COOKIE 中读取数据   
  87.         $this->session->init($this->var['cookie']['sid'], $this->var['clientip'], $this->var['uid']);   
  88.         $this->var['sid'] = $this->session->sid;   
  89.         $this->var['session'] = $this->session->var;   
  90.         //判断 SID 是否相等,不等,说明是多个用户在同一主机上登录网站,需要重新写 COOKIE   
  91.         if($this->var['sid'] != $this->var['cookie']['sid']) {   
  92.             dsetcookie('sid'$this->var['sid'], 86400);   
  93.         }   
  94.     
  95.         if($this->session->isnew) {   
  96.             if(ipbanned($this->var['clientip'])) {   
  97.                 $this->session->set('groupid', 6);   
  98.             }   
  99.         }   
  100.     
  101.         if($this->session->get('groupid') == 6) {   
  102.             $this->var['member']['groupid'] = 6;   
  103.             sysmessage('user_banned');   
  104.         }   
  105.         //UID 不为空,且需要更新 SESSION 或是 SESSION 超时,更改用户状态,需要用户重新登录   
  106.         if($this->var['uid'] && ($this->session->isnew || ($this->session->get('lastactivity') + 600) < TIMESTAMP)) {   
  107.     
  108.             $this->session->set('lastactivity', TIMESTAMP);   
  109.     
  110.             $update = array('lastip' => $this->var['clientip'], 'lastactivity' => TIMESTAMP);   
  111.             if($this->session->isnew) {   
  112.                 $update['lastvisit'] = TIMESTAMP;   
  113.             }   
  114.             DB::update('common_member_status'$update"uid='".$this->var['uid']."'");   
  115.         }   
  116.     
  117.     }   
  118. }   
  119.   
  120. 操作 SESSION 的类是 discuz_session ,我们看这个类里面的两个方法:   
  121.   
  122.    
  123. //此函数负责产生新的 SESSION,但并不负责写入数据库   
  124.     function create($ip$uid) {   
  125. //创建SESSION,执行插入数据,由随机函数产生一个六位随机数即是session的唯一值时间为当前时间,sid为cookie中的sid   
  126.         $this->isnew = true;   
  127.         $this->var = $this->newguest;   
  128.         $this->set('sid', random(6));   
  129.         $this->set('uid'$uid);   
  130.         $this->set('ip'$ip);   
  131.         $this->set('lastactivity', time());   
  132.         $this->sid = $this->var['sid'];   
  133.       
  134.         return $this->var;   
  135.     }   
  136. //此函数负责更新 SESSION   
  137.     function update() {   
  138.         if($this->sid !== null) {   
  139.       
  140.             $data = daddslashes($this->var);   
  141.       
  142.             if($this->isnew) {   
  143.                 $this->delete();   
  144.                 DB::insert('common_session'$data, false, false, true);   
  145.             } else {   
  146.                 DB::update('common_session'$data"sid='$data[sid]'");   
  147.             }   
  148.             dsetcookie('sid'$this->sid, 86400);   
  149.         }   
  150.     }   
  151.   
  152. 至此我们知道了 SESSION 插入数据库的具体函数,与 COOKIE 的联系,但还不清楚是如何触发此操作的。   
  153. 打开 source/function/function_core.php 文件,找到函数,updatesession ,此函数负责更新 SESSION :   
  154.   
  155.    
  156. function updatesession($force = false) {   
  157.       
  158.     global $_G;   
  159.     static $updated = false;   
  160.     if(!$updated) {   
  161.         $discuz = & discuz_core::instance();   
  162.         foreach($discuz->session->var as $k => $v) {   
  163.             if(isset($_G['member'][$k]) && $k != 'lastactivity') {   
  164.                 $discuz->session->set($k$_G['member'][$k]);   
  165.             }   
  166.         }   
  167.       
  168.         foreach($_G['action'as $k => $v) {   
  169.             $discuz->session->set($k$v);   
  170.         }   
  171.       
  172.         $discuz->session->update();   
  173.       
  174.         $updated = true;   
  175.     }   
  176.     return $updated;   
  177. }   
  178.   
  179. 我们在程序源码中搜索此函数,可以看到在很多的模板中都有下面一句代码:   
  180.   
  181.    
  182. {eval updatesession();}   
  183.   
  184. 浏览页面时将触发此函数,并将 SESSION 写入数据库。   
  185.   
  186. 整理一下思绪:   
  187. 第一步:用户登录,程序将 COOKIE 写入客户端,这些 COOKIE 即是 SESSION 的部分数据,如SID、IP、TIME,不包含用户名、密码等关键信息。   
  188. 第二步,登录成功后,程序会自动刷新页面,向服务器再次发送请求,服务器加载 discuz_core 核心类,并从 COOKIE 中读取到 SESSION 的相关信息,但还没有写入数据库。   
  189. 第三步,核心类加载完成,程序继续执行,最后加载模板,触发 updatesession 函数,SESSION 被写入数据库。  
Discuz! X 中 SESSION 机制讲解

  在Discuz! X中一如继往的,SESSION 并没有使用 PHP 自带的 SESSION 机制,而是系统的一套自带的机制。

  在数据库中可以看到有两个 SESSION 表:
    一个是pre_common_adminsession,是管理员登录后台的 SESSION 表;
    另一个是 pre_common_session 表,是所有用户在前台浏览页面时的 SESSION 表。
  这两个表都是内存表(内存表的读写速度远高于 MYISAM 表及文本文件)。

  在 Discuz! X 中 SESSION 与 COOKIE 是分不开的,因为 SESSION 就是从客户端读取的 COOKIE ,
  然后由浏览页面时触发相关的函数执行,再写入数据库 SESSION 表。

  我以登录流程为例来讲解程序具体是如何执行的。
  在前台首页,点击登录后,弹出一个登录窗口,填写好数据后,提交。form表单提交的 URL 是:

1	<a href="http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1">http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1</a>

数据提交到了 member.php 文件中,在程序中可看到下面的代码:
01	$mod = !in_array($discuz->var['mod'], $modarray) ? 'logging' : $discuz->var['mod'];   //mod的值即是接下来加载的php页面
02	define('CURMODULE', $mod);
03	$modcachelist = array('register' => array('modreasons', 'stamptypeid', 'fields_required', 'fields_optional', 'ipctrl'));
04	$cachelist = array();
05	if(isset($modcachelist[CURMODULE])) {
06	    $cachelist = $modcachelist[CURMODULE];
07	}
08	$discuz->cachelist = $cachelist;
09	$discuz->init();
10	runhooks();
11	require DISCUZ_ROOT.'./source/module/member/member_'.$mod.'.php';  //完成程序的包含操作

打开source/module/member/member_logging.php文件,是一个类,在类的前面可看到下面三句代码:


$ctl_obj = new logging_ctl();
$method = 'on_'.$_G['gp_action'];  // $_G['gp_action'] 等于action的值即 login
$ctl_obj->$method();   //$ctl_obj->on_login();

在类中可找到login方法,在方法中,大约 56 行有下面一个判断语句:


if(!submitcheck('loginsubmit', 1, $seccodecheck)) {

判断语句是当游客浏览时,submitcheck 函数的返回值是假,取反,为真。
当用户登录时,程序走的是else部分,在里面可看到下面五句代码:


} else {
            $_G['uid'] = $_G['member']['uid'] = 0;
            $_G['username'] = $_G['member']['username'] = $_G['member']['password'] = '';    //变量赋值
            $result = userlogin($_G['gp_username'], $_G['gp_password'], $_G['gp_questionid'], $_G['gp_answer'], $_G['setting']['autoidselect'] ? 'auto' : $_G['gp_loginfield']);  //从数据库查询用户数据,并返回相应的信息
   
            if($result['status'] > 0) {  //状态值大于 0 ,说明有此用户,可以登录
                setloginstatus($result['member'], $_G['gp_cookietime'] ? 2592000 : 0);  //设置登录状态,即是写 COOKIE 操作,COOKIE 中的数据即是 SESSION 中相应的数据,但此函数并不负责写 SESSION 的操作

我们来看一下 source/function/function_login.php中的 setloginstatus 函数,是普通的写 COOKIE 操作,不再具体讲解:


function setloginstatus($member, $cookietime) {
    global $_G;
    $_G['uid'] = $member['uid'];
    $_G['username'] = $member['username'];
    $_G['adminid'] = $member['adminid'];
    $_G['groupid'] = $member['groupid'];
    $_G['formhash'] = formhash();
    $_G['session']['invisible'] = getuserprofile('invisible');
    $_G['member'] = $member;
    $_G['core']->session->isnew = 1;
   
    dsetcookie('auth', authcode("{$member['password']}\t{$member['uid']}", 'ENCODE'), $cookietime, 1, true);   //authcode加密
    dsetcookie('loginuser');
    dsetcookie('activationauth');
    dsetcookie('pmnum');
}

到这里可以说是登录流程大部分已经走完,但是 COOKIE 不清除时,会一直存在于客户端,如果超时,程序中会在判断弃用此 COOKIE,并重新写入。

下面我们来看一下 DZX 中 SESSION 操作的类,在 source/class/calss_core.php 文件中:
程序中每次请求都会加载 SESSION ,这是由核心类 discuz_core 中的 _init_session 方法来执行的,此方法被置于 类的 init方法中,说明每次加载类,会自动将 SESSION 写入。


function _init_session() {
 
    $this->session = new discuz_session();   //创建 SESSION 类
 
    if($this->init_session) {
        //从 COOKIE 中读取数据
        $this->session->init($this->var['cookie']['sid'], $this->var['clientip'], $this->var['uid']);
        $this->var['sid'] = $this->session->sid;
        $this->var['session'] = $this->session->var;
        //判断 SID 是否相等,不等,说明是多个用户在同一主机上登录网站,需要重新写 COOKIE
        if($this->var['sid'] != $this->var['cookie']['sid']) {
            dsetcookie('sid', $this->var['sid'], 86400);
        }
 
        if($this->session->isnew) {
            if(ipbanned($this->var['clientip'])) {
                $this->session->set('groupid', 6);
            }
        }
 
        if($this->session->get('groupid') == 6) {
            $this->var['member']['groupid'] = 6;
            sysmessage('user_banned');
        }
        //UID 不为空,且需要更新 SESSION 或是 SESSION 超时,更改用户状态,需要用户重新登录
        if($this->var['uid'] && ($this->session->isnew || ($this->session->get('lastactivity') + 600) < TIMESTAMP)) {
 
            $this->session->set('lastactivity', TIMESTAMP);
 
            $update = array('lastip' => $this->var['clientip'], 'lastactivity' => TIMESTAMP);
            if($this->session->isnew) {
                $update['lastvisit'] = TIMESTAMP;
            }
            DB::update('common_member_status', $update, "uid='".$this->var['uid']."'");
        }
 
    }
}

操作 SESSION 的类是 discuz_session ,我们看这个类里面的两个方法:


//此函数负责产生新的 SESSION,但并不负责写入数据库
    function create($ip, $uid) {
//创建SESSION,执行插入数据,由随机函数产生一个六位随机数即是session的唯一值时间为当前时间,sid为cookie中的sid
        $this->isnew = true;
        $this->var = $this->newguest;
        $this->set('sid', random(6));
        $this->set('uid', $uid);
        $this->set('ip', $ip);
        $this->set('lastactivity', time());
        $this->sid = $this->var['sid'];
   
        return $this->var;
    }
//此函数负责更新 SESSION
    function update() {
        if($this->sid !== null) {
   
            $data = daddslashes($this->var);
   
            if($this->isnew) {
                $this->delete();
                DB::insert('common_session', $data, false, false, true);
            } else {
                DB::update('common_session', $data, "sid='$data[sid]'");
            }
            dsetcookie('sid', $this->sid, 86400);
        }
    }

至此我们知道了 SESSION 插入数据库的具体函数,与 COOKIE 的联系,但还不清楚是如何触发此操作的。
打开 source/function/function_core.php 文件,找到函数,updatesession ,此函数负责更新 SESSION :


function updatesession($force = false) {
   
    global $_G;
    static $updated = false;
    if(!$updated) {
        $discuz = & discuz_core::instance();
        foreach($discuz->session->var as $k => $v) {
            if(isset($_G['member'][$k]) && $k != 'lastactivity') {
                $discuz->session->set($k, $_G['member'][$k]);
            }
        }
   
        foreach($_G['action'] as $k => $v) {
            $discuz->session->set($k, $v);
        }
   
        $discuz->session->update();
   
        $updated = true;
    }
    return $updated;
}

我们在程序源码中搜索此函数,可以看到在很多的模板中都有下面一句代码:


{eval updatesession();}

浏览页面时将触发此函数,并将 SESSION 写入数据库。

整理一下思绪:
第一步:用户登录,程序将 COOKIE 写入客户端,这些 COOKIE 即是 SESSION 的部分数据,如SID、IP、TIME,不包含用户名、密码等关键信息。
第二步,登录成功后,程序会自动刷新页面,向服务器再次发送请求,服务器加载 discuz_core 核心类,并从 COOKIE 中读取到 SESSION 的相关信息,但还没有写入数据库。
第三步,核心类加载完成,程序继续执行,最后加载模板,触发 updatesession 函数,SESSION 被写入数据库。

 

分享到:
评论

相关推荐

    Discuz!X中SESSION机制实例详解

    X中的具体操作流程来进一步分析其SESSION机制。当用户在前台点击登录按钮,弹出登录窗口后,填写好登录信息并提交表单。此时,数据被发送至member.php文件,该文件会根据提交的参数(如mod和action)决定后续加载的...

    Discuz! X3.4源码

    在本文中,我们将深入探讨Discuz! X3.4的核心特性、架构设计以及源码分析,帮助开发者更好地理解和利用这一强大的工具。 一、核心特性 1. 强大的社区功能:Discuz! X3.4提供论坛、博客、问答、家园等多种社区模块...

    Discuz! X3.4 繁体中文BIG5 R20171001.zip

    X3.4在继承和完善Discuz! X3.3的基础上,针对PHP7进行了优化,对于X3.3用户来说,X3.4已继承了X3.3的补丁修复工作。 Discuz! X3.4 繁体中文BIG5 R20171001 更新日志: 增加新触屏版;去除云平台相关代码;优化...

    Discuz!X3.5 X3.4腾讯云验证码插件 1.0.0.zip

    Discuz!X3.5和X3.4是流行的开源社区论坛系统,它们为企业和个人提供了构建互动社区的强大工具。为了增强这些平台的安全性,腾讯云开发了一款名为“tencentcloud_captcha”的官方插件。这款插件的核心功能是集成腾讯...

    Discuz! X3.1

    X3.1"指的是 Discuz! 系统的一个具体版本,即X3.1。Discuz! 是一个非常知名的开源社区论坛软件,由康盛创想(Comsenz)开发。X3.1是其在某个时间点推出的更新版本,通常会包含对前一版本的改进、新功能的添加以及...

    Discuz! X3.1 GBK 正式版.zip

    X3.1 在继承和完善 Discuz! X3.0 的基础上,针对广告垃圾防御进行了大幅度的调整,新防水墙、帐号保镖、云验证码等功能为社区的健康运转提供更加可靠的保障。 全新安装: 请参照以往版本的安装过程进行,上传程序...

    Discuz! X3.4 正式版 简体中文 GBK v20180101.zip

    X3.4 在继承和完善 Discuz! X3.3 的基础上,针对 PHP7 进行了优化。对于 X3.2 用户来说,X3.3 已继承了 X3.2 的补丁修复工作。安全稳定的程序为站长提供更加可靠的保障。 Discuz! 安装说明: 全新安装:请参照...

    Discuz! X2.5 PHP7.0 GBK.zip

    X3.4在继承和完善Discuz! X3.3的基础上,针对PHP7进行了优化,对于X3.3用户来说,X3.4已继承了X3.3的补丁修复工作。 Discuz! X2.5 PHP7.0 GBK 更新日志:增加新触屏版;去除云平台相关代码;优化优化缓存机制改为...

    Discuz!X1.0开发手册

    X1.0开发手册》是Comsenz公司官方发布的一份详细指导文档,旨在帮助开发者深入了解和熟练运用Discuz!X1.0这一开源社区论坛系统。手册内容涵盖Discuz!X1.0的核心功能、架构设计、模块开发、模板制作、插件集成等多个...

    Discuz! X3.4 正式版 繁体中文 BIG5 20171001.zip

    X3.4 在继承和完善 Discuz! X3.3 的基础上,针对 PHP7 进行了优化。对于 X3.2 用户来说,X3.3 已继承了 X3.2 的补丁修复工作。安全稳定的程序为站长提供更加可靠的保障。 Discuz! 安装说明: 全新安装:请参照...

    Discuz!X3.1 全新安装图文教程

    ### Discuz!X3.1 全新安装图文教程知识点详解 #### 一、Discuz!X3.1 概述 **Discuz!X3.1**是Comsenz公司开发的一款非常流行的社区论坛软件,它基于PHP语言和MySQL数据库,支持多种操作系统和Web服务器环境。Discuz...

    Discuz! x2.5深黑经典模板

    x2.5深黑经典模板" 涉及的主要知识点是Discuz! X2.5论坛系统以及其配套的主题模板设计。Discuz! 是一款非常流行的开源社区建站软件,主要功能是帮助用户快速搭建属于自己的论坛网站。X2.5是Discuz! 的一个特定版本...

    解决与论坛冲突(Discuz! X3.1)问题

    标题中的“解决与论坛冲突(Discuz! X3.1)问题”指的是在使用Discuz! X3.1这款开源论坛软件时遇到的兼容性或功能冲突问题。Discuz! 是一个广泛使用的PHP和MySQL构建的社区论坛系统,X3.1是其特定的版本,可能包含了...

    Discuz!X3.5 X3.4腾讯云全局配置插件 1.0.0.zip

    X3.5 X3.4腾讯云全局配置插件 1.0.0.zip" 提供了一个专为Discuz! X3.5和X3.4论坛系统设计的插件,它整合了与腾讯云相关的各种服务,并允许用户在一个中心化的界面进行统一管理和配置。 【描述】中的关键信息表明,...

    Discuz! X插件开发

    Discuz! X插件开发

    Discuz! X3.3 正式版免费下载 Comsenz 核心产品 2017年最新论坛源码

    Discuz! X3.3 正式版免费下载 Comsenz 核心产品 2017年最新论坛源码 Discuz_X3.3_SC_UTF8.zip 10.41 MB PHP7 进行了优化。对于 X3.2 用户来说,X3.3 已继承了 X3.2 的补丁修复工作,是 X3.2 的稳定版本(但更新了...

    Discuz! X3.3 正式版 简体中文 论坛程序源码

    Discuz! X3.3 在继承和完善 Discuz! X3.2 的基础上,针对 PHP7 进行了优化。对于 X3.2 用户来说,X3.3 已继承了 X3.2 的补丁修复工作。安全稳定的程序为站长提供更加可靠的保障。

    Discuz! X2.5 愤怒的小鸟插件V1.0.rar

    X2.5 愤怒的小鸟插件V1.0》是一款专为Discuz! X2.5论坛系统设计的趣味插件,同时也兼容Discuz! X1.5、X2版本。这款插件的独特之处在于它并未对论坛的系统文件进行任何修改,而是通过外部实现方式,确保了用户在安装...

    Discuz! X1.5导航插件

    X1.5导航插件是一款专为Discuz! X1.5平台设计的强大辅助工具,它旨在帮助用户更好地管理和优化社区网站的导航功能,提升用户体验,增强网站的互动性和实用性。这款插件在www.58119.com上有着生动的展示,通过实际...

Global site tag (gtag.js) - Google Analytics