1
rts2271
My evolving multisite method
  • 2005/12/20 15:51

  • rts2271

  • Just popping in

  • Posts: 10

  • Since: 2004/5/26


I took some time and worked out something that works for me for what I need a multi-site system. I did'nt have much luck with the module doing what I needed it to so I improvised one here.Mind you this isnt a trusted solution and is better suited for a single owner of the shared site system. I am posting this up for suggestions not as a suggestion and no not know how far I'm going to develop this past the working point.
That said on to my ramblings.

First I altered the mainfile structure. Changing it to www.DOMAINNAME.COM for each subdomain or domain I wished to share the codebase with. Each is set up with its on complete db install at this time. In my case it was a replica of my db and then cut up into individual data sets relating to content type after install.
In my case I have www.DOMAINNAME.COM WWW2.DOMAINNAME.COM DOMAINNAME.COM as 3 configuration files placed apropriately.
My first change to index.php was to add this snipet at the top to build the configuration file include or choose default.

foreach ($_SERVER as $k => $v){
if($k=='HTTP_HOST'){
$SName=$v;
}
}
if (!(empty($SName)))
{
include($SName.".php");
}else{
include("default.main.php");
}

Badda Bing configuration file is is generated from the response to the $_SERVER['HTTP_HOST'].
The next thing we have to do is go into common.php and make a couple of changes.
First is to again pickup the HTTP_HOST value.
foreach ($_SERVER as $k => $v){
if($k=='HTTP_HOST'){
$SName=$v;
}
}

I changed the directory structure of
cache
uploads
templates_c
Each subdomain gets it's own directory inside there original
individual respected directories i.e. ../cache/WWW.DOMAINNAME.COM
In common.php I made these changes.
define("XOOPS_CACHE_PATH", XOOPS_ROOT_PATH."/cache/".$SName."/");
define("XOOPS_UPLOAD_PATH", XOOPS_ROOT_PATH."/uploads".$SName."/");
define("XOOPS_THEME_PATH", XOOPS_ROOT_PATH."/themes");
define("XOOPS_COMPILE_PATH", XOOPS_ROOT_PATH."/templates_c/".$SName."/");
define("XOOPS_THEME_URL", XOOPS_URL."/themes");
define("XOOPS_UPLOAD_URL", XOOPS_URL."/uploads/".$SName);

Now the new altered director structure and configuration files are in place we can move on to the more tedious stuff.
One of the first things your going to say is Oh Noes!!11!! I borked the site cause now anything the references the mainfile is going to need some asjustments.

** NOTE** The one thing I've noticed is that most Core modules as well as well coded 3rd party modules only need this change made in one place per module. Poorly coded modules have it in milions of places. Be warned **

Anywhere the call for mainfile.php comes up has to be changed.Here's my alteration which I am more then likely gonna functionalize.

foreach ($_SERVER as $k => $v){
if($k=='HTTP_HOST'){
$SName=$v;
}
}
if (!(empty($SName)))
{
include($SName.".php");
}else{
include("default.main.php");
}

The easiest way is have a good folder search tool and find all files with mainfile.php in there contents and replace the include with the above sniptet or a call method of your own design.

Now this allows multiple seperate db sites but hey wtf we got that already. Very true. Theres no point to reinventing the wheel. However were not done yet. Were going to move to sharing of user and session data between all the websites. In the kernel file user.php I hard coded the db information i.e. anywhere in user.php with this...
$this->db->prefix("users")
I changed it to DB.PREFIX.TABLE
Example
/**
* retrieve a user
*
* @param int $id UID of the user
* @return mixed reference to the {@link XoopsUser} object, FALSE if failed
*/
function &get($id)
{
if (intval($id) > 0) {
$sql = 'SELECT * FROM DB.PREFIX.TABLE WHERE uid='.$id;
if (!$result = $this->db->query($sql)) {
return false;
}
$numrows = $this->db->getRowsNum($result);
if ($numrows == 1) {
$user = new XoopsUser();
$user->assignVars($this->db->fetchArray($result));
return $user;
}
}
return false;
}

