/[drupal]/contributions/modules/multisite_manager/multisite_manager.module
ViewVC logotype

Contents of /contributions/modules/multisite_manager/multisite_manager.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.8 - (show annotations) (download) (as text)
Fri Feb 8 22:11:51 2008 UTC (21 months, 2 weeks ago) by schuyler1d
Branch: MAIN
CVS Tags: DRUPAL-5--1-0-BETA2, HEAD
Branch point for: DRUPAL-5, DRUPAL-6--1
Changes since 1.7: +8 -8 lines
File MIME type: text/x-php
fix underscores in table search
1 <?php
2 // $Id: multisite_manager.module,v 1.7 2008/02/08 20:25:25 schuyler1d Exp $
3
4 /**
5 * author: Schuyler Duveen
6 * sponsor: Columbia Univeristy Center for New Media Teaching & Learning
7 * URL: http://drupal.org/project/multisite_manager
8 * License: GPL v2 or any later version (see LICENSE.txt)
9 *
10 * TODO
11 * ----
12 * test postgres
13 * actual 'management' options?
14 * update.php support
15 */
16
17 /**
18 * Implementation of hook_help()
19 */
20 function multisite_manager_help($section) {
21 switch ($section) {
22 case 'admin/help#multisite_manager':
23 return t('Multisite Manager helps create new drupal sites from within a main drupal site. For documentation see http://code.google.com/p/drupal-multisite-manager/');
24 }
25 }
26
27
28 function multisite_manager_node_info() {
29 return array(
30 'drupal_site' => array(
31 'name' => t('Drupal Site'),
32 'module' => 'multisite_manager',
33 'description' => t("Enables creation of drupal sites stored in the same database with a different prefix over the web without database info."),
34 )
35 );
36 }
37
38 /**
39 * Implementation of hook_cron()
40 */
41 function multisite_manager_cron() {
42 global $db_prefix, $conf;
43
44 // check if we should run sub-sites' cron jobs
45 if (!variable_get('multisite_manager_run_cron', 0)) {
46 return;
47 } // end if not run cron
48
49 // find all drupal sites
50 $res = db_query('SELECT `nid` FROM {node} WHERE `type` = "%s"', 'drupal_site');
51 while ($node = db_fetch_object($res)) {
52 $node = node_load($node->nid);
53
54 // if don't run this site's cron jobs
55 if (!$node->run_cron) {
56 watchdog('cron', t('Skipping cron for %title.', array('%title' => $node->title)));
57 }
58 // if run this site's cron jobs
59 else {
60 $node = _multisite_manager_node_url($node);
61 $node_cron_url = $node->url . '/cron.php';
62 /* Currently, accessing cron.php has no access restrictions,
63 * (presumably, because the worst it could do is make something happen that should happen!)
64 * therefore, we can do this via the cron.php url.
65 *
66 * Doing so by switching DBs and through drupal_cron_run() runs into considerable issues
67 * mostly with how module_invoke_all() and module_implements() cannot be localized.
68 * These two methods query in-memory modules, which for our cases runs into issues when
69 * different modules are installed in the master site vs. the sub-sites.
70 *
71 * In theory, if these calls were tied to a master drupal state method, then this and other methods
72 * like update, etc might be possible.
73 */
74
75 watchdog('cron', t('Running cron for %title at "%url".', array('%title' => $node->title, '%url' => $node_cron_url)));
76 $result = drupal_http_request($node_cron_url);
77
78 if ($result->error) {
79 $message = t('Error running cron for %title due to %error when accessing %url.',
80 array('%title' => $node->title,
81 '%error' => $result->code .' '. $result->error,
82 '%url' => $node_cron_url,
83 ));
84 drupal_set_message($message,'error');
85 watchdog('cron',$message, WATCHDOG_WARNING);
86 } // end if error
87 }
88 } // end while nodes
89 } // end function multisite_manager_cron()
90
91 /**
92 * Implementation of hook_view().
93 */
94 function multisite_manager_view($node, $teaser = FALSE, $page = FALSE) {
95 global $base_url;
96 $node = node_prepare($node, $teaser);
97 if ($node->installed) {
98 if ($node->link) {
99 $node = _multisite_manager_node_url($node);
100
101 //header('Location: '. $url);
102 $node->content['link'] = array(
103 '#value' => theme('multisite_manager_sitelink', $node),
104 '#weight' => 1,
105 );
106 }
107 } else {
108 $node->content['installed'] = array(
109 '#value' => t("This Drupal site will be created on the next batch creation."),
110 '#weight' => 1,
111 );
112 }
113 return $node;
114 }
115
116 /**
117 * Implementation of hook_menu().
118 */
119 function multisite_manager_menu($may_cache) {
120 $items = array();
121 if ($may_cache) {
122 $items[] = array('path' => 'admin/settings/multisite_manager',
123 'title' => t('Multisite Manager settings'),
124 'description' => t('Configure the database and url defaults for new sites.'),
125 'callback' => 'drupal_get_form',
126 'callback arguments' => array('multisite_manager_admin_settings'),
127 'access' => user_access('administer multisite defaults'),
128 'type' => MENU_NORMAL_ITEM);
129
130 }
131 return $items;
132 }
133
134 /**
135 * Implementation of hook_access().
136 */
137 function multisite_manager_access($op, $node) {
138 global $user;
139
140 if ($op == 'create') {
141 // Only users with permission to do so may create this node type.
142 return user_access('create drupal site');
143 }
144 // Users who create a node may edit or delete it later, assuming they have the // necessary permissions.
145 if ($op == 'update' || $op == 'delete') {
146 if (user_access('delete own drupal site') && ($user->uid == $node->uid)) {
147 return TRUE;
148 }
149 }
150
151 }
152
153 /**
154 * Implementation of hook_perm().
155 */
156 function multisite_manager_perm() {
157 return array('create drupal site',
158 'delete own drupal site',
159 'advanced database setup',
160 'administer multisite defaults',
161 );
162 }
163
164 function _multisite_manager_defaults() {
165 /*
166 global $db_url, $db_prefix;
167 $url = parse_url(is_array($db_url) ? $db_url['default'] : $db_url);
168 $db_user = urldecode($url['user']);
169 $db_pass = urldecode($url['pass']);
170 $db_path = ltrim(urldecode($url['path']), '/');
171 */
172 return array('profile' => variable_get('multisite_manager_profile_default', 'default'),
173 'db_prefix' => variable_get('multisite_manager_dbprefix_default', '{shortname}_'),
174 'db_user' => '',
175 'db_pass' => '',
176 'db_path' => variable_get('multisite_manager_dbpath_default', ''),
177 'link' => variable_get('multisite_manager_link_default', ''),
178 'run_cron' => variable_get('multisite_manager_run_cron', 0),
179 );
180 }
181
182 function _multisite_manager_profile_options() {
183 $profiles = file_scan_directory('./profiles', '\.profile$', array('.', '..', 'CVS'), 0, TRUE, 'name', 0);
184 $profile_options = array();
185 foreach ($profiles as $profile) {
186 require_once $profile->filename;
187 $function = $profile->name.'_profile_details';
188 $details = $function();
189 $profile_options[$profile->name] = $details['name'].': '.$details['description'];
190 }
191 return $profile_options;
192 }
193
194 /**
195 * Implementation of hook_form().
196 */
197 function multisite_manager_form(&$node) {
198 include_once './includes/install.inc';
199
200 $type = node_get_types('type', $node);
201
202 $defaults = _multisite_manager_defaults();
203
204 $form['title'] = array(
205 '#type' => 'textfield',
206 '#title' => check_plain($type->title_label),
207 '#required' => TRUE,
208 '#default_value' => $node->title,
209 '#weight' => -5
210 );
211 $form['run_cron'] = array(
212 '#type' => 'checkbox',
213 '#title' => t('Run Drupal Sites\' Cron'),
214 '#required' => FALSE,
215 '#default_value' => isset($node->run_cron)?$node->run_cron:$defaults['run_cron'],
216 '#description' => t('This sets whether this drupal site\'s cron jobs will be run when the master site\'s cron is run and is configured to trigger sites. By default, it is set ON only if the master is configured to trigger sites.'),
217 );
218 $form['shortname'] = array(
219 '#required' => TRUE,
220 '#type' => 'textfield',
221 '#title' => t('Shortname'),
222 '#default_value' => $node->shortname,
223 '#size' => 15,
224 '#maxlength' => 45,
225 '#description' => t('This short name will be used in the database as a unique identifier and also possibly the default site location. This must only be numbers and letters'),
226 '#weight' => -4
227 );
228
229 $form['profile'] = array(
230 '#type' => 'radios',
231 '#title' => t('Profile'),
232 '#required' => TRUE,
233 '#options' => _multisite_manager_profile_options(),//array_combine($profiles,$profiles),
234 '#default_value' => $defaults['profile'],
235 '#weight' => -2
236 );
237
238 $form['advanced'] = array(
239 '#type' => 'fieldset',
240 '#title' => t('Advanced'),
241 '#collapsible' => TRUE,
242 '#collapsed' => TRUE,
243 '#access' =>user_access('advanced database setup'),
244 '#description' => t('Once created, modifying these values will not do anything except change the local record. If you need to move the database around, you must do it yourself through the database. Then update the record here.'),
245 );
246 $form['advanced']['link'] = array(
247 '#type' => 'textfield',
248 '#title' => t('Site Link'),
249 '#default_value' => isset($node->link)?$node->link:$defaults['link'],
250 '#required' => FALSE,
251 '#access' =>user_access('advanced database setup'),
252 );
253 $form['advanced']['db_prefix'] = array(
254 '#type' => 'textfield',
255 '#title' => t('Table prefix'),
256 '#default_value' => isset($node->db_prefix)?$node->db_prefix:$defaults['db_prefix'],
257 '#required' => FALSE,
258 '#access' =>user_access('advanced database setup'),
259 );
260 $form['advanced']['db_user'] = array(
261 '#type' => 'textfield',
262 '#title' => t('Database username'),
263 '#default_value' => isset($node->db_user)?$node->db_user:$defaults['db_user'],
264 '#required' => FALSE,
265 '#access' =>user_access('advanced database setup'),
266 '#description' => t('Do NOT set this unless your current database account has GRANT option in MySQL or CREATEUSER in Postgres.'),
267 );
268 $form['advanced']['db_pass'] = array(
269 '#type' => 'password',
270 '#title' => t('Database password'),
271 '#default_value' => $defaults['db_pass'],
272 '#required' => FALSE,
273 '#access' =>user_access('advanced database setup'),
274 );
275 $form['advanced']['db_path'] = array(
276 '#type' => 'textfield',
277 '#title' => t('Database name'),
278 '#size' => 45,
279 '#maxlength' => 45,
280 '#default_value' => isset($node->db_path)?$node->db_path:$defaults['db_path'],
281 '#required' => FALSE,
282 '#access' =>user_access('advanced database setup'),
283 '#description' => t('Do NOT set this unless your current database account has CREATE option in MySQL or CREATEDB in Postgres. To ensure database data is not overwritten, if you install in another database, it is either required that your current database have some db_prefix OR that it will be a new database--i.e. that your entry here includes "{shortname}". (An error will result otherwise)'),
284 );
285 // yes, use {pound}access
286 //is there a better way so only people with access can set these items?
287 /*
288 if (!user_access('advanced database setup')) {
289 $form['advanced']['db_prefix']['#value'] = $defaults['db_prefix'];
290 $form['advanced']['db_user']['#value'] = $defaults['db_user'];
291 $form['advanced']['db_pass']['#value'] = $defaults['db_pass'];
292 $form['advanced']['db_path']['#value'] = $defaults['db_path'];
293 $form['advanced']['link']['#value'] = $defaults['link'];
294
295 $form['advanced']['db_prefix']['#type'] = 'hidden';
296 $form['advanced']['db_user']['#type'] = 'hidden';
297 $form['advanced']['db_pass']['#type'] = 'hidden';
298 $form['advanced']['db_path']['#type'] = 'hidden';
299 $form['advanced']['link']['#type'] = 'hidden';
300 }
301 */
302
303 return $form;
304 }
305
306 /**
307 * Implementation of hook_validate().
308 */
309 function multisite_manager_validate(&$node) {
310 global $locale, $multisite_manager_installmodules;
311 include_once './includes/install.inc';
312
313 if (!preg_match('/^[A-Za-z0-9_]+$/', $node->shortname)) {
314 form_set_error('shortname', t('The database table prefix you have entered, %shortname, is invalid. The table prefix can only contain alphanumeric characters, underscores or dots.', array('%shortname' => $node->shortname)), 'error');
315 }
316 if (!$node->nid) {//expected to be NULL if a new node
317 ///if new drupal site, we check to see if the site is already entered
318 ///we don't do this on updates, because maybe the DB admin moved the site manually and is just updating the record
319
320 $already_exists = db_result(@db_query("SELECT shortname FROM {drupal_site} WHERE shortname = '%s'",$node->shortname));
321 if ($already_exists) {
322 form_set_error('shortname', t('The shortname you have entered, %shortname, is already taken. Please use a different one.', array('%shortname' => $node->shortname)), 'error');
323 }
324 if (_multisite_manager_dbexists($node)) {
325 form_set_error('shortname', t('The database location the site would be installed already exists. Please use a different shortname or database path and prefix.'), 'error');
326 }
327 }
328
329 $multisite_manager_installmodules = drupal_verify_profile($node->profile, $locale);
330 if (!$multisite_manager_installmodules) {
331 form_set_error('profile', t('One or more required modules are missing!'), 'error');
332 }
333
334 if (!user_access('advanced database setup')) {
335 /* With #access working in hook_form this should never be called */
336 $defaults = _multisite_manager_defaults();
337 if ($node->db_prefix != $defaults['db_prefix']
338 || $node->db_user != $defaults['db_user']
339 || $node->db_pass != $defaults['db_pass']
340 || $node->db_path != $defaults['db_path']
341 || $node->link != $defaults['link']
342 ) {
343 form_set_error('shortname', t('Just use shortname. You do not have access to modify the database settings directly. If this is necessary, please contact your drupal site administrator.'), 'error');
344 }
345 }
346 }
347
348 function install_no_profile_error() {
349 //this function is required by drupal_verify_profile()
350 //It should probably do something smarter here.
351 echo t("Error installing profile!!!!!!");
352 }
353
354 function _multisite_manager_unparse_dburl($db) {
355 //no such thing as query strings or fragments in db urls, right?
356 $db_url = $db['scheme'].'://';
357 if ($db['user']) {
358 $db_url .= $db['user'];
359 if ($db['pass']) {
360 $db_url .= ':'.$db['pass'];
361 }
362 $db_url .= '@';
363 }
364 $db_url .= $db['host'];
365 if ($db['port']) {
366 $db_url .= ':'.$db['port'];
367 }
368 $db_url .= '/'.$db['path'];
369 //ignoring any querystring or fragment
370 return $db_url;
371 }
372
373 /*
374 * Returns an associative array for the $node that
375 * _multisite_manager_unparse_dburl() can turn into a database url
376 * or be passed to _multisite_manager_dbswitch()
377 */
378 function _multisite_manager_dbobj($node) {
379 global $db_url;
380 $cur_db = parse_url(is_array($db_url) ? $db_url['default'] : $db_url);
381
382 if (strpos($cur_db['path'],'/')===0) {
383 $cur_db['path'] = substr($cur_db['path'],1);
384 }
385
386 $new_db = $cur_db; //copying array, not reference
387
388 //used for set_active array
389 $new_db['name'] = 'multisite_manager_newdb';
390 $new_db['prefix'] = str_replace('{shortname}', $node->shortname, $node->db_prefix);
391
392 if ($node->db_user) {
393 $new_db['user'] = str_replace('{shortname}', $node->shortname, $node->db_user);
394 }
395 if ($node->db_pass) {
396 $new_db['pass'] = $node->db_pass;
397 }
398
399 if ($node->db_path && $node->db_path != $cur_db['path']) {
400 //DIFFERENT DATABASE
401 $new_db['path'] = str_replace('{shortname}', $node->shortname, $node->db_path);
402 $new_db['new'] = TRUE;
403 }
404 else {
405 $new_db['new'] = FALSE;
406 }
407
408 $new_db['url'] = _multisite_manager_unparse_dburl($new_db);
409 return $new_db;
410 }
411
412 /* switch the db, but NOT the prefix!!!
413 *
414 */
415
416 function _multisite_manager_dbswitch($new_db) {
417 global $db_url, $db_prefix;
418
419 $cur_db = array('name' => 'default', //just a guess
420 'url' => $db_url,
421 'prefix' => $db_prefix,
422 );
423
424 /* SWITCH TO NEW DB */
425 if ($db_url != $new_db['url']) {
426 if (!is_array($db_url)) {
427 //this is hacking any just-a-string db_url into an array
428 $db_url = array('default' => $db_url);
429 }
430 else {
431 $cur_db['url'] = $db_url['default'];
432 }
433
434 $db_url[$new_db['name']] = $new_db['url'];
435
436 //actually switch database. db_set_active() defined in includes/database.inc
437 $cur_db['name'] = db_set_active($new_db['name']);
438 }
439 ///We no longer switch the prefix, because we rename the tables after the db is created
440 ///any context that needs to switch the prefix should do so, itself, and when timely
441 //$db_prefix = $new_db['prefix'];
442
443 return $cur_db;
444 }
445
446 /*
447 * Until this is in the db_* api..., return the error text from the last database query
448 */
449 function _multisite_manager_db_error_message() {
450 switch ($GLOBALS['db_type']) {
451 case 'mysql':
452 return mysql_error();
453 case 'mysqli':
454 return mysqli_error();
455 case 'pgsql':
456 return pg_last_error();
457 }
458 }
459
460 /*
461 * Returns true if the database exists where $node is directed
462 */
463 function _multisite_manager_dbexists($node) {
464 global $db_prefix;
465 $new_db = _multisite_manager_dbobj($node);
466 $already_exists = FALSE;
467 switch ($GLOBALS['db_type']) {
468 case 'mysql':
469 case 'mysqli':
470 $already_exists = db_result(@db_query("SHOW DATABASES LIKE '%s'", $new_db['path']));
471 if ($already_exists) {
472 $already_exists = db_result(@db_query("SHOW TABLES FROM %s LIKE '%s%%'", $new_db['path'], str_replace('_','\_',$new_db['prefix'])));
473 if (!$already_exists && $db_prefix != $new_db['prefix']) {
474 ///to avoid watchdog set_active_db() problems we install on the current prefix and then rename tables to the new one
475 ///this needs to be doable, so we can't have tables hanging around that match the current prefix either
476 ///even (or especially!!!) if db_prefix == ''
477 $already_exists = db_result(@db_query("SHOW TABLES FROM %s LIKE '%s%%'", $new_db['path'], str_replace('_','\_',$new_db['prefix'])));
478 }
479 }
480 break;
481 case 'pgsql':
482 ///Since we can't query tables in other DBs in postgres, we have to be a little more picky
483 ///If the database is used by something else, but the prefix is safe, it's still a problem
484 if (strpos($node->db_path,'{shortname}') !== FALSE) {
485 $already_exists = db_result(@db_query("SELECT datname FROM pg_database WHERE datname = '%s'",$new_db['path']));
486 }
487 elseif (!$new_db['new']) {
488 $already_exists = db_result(db_query("SELECT relname FROM pg_stat_user_tables WHERE relname LIKE '%s%%'", str_replace('_','\_',$new_db['prefix'])));
489 }
490 break;
491 }
492 return $already_exists;
493 }
494
495 /*
496 * Creates a new database with permissions to the user if necessary.
497 * (as deemed necessary by $new_db['new'] )
498 */
499 function _multisite_manager_newdb($new_db) {
500 if ($new_db['new']) {
501 //DIFFERENT DATABASE
502 ///create database
503 ///if it already exists, then some error will return, but who cares?
504 ///Actually, we DO care, because if it creates a new db here, we'll
505 /// delete it on node deletion
506 ///in MYSQL you need the 'CREATE' privilege.
507 ///in POSTGRES you need the 'CREATEDB' privilege
508 #$create_req = 'CREATE DATABASE ';
509 #if (strpos($GLOBALS['db_type'], 'mysql') !== FALSE) {
510 #$create_req .= 'IF NOT EXISTS ';
511 #}
512 @db_query('CREATE DATABASE %s', $new_db['path']);
513 $errors = db_error();
514
515 if ($errors === 1007) {
516 ///This is ok, maybe we put all the new databases in one other db
517 drupal_set_message("Database already existed");
518 }
519 elseif ($errors) {
520 drupal_set_message("Database error when creating database: $error", 'error');
521 return FALSE;
522 }
523 }
524 /**
525 * I was tempted to change user only if there's a new database
526 * but clearly this account may have too many rights for a
527 * sub-site, so there is a use case where db_user would change
528 * without the db changing.
529 */
530
531 switch ($GLOBALS['db_type']) {
532 case 'mysql':
533 case 'mysqli':
534 //do if new user AND new db
535 if ($new_db['user'] != $cur_db['user'] && $new_db['new']) {
536 //current user needs GRANT OPTION privilege
537 @db_query("
538 GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX,
539 ALTER, CREATE TEMPORARY TABLES, LOCK TABLES
540 ON %s.*
541 TO '%s'@'%s' IDENTIFIED BY '%s'
542 ", $new_db['path'], $new_db['user'], $new_db['host'], $new_db['pass']);
543 }
544 break;
545 case 'pgsql':
546 if ($new_db['user'] != $cur_db['user']) {//only if diff user
547 //current user needs CREATEUSER privilege
548 @db_query("
549 CREATE USER %s ".
550 ($new_db['pass'])?" WITH PASSWORD '%s'":"%s".
551 " NOCREATEDB
552 NOCREATEUSER
553 ", $new_db['user'], $new_db['pass']);
554 }
555 break;
556 }
557 $errors = db_error();
558 if ($errors) {
559 drupal_set_message(str_replace($new_db['pass'], '******', _multisite_manager_db_error_message()),'error');
560 }
561 }
562
563 /**
564 * Implementation of hook_insert().
565 */
566 function multisite_manager_insert($node) {
567
568 $install_immediately = variable_get('multisite_manager_install_immediate', TRUE);
569 //Store some info about the new site in the main site.
570 //Note that we DO NOT store the password
571 db_query("INSERT INTO {drupal_site} (vid, nid, shortname, profile, link, installed, run_cron, db_prefix, db_user, db_path) VALUES (%d, %d, '%s', '%s', '%s', %d, %d, '%s', '%s', '%s')", $node->vid, $node->nid, $node->shortname, $node->profile, $node->link, $install_immediately, $node->run_cron, $node->db_prefix, $node->db_user, $node->db_path);
572
573 if ($install_immediately) {
574 multisite_manager_install_site($node);
575 }
576
577 }
578
579 function multisite_manager_install_site($node) {
580 /**
581 * 1. If it's a different database and/or user from the current
582 * then creates them (assumes db access to do so)
583 * 2. switches to the new context (db,user,table prefix)
584 * 3. installs drupal profile in new place
585 * 4. switches back to current context
586 */
587 global $multisite_manager_installmodules, $db_prefix, $conf;
588
589 include_once './includes/install.inc';
590
591 /**
592 * creates database and user w/ permissions if necessary
593 * _newdb returns the db_url for the new site based on
594 * the $node's arguments.
595 *
596 * The simplest case would be returning the same url as
597 * the current one
598 */
599
600 $new_db =_multisite_manager_dbobj($node);
601 _multisite_manager_newdb($new_db);
602
603 /* SWITCH TO NEW DB */
604 ///prefix is not changed here
605 $cur_db = _multisite_manager_dbswitch($new_db);
606 if (!$new_db['new']) {
607 ///Only change the prefix if we're in the same database
608 ///Otherwise, we will rename the tables AFTER creation.
609 ///This stops watchdog() from switching back and not finding tables with the right prefix mid-installation
610 $db_prefix = $new_db['prefix'];
611 }
612 /* DB QUERIES NOW ON NEW SITE DB */
613
614 ///Store current $conf (all variable_get/set()'s)
615 $cur_conf = $conf; //should be an array copy here
616 $conf = array();
617
618 drupal_install_profile($node->profile, $multisite_manager_installmodules);
619
620 //after install_profile, because otherwise {cache} and {variable} don't exist yet
621 variable_init(); //soak up anything from {variable}
622
623 /* code ripped from install.php:install_complete */
624 // Store install profile for later use.
625 variable_set('install_profile', $node->profile);
626
627 // Show profile finalization info.
628 $function = $node->profile.'_profile_final';
629 if (function_exists($function)) {
630 // More steps required
631 //PARANOIA: buggy set_active_db() makes us set this again, just for fun
632 $mid_cur_db = _multisite_manager_dbswitch($new_db);
633 $profile_message = $function();
634 }
635
636 if ($new_db['new'] && $new_db['prefix'] != $db_prefix) {
637 ///OK, time to rename all the tables that were just created
638 ///Is this hacky or what!?!?!
639
640 ///PARANOIA. We switch AGAIN to make ABSOLUTELY SURE we don't
641 /// go and rename the tables of the CURRENT DATABASE
642 $mid_cur_db = _multisite_manager_dbswitch($new_db);
643
644 drupal_set_message('Renaming tables in the new database.');
645 $result = _multisite_manager_tables_result($db_prefix);
646
647 if ($cur_db['url'] != $mid_cur_db['url']) {
648 ///This is a sanity check. We can still rename the tables, but without the RE-SWITCH, we would have clobbered our CUR_DB
649 drupal_set_message('Something has gone horribly wrong. The database '. $mid_cur_db['url'] .' is NOT the new one. Therefore, the database was switched mysteriously some time during installation. Thus some features that were intended to be in your new database may have polluted your current database.','error');
650 }
651
652 ///special duty for {sequences} until it doesn't buggily store the prefix; BUG: http://drupal.org/node/168977
653 ///do this BEFORE table rename or we'll have to muck with the db_prefix.
654 if (db_table_exists('sequences')) {
655 @db_query("UPDATE {sequences} SET name = INSERT(name,1,%d,'%s')", strlen($cur_db['prefix']), $new_db['prefix']);
656 }
657
658 ///Actually RENAME the TABLES
659 while($table = @db_fetch_array($result)) {
660 $table_name = array_pop($table);
661 @db_query("ALTER TABLE %s RENAME TO %s%s", $table_name, $new_db['prefix'], substr($table_name,strlen($cur_db['prefix'])) );
662 }
663
664 $db_prefix = $cur_db['prefix'];
665 }
666 /* SWITCH BACK TO OLD DB */
667 _multisite_manager_dbswitch($cur_db);
668
669 //remove cached versions of stuff for new site so as not to corrupt this site's page
670 module_list(TRUE, FALSE);
671 module_implements('nodeapi', FALSE, TRUE);
672 node_get_types('return_nothing', FALSE, TRUE);
673
674 /* DB QUERIES NOW ON MAIN */
675 ///Restore $conf for current context
676 $conf = $cur_conf;
677
678 if (isset($profile_message)) {
679 drupal_set_message($profile_message);
680 }
681 }
682
683 /**
684 * Implementation of hook_update().
685 */
686 function multisite_manager_update($node) {
687 if (user_access('advanced database setup')) {
688 db_query("UPDATE {drupal_site} SET
689 link = '%s', shortname = '%s', run_cron = '%d', db_prefix = '%s', db_user = '%s', db_path = '%s'
690 WHERE vid = %d",
691 $node->link,
692 $node->shortname,
693 $node->run_cron,
694 $node->db_prefix,
695 $node->db_user,
696 $node->db_path,
697 $node->vid);
698 if ($node->db_pass) {
699 db_query("UPDATE {drupal_site} SET db_pass = '%s' WHERE vid = %d", $node->db_pass, $node->vid);
700 }
701 }
702 else {
703 db_query("UPDATE {drupal_site} SET link = '%s', run_cron = '%s' WHERE vid = %d", $node->link, $node->run_cron, $node->vid);
704 }
705 }
706
707 /*
708 * returns a $result from a query of the tables for a certain db prefix
709 */
710 function _multisite_manager_tables_result($prefix) {
711 $result;
712 /* Just to be safe, we escape all queries with @ so it's less likely
713 * that a warning message will try to be written to the {session} db
714 * while we're switched (and {session} in one use case is being deleted!!).
715 */
716 switch ($GLOBALS['db_type']) {
717 case 'mysql':
718 case 'mysqli':
719 $result = @db_query("SHOW TABLES LIKE '%s%%'", str_replace('_','\_',$prefix));
720 break;
721 case 'pgsql':
722 $result = @db_query("SELECT relname FROM pg_stat_user_tables WHERE relname LIKE '%s%%'", str_replace('_','\_',$prefix));
723 break;
724 }
725 return $result;
726 }
727
728 /**
729 * Implementation of hook_delete().
730 */
731 function multisite_manager_delete($node) {
732 /*
733 before deleting database, make sure the list of show tables
734 if prefix isn't "" --but even then, what if the 'new' ones
735 all end up in the same database
736 */
737 /*
738 If db_path has {shortname} in it, then a database is singularly related to that
739 site, and we can just delete the whole thing.
740 */
741 if (strpos($node->db_path,'{shortname}') !== FALSE) {
742 db_query('DROP DATABASE %s',str_replace('{shortname}', $node->shortname, $node->db_path));
743 }
744 elseif (strpos($node->db_prefix,'{shortname}') !== FALSE) {
745 $new_db =_multisite_manager_dbobj($node);
746 /* SWITCH TO NEW DB */
747 if ($new_db['new']) {
748 ///note: prefix is not changed here, and doesn't have to be.
749 $cur_db = _multisite_manager_dbswitch($new_db);
750 }
751 /* DB QUERIES NOW ON NEW SITE DB */
752 $result = _multisite_manager_tables_result($new_db['prefix']);
753
754 while ($table = @db_fetch_array($result)) {
755 $table_name = array_pop($table);
756 drupal_set_message("Dropping table ".$table_name);
757 @db_query("DROP TABLE %s", $table_name);
758 }
759 /* SWITCH BACK TO OLD DB */
760 if ($new_db['new']) {
761 _multisite_manager_dbswitch($cur_db);
762 }
763 /* DB QUERIES NOW ON MAIN */
764 }
765
766 ///Finally, delete from our own records
767 db_query("DELETE FROM {drupal_site} WHERE vid = %d", $node->vid);
768 }
769
770 /**
771 * Implementation of hook_load().
772 */
773 function multisite_manager_load($node) {
774 $additions = db_fetch_object(db_query('SELECT shortname, profile, link, installed, run_cron, db_prefix, db_user, db_path FROM {drupal_site} WHERE vid = %d', $node->vid));
775 return $additions;
776 }
777
778 function multisite_manager_admin_settings_validate($form_id, $form_values) {
779 if ($form_values['module'] == 'multisite_manager') {
780 if (empty($form_values['multisite_manager_dbprefix_default']) && empty($form_values['multisite_manager_dbpath_default'])) {
781 form_set_error('multisite_manager_dbprefix_default', t('You must, at least, enter a database or table prefix string. Both cannot be empty'));
782 }
783 }
784 }
785
786 function multisite_manager_admin_settings() {
787 $defaults = array('db_prefix' => '{shortname}_',
788 'db_path' => '',
789 'link' => '{base_url}/site/{shortname}',
790 'profile' => 'default',
791 'install_immediate' => TRUE,
792 'run_cron' => 0,
793 );
794 $form['multisite_manager_run_cron'] = array(
795 '#type' => 'checkbox',
796 '#title' => t('Run drupal sites\' cron jobs'),
797 '#required' => FALSE,
798 '#default_value' => variable_get('multisite_manager_run_cron', $defaults['run_cron']),
799 '#description' => t('This sets whether each drupal site\'s cron jobs will be run when this master site\'s cron is run. If this is unset no drupal site will have their cron jobs executed regardless of each individual sites\' settings for running cron jobs.')
800 );
801 $form['multisite_manager_install_immediate'] = array(
802 '#type' => 'checkbox',
803 '#title' => t('Install site upon node creation'),
804 '#required' => FALSE,
805 '#default_value' => variable_get('multisite_manager_install_immediate', $defaults['install_immediate']),
806 '#description' => t('This sets whether Drupal site\'s creation is done upon node creation or wether it is delayed until the batch create script is run.')
807 );
808 $form['multisite_manager_dbprefix_default'] = array(
809 '#type' => 'textfield',
810 '#title' => t('Table prefix default'),
811 '#required' => FALSE,
812 '#default_value' => variable_get('multisite_manager_dbprefix_default', $defaults['db_prefix']),
813 '#description' => t('This is the default prefix that will be forced on those without "advanced database setup" rights. {shortname} stands for the shortname field input when creating the site. If you make the default database different from the current one, you can reasonably make this empty.')
814 );
815 $form['multisite_manager_dbpath_default'] = array(
816 '#type' => 'textfield',
817 '#title' => t('Database name default'),
818 '#required' => FALSE,
819 '#default_value' => variable_get('multisite_manager_dbpath_default', $defaults['db_path']),
820 '#description' => t('If left blank, it will use the current database. Otherwise, "{shortname}" will be replaced, so another common default might be "{shortname}_drupal". Do NOT set this unless the current database account has CREATE access in MySQL or CREATEDB access in Postgres. To ensure database data is not overwritten, if you install in another database, it is either required that your current database have some db_prefix OR that it will be a new database--i.e. that your entry here includes "{shortname}".'),
821 );
822 $form['multisite_manager_link_default'] = array(
823 '#type' => 'textfield',
824 '#title' => t('Link default'),
825 '#required' => FALSE,
826 '#default_value' => variable_get('multisite_manager_link_default', $defaults['link']),
827 '#description' => t('This is where the site will be accessible by default. If you setup your ./sites/default/settings.php correctly along with your web server (e.g. apache/htaccess) config, you can anticipate where the new site will live and forward the user to the new site location upon creation. Here, there are two dynamic variables, {base_url} and {shortname}.')
828 );
829 $form['multisite_manager_profile_default'] = array(
830 '#type' => 'radios',
831 '#title' => t('Profile default'),
832 '#required' => FALSE,
833 '#options' => _multisite_manager_profile_options(),
834 '#default_value' => variable_get('multisite_manager_profile_default', $defaults['profile']),
835 );
836
837 return system_settings_form($form);
838 }
839
840 /**
841 * Gets the node url from the link
842 *
843 * @param object $node
844 * @return object
845 */
846 function _multisite_manager_node_url($node) {
847 global $base_url;
848 if ($node->link) {
849 $node->url = str_replace('{base_url}', $base_url, $node->link);
850 $node->url = str_replace('{shortname}', $node->shortname, $node->url);
851 } // end if node link
852
853 return $node;
854 } // end function _multisite_manager_node_url()
855
856
857 function theme_multisite_manager_sitelink($node) {
858 return l($node->title .' '. t('Site'), $node->url);
859 }
860
861 // vim:fenc=utf-8:ft=php:ai:si:ts=2:sw=2:et:

  ViewVC Help
Powered by ViewVC 1.1.2