歡迎光臨, 訪客. 請先 登入 或 註冊一個帳號.
九月 26, 2026, 08:50:10 下午
19595 文章 在 3865 主題 由 4579 會員
最新註冊會員: aa123aa1
LifeType 中文開發論壇  |  開發  |  外掛程式  |  lifetype和discuz整合的思路 « 上篇主題 下篇主題 »
頁: 1 [2]
作者 主題: lifetype和discuz整合的思路  (閱讀 124830 次)
panying
新手見習
*
文章: 39


檢視個人資料
« 回覆文章 #15 於: 四月 07, 2007, 12:42:41 上午 »

lifetype管理和維護方面還是比較復雜,如果沒有php經驗會比較困難,Discuz雖然用的人很多,但很多不懂php的。。。
已記錄

xyan
新手見習
*
文章: 14


檢視個人資料
« 回覆文章 #16 於: 四月 07, 2007, 06:43:06 上午 »

对 是的
所以不可能人人都用
已記錄
lonestone
新手見習
*
文章: 9


檢視個人資料
« 回覆文章 #17 於: 四月 16, 2007, 10:17:56 下午 »

这个贴可不能就这么沉下去,我还指望Mark帮我看看程序呢?!现在公布我搞的Discuz!的用户类,希望大家一起讨论完善它,大家都可以用起来。我这个类需要在dz的cdb_members表加一个int字段blogstatus,标记lifetype是否开通。

程式碼:
<?php

/*

 * Name:    DiscuzUserDataProvider (support read user info from dz)

 * Version: 1.0

 * Author:  王勇

 * Contact: wangyong.yichang@gmail.com

 * Release: 2007-3-9

 * 

 * 注意事项:

 *    Could not update user info in dz.

 *    Could not delete user from dz

 *    Do not support dz user's Muti-user group , only support main group (todo in future)  

 * 

 *   This program is distributed in the hope that it will be useful,

 * but WITHOUT ANY WARRANTY; without even the implied warranty of

 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  

 */

include_once( PLOG_CLASS_PATH . "class/dao/userdata/baseuserdataprovider.class.php" );
include_once( 
PLOG_CLASS_PATH . "class/database/db.class.php" );

/**
 * Model representing the users in our application. Provides the methods such as
 * authentication and querying for users.
 * 
 * \ingroup User_Data_Providers
 */