/**
* insert a new user in the database
*
* @param object $user reference to the {@link XoopsUser} object
* @param bool $force
* @return bool FALSE if failed, TRUE if already present and unchanged or successful
*/
function insert(&$user, $force = false)
{
if (strtolower(get_class($user)) != 'xoopsuser') {
return false;
}
if (!$user->isDirty()) {
return true;
}
if (!$user->cleanVars()) {
return false;
}
foreach ($user->cleanVars as $k => $v) {
${$k} = $v;
}
// RMV-NOTIFY
// Added two fields, notify_method, notify_mode
if ($user->isNew()) {
$uid = $this->db->genId($this->db->prefix('users').'_uid_seq');
$sql = sprintf("INSERT INTO DB.PREFIX.TABLE (uid, uname, name, email, url, user_avatar, user_regdate, user_icq, user_from, user_sig, user_viewemail, actkey, user_aim, user_yim, user_msnm, pass, posts, attachsig, rank, level, theme, timezone_offset, last_login, umode, uorder, notify_method, notify_mode, user_occ, bio, user_intrest, user_mailok, steamid) VALUES (%u, %s, %s, %s, %s, %s, %u, %s, %s, %s, %u, %s, %s, %s, %s, %s, %u, %u, %u, %u, %s, %.2f, %u, %s, %u, %u, %u, %s, %s, %s, %u, %s)", $uid, $this->db->quoteString($uname), $this->db->quoteString($name), $this->db->quoteString($email), $this->db->quoteString($url), $this->db->quoteString($user_avatar), time(), $this->db->quoteString($user_icq), $this->db->quoteString($user_from), $this->db->quoteString($user_sig), $user_viewemail, $this->db->quoteString($actkey), $this->db->quoteString($user_aim), $this->db->quoteString($user_yim), $this->db->quoteString($user_msnm), $this->db->quoteString($pass), $posts, $attachsig, $rank, $level, $this->db->quoteString($theme), $timezone_offset, 0, $this->db->quoteString($umode), $uorder, $notify_method, $notify_mode, $this->db->quoteString($user_occ), $this->db->quoteString($bio), $this->db->quoteString($user_intrest), $user_mailok, $this->db->quoteString($steamid));
} else {
$sql = sprintf("UPDATE DB.PREFIX.TABLE SET uname = %s, name = %s, email = %s, url = %s, user_avatar = %s, user_icq = %s, user_from = %s, user_sig = %s, user_viewemail = %u, user_aim = %s, user_yim = %s, user_msnm = %s, posts = %d, pass = %s, attachsig = %u, rank = %u, level= %u, theme = %s, timezone_offset = %.2f, umode = %s, last_login = %u, uorder = %u, notify_method = %u, notify_mode = %u, user_occ = %s, bio = %s, user_intrest = %s, user_mailok = %u, steamid = %s WHERE uid = %u", $this->db->quoteString($uname), $this->db->quoteString($name), $this->db->quoteString($email), $this->db->quoteString($url), $this->db->quoteString($user_avatar), $this->db->quoteString($user_icq), $this->db->quoteString($user_from), $this->db->quoteString($user_sig), $user_viewemail, $this->db->quoteString($user_aim), $this->db->quoteString($user_yim), $this->db->quoteString($user_msnm), $posts, $this->db->quoteString($pass), $attachsig, $rank, $level, $this->db->quoteString($theme), $timezone_offset, $this->db->quoteString($umode), $last_login, $uorder, $notify_method, $notify_mode, $this->db->quoteString($user_occ), $this->db->quoteString($bio), $this->db->quoteString($user_intrest), $user_mailok, $this->db->quoteString($steamid), $uid);
}
if (false != $force) {
$result = $this->db->queryF($sql);
} else {
$result = $this->db->query($sql);
}
if (!$result) {
return false;
}
if (empty($uid)) {
$uid = $this->db->getInsertId();
}
$user->assignVar('uid', $uid);
return true;
}

/**
* delete a user from the database
*
* @param object $user reference to the user to delete
* @param bool $force
* @return bool FALSE if failed.
*/
function delete(&$user, $force = false)
{
if (strtolower(get_class($user)) != 'xoopsuser') {
return false;
}
$sql = sprintf("DELETE FROM DB.PREFIX.TABLE WHERE uid = %u", $user->getVar('uid'));
if (false != $force) {
$result = $this->db->queryF($sql);
} else {
$result = $this->db->query($sql);
}
if (!$result) {
return false;
}
return true;
}

/**
* retrieve users from the database
*
* @param object $criteria {@link CriteriaElement} conditions to be met
* @param bool $id_as_key use the UID as key for the array?
* @return array array of {@link XoopsUser} objects
*/
function &getObjects($criteria = null, $id_as_key = false)
{
$ret = array();
$limit = $start = 0;
$sql = 'SELECT * FROM DB.PREFIX.TABLE';
if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
$sql .= ' '.$criteria->renderWhere();
if ($criteria->getSort() != '') {
$sql .= ' ORDER BY '.$criteria->getSort().' '.$criteria->getOrder();
}
$limit = $criteria->getLimit();
$start = $criteria->getStart();
}
$result = $this->db->query($sql, $limit, $start);
if (!$result) {
return $ret;
}
while ($myrow = $this->db->fetchArray($result)) {
$user = new XoopsUser();
$user->assignVars($myrow);
if (!$id_as_key) {
$ret[] =& $user;
} else {
$ret[$myrow['uid']] =& $user;
}
unset($user);
}
return $ret;
}

/**
* count users matching a condition
*
* @param object $criteria {@link CriteriaElement} to match
* @return int count of users
*/
function getCount($criteria = null)
{
$sql = 'SELECT COUNT(*) FROM DB.PREFIX.TABLE';
if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
$sql .= ' '.$criteria->renderWhere();
}
$result = $this->db->query($sql);
if (!$result) {
return 0;
}
list($count) = $this->db->fetchRow($result);
return $count;
}

/**
* delete users matching a set of conditions
*
* @param object $criteria {@link CriteriaElement}
* @return bool FALSE if deletion failed
*/
function deleteAll($criteria = null)
{
$sql = 'DELETE FROM DB.PREFIX.TABLE';
if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
$sql .= ' '.$criteria->renderWhere();
}
if (!$result = $this->db->query($sql)) {
return false;
}
return true;
}

/**
* Change a value for users with a certain criteria
*
* @param string $fieldname Name of the field
* @param string $fieldvalue Value to write
* @param object $criteria {@link CriteriaElement}
*
* @return bool
**/
function updateAll($fieldname, $fieldvalue, $criteria = null)
{
$set_clause = is_numeric($fieldvalue) ? $fieldname.' = '.$fieldvalue : $fieldname.' = '.$this->db->quoteString($fieldvalue);
$sql = 'UPDATE DB.PREFIX.TABLE SET '.$set_clause;
if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
$sql .= ' '.$criteria->renderWhere();
}
if (!$result = $this->db->query($sql)) {
return false;
}
return true;
}

This sets the user db to one single defined db between multiple db's. I'm sure theres a better way to aproach this and I'm going to try to figure out how to make less changes to this file.
Next I did some session changes because I was trying to make all domains share session data for single sign on. Havent had much luck so I'll post this up saying it sucks and only half assed works. It closes the session in 15 minutes without fail lol doesnt clean up well either. I'm gonna make this my next priority.
function read($sess_id)
{
$sql = sprintf('SELECT sess_data FROM DB.PREFIX.TABLE WHERE sess_id = %s', $this->db->quoteString($sess_id));
if (false != $result = $this->db->query($sql)) {
if (list($sess_data) = $this->db->fetchRow($result)) {
return $sess_data;
}
}
return '';
}

/**
* Write a session to the database
*
* @param string $sess_id
* @param string $sess_data
*
* @return bool
**/
function write($sess_id, $sess_data)
{
$sess_id = $this->db->quoteString($sess_id);
list($count) = $this->db->fetchRow($this->db->query("SELECT COUNT(*) FROM DB.PREFIX.TABLE WHERE sess_id=".$sess_id));
if ( $count > 0 ) {
$sql = sprintf('UPDATE DB.PREFIX.TABLE SET sess_updated = %u, sess_data = %s WHERE sess_id = %s', time(), $this->db->quoteString($sess_data), $sess_id);
} else {
$sql = sprintf('INSERT INTO DB.PREFIX.TABLE (sess_id, sess_updated, sess_ip, sess_data) VALUES (%s, %u, %s, %s)', $sess_id, time(), $this->db->quoteString($_SERVER['REMOTE_ADDR']), $this->db->quoteString($sess_data));
}
if (!$this->db->queryF($sql)) {
return false;
}
return true;
}

/**
* Destroy a session
*
* @param string $sess_id
*
* @return bool
**/
function destroy($sess_id)
{
$sql = sprintf("DELETE FROM DB.PREFIX.TABLE WHERE sess_id='".$sess_id."'");
if ( !$result = $this->db->queryF($sql) ) {
return false;
}
return true;
}

/**
* Garbage Collector
*
* @param int $expire Time in seconds until a session expires
* @return bool
**/
function gc($expire)
{
$mintime = time() - intval($expire);
$sql = sprintf('DELETE FROM DB.PREFIX.TABLE WHERE sess_updated < %u', $mintime);
return $this->db->queryF($sql);
}
At this point you should have a working multi-site setup with a shared session and user table. Points of interest I'm looking at is a user interface to add domains replicate domains. I'd love to make this a module format but havent figured out how to get past some of the hacks I've had to do to get this to work smoothly. If it wasnt for the fact XOOPS is so #OOPS#ed extensible I wouldnt have gotten this far.
Any questions comments suggestions or go to hells would be apreciated. err..

2
Anonymous
Re: My evolving multisite method
  • 2006/2/9 11:41

  • Anonymous

  • Posts: 0

  • Since:


No one test it ?

Login

Username:
Password:

Lost Password? Register now!

Who's Online

69 user(s) are online (45 user(s) are browsing Support Forums)


Members: 0


Guests: 69


more...

Donat-O-Meter

Stats
Goal: $100.00
Due Date: Dec 31
Gross Amount: $0.00
Net Balance: $0.00
Left to go: $100.00
Make donations with PayPal!

Latest GitHub Commits