class DiscuzUserDataProvider extends BaseUserDataProvider
{
var $_dbc; //database connect
var $_dzprefix; //dz database prefix
var $_allowedusergroups; //which group in dz will be active .
var $_disallowedusergroups; //which group in dz will be not active , if you have block group , set it
var $_adminusergroups; //which group in dz will have admin permission?  
// var $_adminusers; //special user in dz to have admin permission.
/**
 * Initializes the model
 */
function DiscuzUserDataProvider( $providerConfig )
{
$this->BaseUserDataProvider( $providerConfig ); 
// initialize the database connection based on our parameters
$config = $this->getProviderConfiguration();
$user = $config->getValue( "user" );
$pass = $config->getValue( "password" );
$host = $config->getValue( "host" );
$db = $config->getValue( "database" );

$this->_dzprefix = $config->getValue( "prefix" );
$this->_allowedusergroups = $config->getValue( "allowgroup" );
$this->_disallowedusergroups = $config->getValue( "denygroup" );
$this->_adminusergroups = $config->getValue( "admingroup" ); 
// $this->_adminusers = $config->getValue( "adminuser" );
$this->_dbc = & Db::getNewDb( $host, $user, $pass, $db );
$this->_dbc->Execute("set names 'gbk'");
}

function dzAllowed( $row )
{ 
//echo "dzAllowed called".$row['groupid'];
if ( !in_array( $row['groupid'], $this->_disallowedusergroups ) )
if ( in_array( $row['groupid'], $this->_allowedusergroups ) )
return true; 
// echo "dzAllowed return false";
return false;
}

function dzAdmin( $row )
{ 
// echo "dzAdmin called";
if ( in_array( $row['groupid'], $this->_adminusergroups ) )
return true; 
// if ( in_array( $row['uid'], $this->_adminusers ) )
// return true;
// echo "dzAdmin return false";
return false;
}

function dzCheckPassword( $pass , $row )
{ 
// echo("dzCheckPassword called");
if ( md5( $pass ) == $row['password'] ) return true;
return false;
}

/**
 * Returns true if the user is in the database and the username
 * and password match
 * 
 * @param user $ Username of the user who we'd like to authenticate
 * @param pass $ Password of the user
 * @return true if user and password correct or false otherwise.
 */
function authenticateUser( $user, $pass )
{
$query = "SELECT * FROM " . $this->_dzprefix . "members WHERE username = '" . Db::qstr( $user ) . "'";

$result = $this->_dbc->Execute( $query );

if ( !$result )
return false;

$ret = ( $result->RecordCount() == 1 );

if ( $ret ) $row = $result->FetchRow();

$result->Close();

if ( $ret && $this->dzCheckPassword( $pass, $row ) && $this->dzAllowed( $row ) )
return true;
else
return false;
}

/**
 * Returns all the information associated to the user given
 * 
 * @param user $ Username of the user from who we'd like to get the information
 * @param pass $ Password of the user we'd like to get the information
 * @return Returns a UserInfo object with the requested information, or false otherwise.
 */
function getUserInfo( $user, $pass )
{
$query = "SELECT * FROM " . $this->_dzprefix . "members WHERE username = '" . Db::qstr( $user ) . "'";
$result = $this->_dbc->Execute( $query );

if ( !$result )
return false;

$row = $result->FetchRow();
$result->Close();

if ( !$this->dzCheckPassword( $pass, $row ) )
return false;

return( $this->_mapUserInfoObject( $row ) );
}

/**
 * Retrieves the user information but given only a username
 * 
 * @param username $ The username of the user
 * @return Returns a UserInfo object with the requested information, or false otherwise.
 */
function getUserInfoFromUsername( $username )
{
$query = "SELECT * FROM " . $this->_dzprefix . "members WHERE username = '" . Db::qstr( $username ) . "'";
$result = $this->_dbc->Execute( $query );

if ( !$result )
return false;

if ( $result->RowCount() == 0 )
{
$result->Close();
return false;
}

$row = $result->FetchRow();
$result->Close();

return( $this->_mapUserInfoObject( $row ) );
}

/**
 * Retrieves the user infromation but given only a userid
 * 
 * @param userId $ User ID of the user from whom we'd like to get the information
 * @return Returns a UserInfo object with the requested information, or false otherwise.
 */
function getUserInfoFromId( $userid, $extendedInfo = false )
{
include_once( PLOG_CLASS_PATH . "class/dao/userpermissions.class.php" );

$query = "SELECT * FROM " . $this->_dzprefix . "members WHERE uid = '" . Db::qstr( $userid ) . "'";

$result = $this->_dbc->Execute( $query );

if ( !$result )
return false;

$row = $result->FetchRow();
$result->Close(); 
// fetch the user permissions
// $perms = new UserPermissions();
// $row["site_admin"] = $perms->isSiteAdmin( $userid );
return( $this->_mapUserInfoObject( $row ) );
}

function dzAddBlog( $row )
{ 
// create a new blog
include_once( PLOG_CLASS_PATH . "class/dao/blogs.class.php" );
include_once( PLOG_CLASS_PATH . "class/dao/articles.class.php" );
include_once( PLOG_CLASS_PATH . "class/dao/articlecategories.class.php" );

$blogs = new Blogs();
$blog = new BlogInfo( $row["user"], // name of the new blog
$row["id"], // id of the owner
"", // no about
"" ); // no properties either
$newBlogId = $blogs->addBlog( $blog ); 
// add a default category and a default post
$articleCategories = new ArticleCategories();
$articleCategory = new ArticleCategory( "默认分类", "", $newBlogId, true );
$catId = $articleCategories->addArticleCategory( $articleCategory );
$config = & Config::getConfig();
$locale = & Locales::getLocale( $config->getValue( "default_locale" ) );
$articleTopic = $locale->tr( "register_default_article_topic" );
$articleText = $locale->tr( "register_default_article_text" );
$article = new Article( $articleTopic,
$articleText,
Array( $catId ),
$row["uid"],
$newBlogId,
POST_STATUS_PUBLISHED,
0,
Array(),
"welcome" );
$t = new Timestamp();
$article->setDateObject( $t );
$articles = new Articles();
$articles->addArticle( $article );

 
           //修改dz数据库的blogstatus状态为1
$sql="update " . $this->_dzprefix . "members set blogstatus=1 where uid=".$row["uid"];
$this->_dbc->Execute($sql);

// lt_include( PLOG_CLASS_PATH."class/dao/permissions.class.php" );
// lt_include( PLOG_CLASS_PATH."class/dao/userpermissions.class.php" );
// $perms = new Permissions();
// $loginPerm = $perms->getPermissionByName( "login_perm" );
// $userPerms = new UserPermissions();
// $userPerm = new UserPermission( $row["uid"], 0, $loginPerm->getId());
// $userPerms->grantPermission( $userPerm );

}

function _mapUserInfoObject( $row, $extraInfo = false )
{
include_once( PLOG_CLASS_PATH . "class/dao/userpermissions.class.php" );

$plogDzData = $this->getpLogDzUserData( $row["uid"] );

$row["user"] = $row["username"];
$row["password"] = $row["password"]; //todo
$row["email"] = $row["email"]; 
// $row["about"] = $plogDzData["bio"];
$row["full_name"] = $plogDzData["nickname"]; 
// $row["resource_picture_id"] = $plogDzData["resource_picture_id"];
// if( $row["resource_picture_id"] == "" )
$row["resource_picture_id"] = 0;
$row["properties"] = serialize( Array() );
$row["id"] = $row["uid"];
$row["status"] = $this->dzAllowed( $row ) ? USER_STATUS_ACTIVE : USER_STATUS_DISABLED;
$row["site_admin"] = $this->dzAdmin( $row )?1:0; 
// does this dz user have a blog yet? If so, create one if the configuration
// of the user data provider says so
$providerConfig = $this->getProviderConfiguration();
if ( $providerConfig->getValue( "createBlogIfNotExisting" ) )
{
$userInfo = BaseUserDataProvider::mapRow( $row, true ); 
// check if this user is assigned to any blog
$userBlogs = $userInfo->getBlogs();
//$this->log->debug( "dz: checking if user " . $row["user"] . " has at least one blog..." );
if ( empty( $userBlogs ) )
{
//$this->log->debug( "dz: creating new blog for user!" );
$this->dzAddBlog( $row );
$userInfo->setBlogs( $this->getUsersBlogs( $userInfo->getId() ) );
}
else
{
//$this->log->debug( "he already has one!!!" );
}
}
else
{
$userInfo = BaseUserDataProvider::mapRow( $row );
}

return( $userInfo );
}

/**
 * Returns an array with all the users available in the database
 * 
 * @param status $ 
 * @param includeExtraInfo $ 
 * @param page $ 
 * @param itemsPerPage $ 
 * @return An array containing all the users.
 */
function getAllUsers( $status = USER_STATUS_ALL, $searchTerms = "", $page = -1, $itemsPerPage = DEFAULT_ITEMS_PER_PAGE )
{
$where = "";
switch ( $status )
{
case user_status_all:
$where = "";
break;
case user_status_active:
$where = "groupid in (" . implode( ",", $this->_allowedusergroups ) . ")";
break;
case user_status_unconfirmed:
case user_status_disabled:
$where = "not(groupid in (" . implode( ",", $this->_allowedusergroups ) . "))";
break;
}

if ( $searchTerms != "" )
{
if ( $where != "" )
$where = $where . " AND " . ( $this->getSearchConditions( $searchTerms ) );
else
$where = $this->getSearchConditions( $searchTerms );
}

if ( $where != "" )
$where = " where blogstatus=1 and " . $where;
else
$where = " where blogstatus=1";


$query = "SELECT * FROM " . $this->_dzprefix . "members" . $where . " ORDER BY uid ASC";
$result = $this->_dbc->Execute( $query, $page, $itemsPerPage );

$users = Array();

if ($result&& $info = $result->FetchRow( $result ) )
{
array_push( $users, $this->_mapUserInfoObject( $info ) );
$result->Close();
}

return $users;
}



已記錄
lonestone
新手見習
*
文章: 9


檢視個人資料
« 回覆文章 #18 於: 四月 16, 2007, 10:18:23 下午 »

程式碼:
<?php
/**
 * Updates the information related to a user
 * 
 * @param userInfo $ An UserInfo object containing the <b>already udpated</b> information of the
 * user we would like to update.
 * @return Returns true if ok or false otherwise.
 */
function updateUser( $userInfo )
{
$result = $this->update( $userInfo );
if( $result ) {
// remove the old data
            $this->_cache->removeData( $userInfo->getId(), CACHE_USERINFO );
             $this->_cache->removeData( $userInfo->getUsername(), CACHE_USERIDBYNAME );
         }

BaseUserDataProvider::updateUser( $userInfo );
return $this->updatepLogDzUserData( $userInfo ); //nerver change data in dz table , just return the updatepLogDzUserData' return value 
// die("!!!!");
// $query = "UPDATE " . $this->_dzprefix . "members SET
// username = '" . Db::qstr( $userInfo->getUserName() ) . "',
// email = '" . Db::qstr( $userInfo->getEmail() ) . "',
// WHERE uid = '" . Db::qstr( $userInfo->getId() ) . "'"; //todo
// // user_active = '".Db::qstr($userInfo->getPassword())."'
// $result = $this->_dbc->Execute( $query );
// if ( !$result )
// return false;
// BaseUserDataProvider::updateUser( $userInfo );
// // update plog's phpbb2_user table
// $result = $this->updatepLogDzUserData( $userInfo );
// return( $result );
}

/**
 * 
 * @private Why the hell couldn't they make the user_id field auto-incrementable???
 */
function getLastDzUserId()
{
$query = "SELECT MAX(uid)+1 AS next_id FROM " . $this->_dzprefix . "members";

$result = $this->_dbc->Execute( $query );

$row = $result->FetchRow();
$result->Close();

return( $row["next_id"] );
}

/**
 * Adds a user to the database.
 * 
 * @param user $ An UserInfo object with the necessary information
 * @return Returns the identifier assigned to the user, or false if there was any error. It will also modify the
 * UserInfo object passed by parameter and set its database id.
 */
function addUser( & $user )
{
return false; //nerver change data in dz table , just tell pblog can not do that
$password = $user->getPassword();
$id = $this->getLastDzUserId();

$query = "INSERT INTO " . $this->_dzprefix . "user (userid,username,password,useremail)

                  VALUES (
$id, '" . Db::qstr( $user->getUserName() ) . "','" . md5( $user->getPassword() ) . "','" .
Db::qstr( $user->getEmail() ) . "');";

$result = $this->_dbc->Execute( $query );

if ( !$result )
return false;

$user->setId( $id ); 
// update plog's phpbb2_user table
$this->updatepLogDzUserData( $user );

return( $id );
}

/**
 * 
 * @private Updates the plog-specific user data that is used when the dz integration is enabled, since
 * plog has some extra information that does not fit anywhere in dz
 * @param user $ A UserInfo object
 * @return true if successful or false otherwise
 */
function updatepLogDzUserData( & $user )
{ 
// is the user already there?
if($user->getStatus()==2)
{
// delete blog
include_once( PLOG_CLASS_PATH . "class/dao/blogs.class.php" );
include_once( PLOG_CLASS_PATH . "class/dao/bloginfo.class.php" );

$blogs = new Blogs();
$blogsinfoarray = $this->getUsersBlogs($user->getId());
//print_r($blogs[0]->getId());die();
foreach($blogsinfoarray as $bloginfo)
{
$blogs->deleteBlog( $bloginfo->getId()); 
}
$this->deleteUser($user->getId());

}

if ( $this->getpLogDzUserData( $user->getId() ) )
{
//$this->log->debug( "dz user " . $user->getUsername() . " already exists! Running an UPDATE query..." ); 
// we need to run an UPDATE query...
$query = "UPDATE " . $this->_dzprefix . "memberfields

               SET nickname = '" 
. Db::qstr( $user->getFullName() ) ."' WHERE uid = '" . Db::qstr( $user->getId() ) . "'"; 
// bio = '" . Db::qstr( $user->getAboutMyself() ) . "'
// properties = '".Db::qstr( serialize($user->getProperties()))."',
// resource_picture_id = '".Db::qstr( $user->getPictureId())."',
$querypw = "update " . $this->_dzprefix . "members set `password`='" . Db::qstr( $user->getMD5Password() ) . "', email= '" . Db::qstr( $user->getEmail() )."' where uid = '" . Db::qstr( $user->getId() ) . "'";
} 
// else
// {
// // we need to run an INSERT query...
// //$this->log->debug( "dz user " . $user->getUsername() . " does NOT exist yet! Running an INSERT query..." );
// $query = "INSERT INTO " . $this->_dzprefix . "memberfields
// (nickname, bio)
// VALUES ('" . Db::qstr( $user->getFullName() ) . "', '" .
// Db::qstr( $user->getAboutMyself() ) . "')";
// }
$result = $this->_dbc->Execute( $query );
$result = $this->_dbc->Execute( $querypw );
return( true );
}

/**
 * 
 * @private Load the plog-specific dz user data
 * @param userId $ 
 * @return A row with the extra user data or false otherwise
 */
function getpLogDzUserData( $uid )
{
$query = "SELECT * FROM " . $this->_dzprefix . "members as u, " . $this->_dzprefix . "memberfields as m WHERE u.uid=m.uid and u.uid = '" . Db::qstr( $uid ) . "'";

$result = $this->_dbc->Execute( $query );

if ( !$result )
return false;

if ( $result->RowCount() == 0 )
{
$result->Close();
return false;
}

$ret = $result->FetchRow();
$result->Close();

return $ret;
}

/**
 * Removes users from the database
 * 
 * @param userId $ The identifier of the user we are trying to remove
 */
function deleteUser( $userId )
{
//修改dz数据库的blogstatus状态为0
$sql="update " . $this->_dzprefix . "members set blogstatus=0 where uid='".$userId."'";
$this->_dbc->Execute($sql);
}

/**
 * returns the total number of users
 * 
 * @return total number of users
 */
function getNumUsers( $status = USER_STATUS_ALL , $searchTerms = "" )
{
$where = "blogstatus=1 ";
switch ( $status )
{
case user_status_all:
$where = "";
break;
case user_status_active:
$where = "and groupid in (" . implode( ",", $this->_allowedusergroups ) . ")";
break;
case user_status_unconfirmed:
case user_status_disabled:
$where = "and not(groupid in (" . implode( ",", $this->_allowedusergroups ) . "))";
break;
}

if ( $searchTerms != "" )
{
if ( $where != "" )
$where = $where . " AND " . $this->getSearchConditions( $searchTerms );
else
$where = $this->getSearchConditions( $searchTerms );
}

if ( $where != "" )
$where = " where " . $where;

$query = "SELECT COUNT(uid) AS total FROM " . $this->_dzprefix . "members" . $where;

$result = $this->_dbc->Execute( $query ); 
// return no users if this doesn't work!
if ( !$result )
return 0;

$row = $result->FetchRow();
$result->Close();

if ( $row["total"] == "" )
$row["total"] = 0;

return( $row["total"] );
}

/**
 * check if the email account has been registered
 * 
 * @return true if the email account has been registered
 */
function emailExists( $email )
{
$query = "SELECT * FROM " . $this->_dzprefix . "members WHERE email = '" . Db::qstr( $email ) . "'";

$result = $this->_dbc->Execute( $query );

if ( !$result )
return false;
$ret = ( $result->RecordCount() > 0 );
$result->Close();
return $ret;
}

/**
 * 
 * @see Model::getSearchConditions
 */
function getSearchConditions( $searchTerms )
{
include_once( PLOG_CLASS_PATH . "class/dao/searchengine.class.php" ); 
// prepare the query string
$searchTerms = SearchEngine::adaptSearchString( $searchTerms );

return( "(username LIKE '%" . $searchTerms . "%')" );
}

/**
 * Returns an array with all the users that belong to the given
 * blog.
 * 
 * @param blogId $ The blog identifier.
 * @param includeOwner $ Wether to include the owner of the blog or not.
 * @param status $ 
 * @param searchTerms $ 
 * @return An array with the information about the users who belong in
 * one way or another to that blog.
 */
function getBlogUsers( $blogId, $includeOwner = true, $status = USER_STATUS_ALL, $searchTerms = "" )
{
$userids = Array();
$users = Array();
$prefix = $this->getPrefix(); 
// get the information about the owner, if requested so
if ( $includeOwner )
{
$query = "SELECT {$prefix}blogs.owner_id as userid FROM {$prefix}blogs 

                          WHERE 
{$prefix}blogs.id = '" . Db::qstr( $blogId ) . "';";
$result = $this->Execute( $query );

if ( !$result )
return $users;

$row = $result->FetchRow();
$result->Close();

array_push( $userids, $row['userid'] );
} 
// now get the other users who have permission for that blog.
$query2 = "SELECT {$prefix}users_permissions.user_id as userid FROM {$prefix}users_permissions 

                       WHERE 
{$prefix}users_permissions.blog_id = '" . Db::qstr( $blogId ) . "';";
$result2 = $this->Execute( $query2 );

if ( $result2 )
{
while ( $row = $result2->FetchRow() )
{
array_push( $userids, $row['userid'] );
}
$result2->Close();
}

if ( !is_array( $userids ) ) // return empty value
{
return $users;
}

$where = "";
switch ( $status )
{
case user_status_all:
$where = "";
break;
case user_status_active:
$where = "groupid in (" . implode( ",", $this->_allowedusergroups ) . ")";
break;
case user_status_unconfirmed:
case user_status_disabled:
$where = "not(groupid in (" . implode( ",", $this->_allowedusergroups ) . "))";
break;
}

if ( $searchTerms != "" )
{
if ( $where != "" )
$where = $where . " AND " . ( $this->getSearchConditions( $searchTerms ) );
else
$where = $this->getSearchConditions( $searchTerms );
}

if ( $where != "" )
$where = $where . " AND ";

$where = $where . " (uid in (" . implode( ",", $userids ) . "))";

if ( $where != "" )
$where = " where " . $where;

$query3 = "SELECT * FROM " . $this->_dzprefix . "members" . $where . " ORDER BY uid ASC";

$result3 = $this->_dbc->Execute( $query3 );

if ($result3 && $info = $result3->FetchRow( $result3 ) )
{
array_push( $users, $this->_mapUserInfoObject( $info ) );
$result3->Close();
}
return $users;
}
}

?>

已記錄
xyan
新手見習
*
文章: 14


檢視個人資料
« 回覆文章 #19 於: 四月 17, 2007, 08:09:53 下午 »

看来我php基础还是差点呢
继续学~
已記錄
forlife
新手見習
*
文章: 13


檢視個人資料 個人網站
« 回覆文章 #20 於: 五月 01, 2007, 03:05:52 上午 »

其實最簡單的方法, 可不可以這樣呢:
注冊lifetype時, 同時注冊discuz
登入lifetype時, 同時登入discuz
在lifetype加一條連結到discuz的Link
同樣在discuz加一條連結到lifetype的Link

More:
登入時根據用戶名, 在discuz顯示用戶blog的link
在lifetype的summary顯示新post等內容

這樣雖然用戶資料重覆了, 但卻不會影響到對方, 使雙方都能獨立運作.
已記錄

lonestone
新手見習
*
文章: 9


檢視個人資料
« 回覆文章 #21 於: 五月 10, 2007, 09:19:56 上午 »

光说不做,哈哈,等你来弄呢
已記錄
yuhai00
新手見習
*
文章: 3


檢視個人資料
« 回覆文章 #22 於: 十一月 26, 2007, 03:02:17 下午 »

我最近也要做     lifetype和discuz的整合,没有太多太好的思路。希望版主和各位朋友能给点建议和帮助。
先谢谢各位了。 期待版主的整合的文档早点出来。呵呵 微笑
已記錄
andywang
初級會員
**
文章: 93


檢視個人資料
« 回覆文章 #23 於: 三月 07, 2008, 10:23:50 上午 »

这个到底完整做成功了没有呢?如果哪位高手做成功了,直接把文件贡献出来,免得我们这些菜鸟再痛苦的测试啊,热烈期盼中~~~~
已記錄
Mr.dodo
新手見習
*
文章: 1


檢視個人資料
« 回覆文章 #24 於: 三月 31, 2008, 11:40:08 上午 »

在哪里下啊.~~~~~~~~~~
已記錄
zizi474lr
新手見習
*
文章: 1


檢視個人資料
« 回覆文章 #25 於: 六月 15, 2008, 05:35:32 上午 »

 嚎啕大哭 嚎啕大哭 嚎啕大哭 嚎啕大哭。。。着急想要pw能用的!!~~~~~~~~~~phpwind
已記錄
頁: 1 [2]
LifeType 中文開發論壇  |  開發  |  外掛程式  |  lifetype和discuz整合的思路 « 上篇主題 下篇主題 »
    前往: