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

Contents of /contributions/modules/domain/domain.module

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


Revision 1.134 - (show annotations) (download) (as text)
Sun Nov 1 18:20:19 2009 UTC (3 weeks, 6 days ago) by agentken
Branch: MAIN
CVS Tags: DRUPAL-6--2-0
Branch point for: DRUPAL-6--2
Changes since 1.133: +5 -1 lines
File MIME type: text/x-php
-- #372887 patch by nonsie and blackdog. Super-awesome set values for all domains in domain batch.
-- Creates the 6.x.2.0 stable release.
1 <?php
2 // $Id: domain.module,v 1.133 2009/11/01 16:30:55 agentken Exp $
3
4 /**
5 * @defgroup domain Domain Access: A domain-based access control system
6 *
7 * The core Domain Access module.
8 */
9
10 /**
11 * @file
12 * Core module functions for the Domain Access suite.
13 * @ingroup domain
14 */
15
16 /**
17 * Defines how to handle access permissions when installing the module.
18 * You may alter this variable before installing the module. See README.txt.
19 */
20 define('DOMAIN_INSTALL_RULE', TRUE);
21
22 /**
23 * Defines whether to show affiliated content on all domains.
24 * You may alter this variable before installing the module. See README.txt.
25 */
26 define('DOMAIN_SITE_GRANT', TRUE);
27
28 /**
29 * Defines whether to assign users to the default domain on install.
30 * You may alter this variable before installing the module. See README.txt.
31 */
32 define('DOMAIN_ASSIGN_USERS', TRUE);
33
34 /**
35 * Sets a default value at which to start paginating Domain lists.
36 */
37 define('DOMAIN_LIST_SIZE', 25);
38
39 /**
40 * Adds the domain user data to the $user object.
41 */
42 function domain_boot() {
43 global $user;
44 $user->domain_user = domain_get_user_domains($user);
45 }
46
47 /**
48 * Implement hook_init().
49 *
50 * Inititalizes a global $_domain variable if necessary (usually that's done in
51 * domain_bootstrap.inc) and loads information on current domain.
52 *
53 * Also handles www stripping, checks the validity of user domains and updates
54 * $conf['site_name'].
55 */
56 function domain_init() {
57 global $_domain, $conf;
58
59 if (!is_array($_domain)) {
60 $_domain = array();
61 }
62
63 // Error handling in case the module is not installed correctly.
64 if (empty($_domain)) {
65 $_domain['error'] = '-1';
66 }
67 // If $_domain['error'] is present, then set a message and stop.
68 if (!isset($error) && isset($_domain['error'])) {
69 $error = 'Domain access failed to load during phase: '. $_domain['error'] .'. Please check your settings.php file and site configuration.';
70 // Do not show on form submissions, when enabling the module.
71 if (empty($_POST)) {
72 // Show a warning to admin users.
73 if (user_access('administer domains')) {
74 drupal_set_message($error, 'error');
75 }
76 watchdog('Domain Access', $error, NULL, WATCHDOG_ERROR);
77 }
78 return;
79 }
80 // End of the error handling routine.
81
82 // If coming from a node save, make sure we are on an accessible domain.
83 domain_node_save_redirect();
84
85 // Strip the www. off the domain, if required by the module settings.
86 $www_replaced = FALSE;
87 if (variable_get('domain_www', 0) && strpos($_domain['subdomain'], 'www.') !== FALSE) {
88 $_domain['subdomain'] = str_replace('www.', '', $_domain['subdomain']);
89 $www_replaced = TRUE;
90 }
91
92 // Add information from domain_lookup but keep existing values (domain_id and subdomain)
93 $domain = domain_lookup($_domain['domain_id']);
94 $_domain = array_merge($domain, $_domain);
95
96 // If we have replaced 'www.' in the url, redirect to the clean domain.
97 if ($www_replaced) {
98 drupal_goto(domain_get_uri($_domain));
99 }
100
101 // For Domain User, we check the validity of accounts, so the 'valid' flag must be TRUE.
102 if (empty($_domain['valid'])) {
103 domain_invalid_domain_requested();
104 }
105
106 // Set the site name to the domain-specific name.
107 $conf['site_name'] = $_domain['sitename'];
108
109 // Properly load custom_url_rewrite_outbound().
110 // See http://drupal.org/node/529026
111 include_once(drupal_get_path('module', 'domain') .'/settings_custom_url.inc');
112 }
113
114 /**
115 * Implement hook_menu()
116 */
117 function domain_menu() {
118 $items = array();
119 $admin = user_access('administer domains');
120 $items['admin/build/domain'] = array(
121 'title' => 'Domains',
122 'access arguments' => array('administer domains'),
123 'page callback' => 'domain_view',
124 'file' => 'domain.admin.inc',
125 'description' => 'Settings for the Domain Access module.',
126 );
127 $items['admin/build/domain/view'] = array(
128 'title' => 'Domain list',
129 'access arguments' => array('administer domains'),
130 'type' => MENU_DEFAULT_LOCAL_TASK,
131 'page callback' => 'domain_view',
132 'file' => 'domain.admin.inc',
133 'weight' => -10
134 );
135 $items['admin/build/domain/settings'] = array(
136 'title' => 'Settings',
137 'access arguments' => array('administer domains'),
138 'type' => MENU_LOCAL_TASK,
139 'page callback' => 'domain_configure',
140 'file' => 'domain.admin.inc',
141 'weight' => -8
142 );
143 $items['admin/build/domain/create'] = array(
144 'title' => 'Create domain record',
145 'access arguments' => array('administer domains'),
146 'type' => MENU_LOCAL_TASK,
147 'page callback' => 'drupal_get_form',
148 'page arguments' => array('domain_form'),
149 'file' => 'domain.admin.inc',
150 'weight' => -7
151 );
152 $items['admin/build/domain/advanced'] = array(
153 'title' => 'Node settings',
154 'access arguments' => array('administer domains'),
155 'type' => MENU_LOCAL_TASK,
156 'page callback' => 'drupal_get_form',
157 'page arguments' => array('domain_advanced_form'),
158 'file' => 'domain.admin.inc',
159 'weight' => -6
160 );
161 // Register the batch actions as menu callbacks
162 $batch = module_invoke_all('domainbatch');
163 if (!empty($batch)) {
164 $items['admin/build/domain/batch'] = array(
165 'title' => 'Batch updating',
166 'access arguments' => array('administer domains'),
167 'type' => MENU_LOCAL_TASK,
168 'page callback' => 'domain_batch',
169 'file' => 'domain.admin.inc',
170 'weight' => -5
171 );
172 // Get the submenu items
173 foreach ($batch as $key => $value) {
174 $items['admin/build/domain/batch/'. $key] = array(
175 'title' => $value['#form']['#title'],
176 'access arguments' => array('administer domains'),
177 'type' => MENU_CALLBACK,
178 'page callback' => 'domain_batch',
179 'page arguments' => array($key),
180 'file' => 'domain.admin.inc',
181 'weight' => $value['#weight']
182 );
183 }
184 }
185 $items['admin/build/domain/roles'] = array(
186 'title' => 'User defaults',
187 'access arguments' => array('administer domains'),
188 'type' => MENU_LOCAL_TASK,
189 'page callback' => 'drupal_get_form',
190 'page arguments' => array('domain_roles_form'),
191 'file' => 'domain.admin.inc',
192 'weight' => -4
193 );
194 $items['admin/build/domain/edit/%domain'] = array(
195 'title' => 'Edit domain record',
196 'access arguments' => array('administer domains'),
197 'type' => MENU_CALLBACK,
198 'page callback' => 'drupal_get_form',
199 'page arguments' => array('domain_form', 4),
200 'file' => 'domain.admin.inc',
201 );
202 $items['admin/build/domain/delete/%domain'] = array(
203 'title' => 'Delete domain record',
204 'access arguments' => array('administer domains'),
205 'type' => MENU_CALLBACK,
206 'page callback' => 'drupal_get_form',
207 'page arguments' => array('domain_delete_form', 4),
208 'file' => 'domain.admin.inc',
209 );
210 return $items;
211 }
212
213 /**
214 * Implement hook_perm()
215 */
216 function domain_perm() {
217 $perms = array(
218 'access inactive domains',
219 'administer domains',
220 'assign domain editors',
221 'delete domain nodes',
222 'edit domain nodes',
223 'set domain access',
224 'publish to any assigned domain',
225 'publish from assigned domain',
226 'publish from default domain',
227 );
228 return $perms;
229 }
230
231 /**
232 * Implement hook_theme()
233 */
234 function domain_theme() {
235 $themes = array(
236 'domain_admin_users_form' => array(
237 'arguments' => array('form' => array()),
238 'file' => 'domain.admin.inc',
239 ),
240 'domain_batch_form' => array(
241 'arguments' => array('form' => array()),
242 'file' => 'domain.admin.inc',
243 ),
244 'domain_batch_title' => array(
245 'arguments' => array('batch' => array()),
246 'file' => 'domain.admin.inc',
247 ),
248 'domain_roles_form' => array(
249 'arguments' => array('form' => array()),
250 'file' => 'domain.admin.inc',
251 ),
252 );
253 return $themes;
254 }
255
256 /**
257 * Implement hook_block()
258 *
259 * A nifty little domain-switcher block, useful during debugging.
260 */
261 function domain_block($op = 'list', $delta = 0, $edit = array()) {
262 global $_domain, $base_url;
263 $blocks = array();
264 switch ($op) {
265 case 'list':
266 $blocks[0] = array(
267 'info' => t('Domain switcher'),
268 );
269 $blocks[1] = array(
270 'info' => t('Domain access information'),
271 );
272 return $blocks;
273 break;
274 case 'view':
275 switch ($delta) {
276 case 0:
277 $block['subject'] = t('Domain switcher');
278 $items = array();
279 $domains = domain_domains();
280 $msg = FALSE;
281 foreach ($domains as $domain) {
282 if ($domain['valid']) {
283 $title = $domain['sitename'];
284 $allow = TRUE;
285 }
286 else {
287 $title = $domain['sitename'] .' *';
288 $allow = FALSE;
289 if (user_access('access inactive domains')) {
290 $msg = TRUE;
291 $allow = TRUE;
292 }
293 }
294 if ($allow) {
295 $items[] = l($title, domain_get_uri($domain));
296 }
297 }
298 $block['content'] = theme('item_list', $items);
299 if ($msg) {
300 $block['content'] .= t('<em>* Inactive domain.</em>');
301 }
302 break;
303 case 1:
304 $block['content'] = '';
305 if (arg(0) == 'node' && is_numeric(arg(1))) {
306 $block['subject'] = t('Domain access information');
307 $this_node = node_load(arg(1));
308 $output = '';
309 if (!empty($this_node->subdomains)) {
310 $output .= theme('item_list', $this_node->subdomains, t('Assigned domains'));
311 }
312 $this_domain = domain_get_node_match($this_node->nid);
313 $output .= theme('item_list', array($this_domain['sitename']), t('Source domain'));
314 if (empty($output)) {
315 $output = t('This node is not assigned to a domain.');
316 }
317 $block['content'] = '<p>'. t('%node is published with the following Domain Access rules:', array('%node' => $this_node->title)) .'</p>'. $output;
318 }
319 return $block;
320 break;
321 }
322 return $block;
323 break;
324 }
325 }
326
327 /**
328 * Implement hook_user()
329 *
330 * Attached domain_id records to all registering users. These
331 * are used to determine which 'domain_editor' group that users
332 * with the 'edit domain nodes' and 'delete domain nodes' permissions are in.
333 */
334 function domain_user($op, &$edit, &$account, $category = NULL) {
335 switch ($op) {
336 case 'load':
337 $domains = domain_get_user_domains($account);
338 $account->domain_user = $domains;
339 break;
340 case 'form':
341 case 'register':
342 if (is_null($category) || $category == 'account') {
343 global $_domain;
344 $result = db_query("SELECT domain_id, subdomain, sitename, scheme FROM {domain}");
345 $options = array();
346 // Get the domains for this user, but ignore roles unless told to use them.
347 $add_roles = variable_get('domain_add_roles', 0);
348 // In the register case, we take the 'new user' settings.
349 if ($op == 'register') {
350 $add_roles = TRUE;
351 }
352 $account->domain_user = domain_get_user_domains($account, $add_roles, TRUE);
353 // By default, the requesting domain is assigned.
354 if (empty($account->domain_user)) {
355 ($_domain['domain_id'] == 0) ? $default = array(-1) : $default = array($_domain['domain_id'] => $_domain['domain_id']);
356 }
357 else {
358 $default = $account->domain_user;
359 }
360 $options[-1] = variable_get('domain_sitename', variable_get('site_name', 'Drupal'));
361 while ($data = db_fetch_array($result)) {
362 $options[$data['domain_id']] = check_plain($data['sitename']);
363 }
364 // Replace the zero record to -1.
365 unset($options[0]);
366 $format = domain_select_format();
367 if (user_access('assign domain editors')) {
368 $form['domain_user'] = array(
369 '#type' => 'fieldset',
370 '#title' => t('Domain access'),
371 '#collapsible' => TRUE,
372 '#collapsed' => FALSE,
373 '#weight' => 1
374 );
375 $form['domain_user']['domain_user'] = array(
376 '#type' => empty($format) ? 'checkboxes' : 'select',
377 '#options' => $options,
378 '#title' => t('Domain access settings'),
379 '#description' => t('Select the affiliates that this user belongs to. Used to grant editing permissions for users with the "edit domain nodes" permission.'),
380 '#default_value' => $default
381 );
382 if ($format) {
383 $form['domain_user']['domain_user']['#multiple'] = TRUE;
384 $form['domain_user']['domain_user']['#size'] = count($options) > 10 ? 10 : count($options);
385 }
386 }
387 else {
388 $form['domain_user'] = array(
389 '#type' => 'value',
390 '#value' => $default
391 );
392 }
393 return $form;
394 }
395 break;
396 case 'validate':
397 return array('domain_user' => $edit['domain_user']);
398 break;
399 case 'insert':
400 case 'update':
401 // If our field element is missing, do nothing.
402 if (!isset($edit['domain_user'])) {
403 return;
404 }
405 // Clear and reset the {domain_editor} table.
406 db_query("DELETE FROM {domain_editor} WHERE uid = %d", $account->uid);
407 if (empty($edit['domain_user'])) {
408 return;
409 }
410 foreach ($edit['domain_user'] as $domain_id => $status) {
411 if ($status != 0) {
412 // Convert the -1 checkboxes to a zero.
413 if ($domain_id == -1) {
414 $domain_id = 0;
415 }
416 db_query("INSERT INTO {domain_editor} (uid, domain_id) VALUES (%d, %d)", $account->uid, $domain_id);
417 }
418 }
419 // Clear the $edit field.
420 $edit['domain_user'] = NULL;
421 break;
422 case 'view':
423 if (user_access('assign domain editors')) {
424 $account->content['domain'] = array(
425 '#type' => 'user_profile_category',
426 '#weight' => 10,
427 '#title' => t('Domain status'),
428 );
429 if (empty($account->domain_user)) {
430 $output = t('This user is not assigned to a domain.');
431 }
432 else {
433 $output = '<ul>';
434 foreach ($account->domain_user as $id) {
435 if (abs($id) > 0) {
436 if ($id > 0) {
437 $domain = domain_lookup($id);
438 $output .= '<li>'. $domain['sitename'] .'</li>';
439 }
440 else {
441 $output .= '<li>'. variable_get('domain_sitename', variable_get('site_name', 'Drupal')) .'</li>';
442 }
443 }
444 }
445 $output .= '</ul>';
446 }
447 $account->content['domain']['domain_settings'] = array(
448 '#type' => 'user_profile_item',
449 '#title' => t('Domain settings'),
450 '#value' => $output,
451 );
452 break;
453 }
454 }
455 }
456
457 /**
458 * Implement hook_user_operations().
459 */
460 function domain_user_operations() {
461 if (!user_access('assign domain editors')) {
462 return;
463 }
464 return array(
465 'domain' => array(
466 'label' => t('Assign users to domains'),
467 'callback' => 'domain_user_operation_assign',
468 ),
469 );
470 }
471
472 /**
473 * Implement hook_form_alter().
474 */
475 function domain_form_user_admin_account_alter(&$form, $form_state) {
476 if (!user_access('assign domain editors')) {
477 return;
478 }
479 $options = array();
480 $format = domain_select_format();
481 foreach (domain_domains() as $data) {
482 // Cannot pass zero in checkboxes.
483 ($data['domain_id'] == 0) ? $key = -1 : $key = $data['domain_id'];
484 // The domain must be valid.
485 if ($data['valid'] || user_access('access inactive domains')) {
486 // Filter checkbox output but not select list.
487 $options[$key] = empty($format) ? check_plain($data['sitename']) : $data['sitename'];
488 }
489 }
490 $form['domain'] = array(
491 '#type' => 'fieldset',
492 '#title' => t('Affiliate editor options'),
493 '#collapsible' => TRUE,
494 '#collapsed' => TRUE,
495 '#prefix' => '<div class="description">'. t('If you select <em>Assign users to domains</em> above, you should confirm the <em>Affiliate editor options</em> settings below.') .'</div>',
496 '#weight' => -1,
497 );
498 $form['domain']['domains'] = array(
499 '#type' => empty($format) ? 'checkboxes' : 'select',
500 '#title' => t('Assign to'),
501 '#options' => $options,
502 '#required' => FALSE,
503 '#description' => t('Select which affiliates these users should belong to. <em>Note: this will erase any current assignment for the selsected users.</em>'),
504 '#default_value' => array(($_domain['domain_id'] == 0) ? -1 : $_domain['domain_id']), // Can't use 0 as a checkbox value.
505 );
506 if ($format) {
507 $form['domain']['domains']['#multiple'] = TRUE;
508 $form['domain']['domains']['#size'] = count($options) > 10 ? 10 : count($options);
509 }
510 // Add our domain elements.
511 foreach (array_keys($form['name']) as $uid) {
512 $form['user_domains'][$uid][0] = array('#value' => theme('item_list', _domain_user_list($uid)));
513 }
514 $form['#theme'] = 'domain_admin_users_form';
515 $form['#submit'][] = 'domain_update_users';
516 }
517
518 /**
519 * Helper function to get the names of all domains for a user.
520 *
521 * @param $uid
522 * The user id.
523 * @return
524 * An array of domain names.
525 */
526 function _domain_user_list($uid) {
527 $temp_account = new stdClass;
528 $temp_account->uid = $uid;
529 $list = domain_get_user_domains($temp_account, FALSE);
530 $domains = array();
531 foreach ($list as $domain_id) {
532 if ($domain_id == -1) {
533 $domain_id = 0;
534 }
535 $domains[] = check_plain(db_result(db_query("SELECT sitename FROM {domain} WHERE domain_id = %d ORDER BY domain_id ASC", $domain_id)));
536 }
537 return $domains;
538 }
539
540 /**
541 * Callback for domain_content_node_operations().
542 *
543 * This callback is required, but we actually do our action inside
544 * of domain_update_users().
545 */
546 function domain_user_operation_assign($accounts) {
547 }
548
549 /**
550 * FormsAPI to handle the batch update of users.
551 */
552 function domain_update_users($form, &$form_state) {
553 $values = $form_state['values'];
554 if ($values['operation'] != 'domain') {
555 return;
556 }
557 foreach ($values['accounts'] as $uid) {
558 db_query("DELETE FROM {domain_editor} WHERE uid = %d", $uid);
559 foreach (array_filter($values['domains']) as $domain_id) {
560 // Cannot use 0 as a checkbox.
561 if ($domain_id == -1) {
562 $domain_id = 0;
563 }
564 db_query("INSERT INTO {domain_editor} (uid, domain_id) VALUES (%d, %d)", $uid, $domain_id);
565 }
566 }
567 }
568
569 /**
570 * Implement hook_cron()
571 *
572 * This function invokes hook_domaincron() and allows
573 * Domain Access modules to run functions for all active affiliates.
574 */
575 function domain_cron() {
576 global $_domain;
577 // Check to see if this function is needed at all.
578 $modules = module_implements('domaincron');
579 if (!empty($modules)) {
580 // Store the current $_domain global.
581 $_temp = $_domain;
582 // Get the domain list.
583 $domains = domain_domains();
584 // Run the hook for each active domain.
585 foreach ($domains as $domain) {
586 domain_set_domain($domain['domain_id'], TRUE);
587 foreach ($modules as $module) {
588 module_invoke($module, 'domaincron', $domain);
589 }
590 }
591 // Set the $_domain global back.
592 $_domain = $_temp;
593 }
594 }
595
596 /**
597 * Menu loader function.
598 *
599 * The passed parameter will be checked against the {domain} table for
600 * valid records.
601 *
602 * @param $domain_id
603 * The id request for a specific domain.
604 * @return
605 * $domain array on success or FALSE on failure.
606 */
607 function domain_load($domain_id = NULL) {
608 $domain = domain_lookup($domain_id);
609 if ($domain == -1) {
610 return FALSE;
611 }
612 else {
613 return $domain;
614 }
615 }
616
617 /**
618 * Runs a lookup against the {domain} table. One of the two values must be present
619 *
620 * This function also calls hook_domainload(), which lets module developers overwrite
621 * or add to the $domain array.
622 *
623 * @param $domain_id
624 * The domain_id taken from {domain}. Optional.
625 * @param $subdomain
626 * The string representation of a {domain} entry. Optional.
627 * @param $reset
628 * A boolean flag to clear the static variable if necessary.
629 * @return
630 * An array containing the requested row from the {domain} table, plus the
631 * elements added by hook_domainload(). Returns -1 on failure.
632 */
633 function domain_lookup($domain_id = NULL, $subdomain = NULL, $reset = FALSE) {
634 static $domains;
635 // If both are NULL, no lookup can be run.
636 if (is_null($domain_id) && is_null($subdomain)) {
637 return -1;
638 }
639 // Create a unique key so we can static cache all requests.
640 $key = $domain_id . $subdomain;
641 // Run the lookup, if needed.
642 if (!isset($domains[$key]) || $reset) {
643 if ($subdomain) {
644 $domain = db_fetch_array(db_query("SELECT domain_id, subdomain, sitename, scheme, valid FROM {domain} WHERE subdomain = '%s'", $subdomain));
645 }
646 else if ($domain_id == 0) {
647 $domain = domain_default();
648 }
649 else {
650 $domain = db_fetch_array(db_query("SELECT domain_id, subdomain, sitename, scheme, valid FROM {domain} WHERE domain_id = %d", $domain_id));
651 }
652 // Did we get a valid result?
653 if (isset($domain['domain_id'])) {
654 // Let Domain Access module extensions act to override the defaults.
655 $domains[$key] = domain_api($domain, $reset);
656 }
657 else {
658 $domains[$key] = -1;
659 }
660 }
661 return $domains[$key];
662 }
663
664 /**
665 * Assigns the default settings to domain 0, the root domain.
666 *
667 * This value is used throughout the modules. Even though this
668 * record is in the {domain} table, we use the value stored as
669 * a variable. Doing so prevents the module from firing when
670 * it has not been configured.
671 *
672 * @param $reset
673 * A boolean flag indicating whether to reset the static array or not.
674 */
675 function domain_default($reset = FALSE) {
676 static $default;
677 if (empty($default) || $reset) {
678 $default['domain_id'] = 0;
679 $default['sitename'] = variable_get('domain_sitename', variable_get('site_name', 'Drupal'));
680 $default['subdomain'] = variable_get('domain_root', '');
681 $default['scheme'] = variable_get('domain_scheme', 'http');
682 // Set the valid flag.
683 $default['valid'] = TRUE;
684 // Let submodules overwrite the defaults, if they wish.
685 $default = domain_api($default);
686 }
687 return $default;
688 }
689
690 /**
691 * Set the primary domain properly, if necessary.
692 */
693 function domain_set_primary_domain() {
694 $root = strtolower(rtrim($_SERVER['SERVER_NAME']));
695 $site = variable_get('site_name', 'Drupal');
696 $scheme = 'http';
697 if (!empty($_SERVER['HTTPS'])) {
698 $scheme = 'https';
699 }
700 db_query("UPDATE {domain} SET subdomain = '%s', sitename = '%s', scheme = '%s', valid = 1 WHERE domain_id = 0", $root, $site, $scheme);
701 if (!db_affected_rows()) {
702 db_query("INSERT INTO {domain} (subdomain, sitename, scheme, valid) VALUES ('%s', '%s', '%s', %d)", $root, $site, $scheme, 1);
703 // MySQL won't let us insert row 0 into an autoincrement table.
704 // Similar to the {users} table, this leaves us with no row 1.
705 db_query("UPDATE {domain} SET domain_id = domain_id - 1");
706 }
707 // Set the default domain variables.
708 variable_set('domain_root', $root);
709 variable_set('domain_scheme', $scheme);
710 variable_set('domain_sitename', $site);
711
712 // Allow other modules to respond to changes.
713 module_invoke_all('domainupdate', 'update', domain_default(TRUE));
714 }
715
716 /**
717 * Return all active domains (including the default) as an array.
718 *
719 * @param $reset
720 * A boolean flag indicating whether to reset the static array or not.
721 * @return
722 * An array of all active domains, with the domain_id as the key.
723 */
724 function domain_domains($reset = FALSE) {
725 static $domains;
726 if (empty($domains) || $reset) {
727 $domains = array();
728 // Query the db for active domain records.
729 $result = db_query("SELECT domain_id FROM {domain}");
730 while ($data = db_fetch_array($result)) {
731 $domain = domain_lookup($data['domain_id'], NULL, TRUE);
732 $domains[$domain['domain_id']] = $domain;
733 }
734 }
735 $sort = variable_get('domain_sort', 'id');
736 uasort($domains, '_domain_'. $sort .'_sort');
737 return $domains;
738 }
739
740 /**
741 * Helper sort function
742 */
743 function _domain_id_sort($a, $b) {
744 return ($a['domain_id'] < $b['domain_id']) ? -1 : 1;
745 }
746
747 /**
748 * Helper sort function
749 */
750 function _domain_name_sort($a, $b) {
751 return strcmp($a['sitename'], $b['sitename']);
752 }
753
754 /**
755 * Helper sort function
756 */
757 function _domain_url_sort($a, $b) {
758 return strcmp($a['subdomain'], $b['subdomain']);
759 }
760
761 /**
762 * Helper sort function
763 */
764 function _domain_rid_sort($a, $b) {
765 return ($a['domain_id'] > $b['domain_id']) ? -1 : 1;
766 }
767
768 /**
769 * Helper sort function
770 */
771 function _domain_rname_sort($a, $b) {
772 return strcmp($b['sitename'], $a['sitename']);
773 }
774
775 /**
776 * Helper sort function
777 */
778 function _domain_rurl_sort($a, $b) {
779 return strcmp($a['subdomain'], $b['subdomain']);
780 }
781
782 /**
783 * Determine the default format for domain list forms.
784 */
785 function domain_select_format() {
786 $domains = domain_domains();
787 $format = 0;
788 if (count($domains) > variable_get('domain_list_size', DOMAIN_LIST_SIZE)) {
789 $format = 1;
790 }
791 return variable_get('domain_select_format', $format);
792 }
793
794 /**
795 * Validates a domain string.
796 * @param string $subdomain
797 * The string to check for domain validity
798 * @return array
799 * List of error messages or empty array.
800 */
801 function domain_validate($subdomain) {
802 $error_list = array();
803 // Validate the domains format generically for now.
804 $error = domain_valid_domain($subdomain);
805 if (!empty($error)) {
806 $error_list[] = $error;
807 }
808 // Make sure domain is unique
809 if (!domain_unique_domain($subdomain)) {
810 $error_list[] = t('The domain value must be unique.');
811 }
812 return $error_list;
813 }
814
815 /**
816 * Validate the domain against all correctable errors.
817 *
818 * Note that we decided not to check for valid TLDs here.
819 *
820 * @param $subdomain
821 * Domain string to check.
822 * @return string
823 * Empty if valid, error message on invalid.
824 */
825 function domain_valid_domain($subdomain) {
826 $error_list = array();
827 // Check for one colon only.
828 if (substr_count($subdomain, ':') > 1) {
829 $error_list[] = t('Only one colon (:) is allowed.');
830 }
831 // If a colon, make sure it is only followed by numbers.
832 else if (substr_count($subdomain, ':') == 1) {
833 $parts = explode(':', $subdomain);
834 $port = (int) $parts[1];
835 if (empty($port) || $port != (float) $parts[1]) {
836 $error_list[] = t('The port protocol must be an integer.');
837 }
838 }
839 // The domain cannot begin or end with a period.
840 if (substr($subdomain, 0, 1) == '.') {
841 $error_list[] = t('The domain must not begin with a dot (.)');
842 }
843 // The domain cannot begin or end with a period.
844 if (substr($subdomain, -1) == '.') {
845 $error_list[] = t('The domain must not end with a dot (.)');
846 }
847 // Check for valid characters
848 $pattern = '/^[a-z0-9\.\+\-\*\?:]*$/i';
849 if (!preg_match($pattern, $subdomain)) {
850 $error_list[] = t('Only alphanumeric characters, dashes, and a colon are allowed.');
851 }
852 if (!empty($error_list)) {
853 return t('The domain string is invalid:') . theme('item_list', $error_list);
854 }
855 }
856
857 /**
858 * Validate the domain against existing domains.
859 *
860 * @param $subdomain
861 * Domain string to check
862 * @return bool
863 * TRUE if unique; FALSE if duplicate.
864 */
865 function domain_unique_domain($subdomain) {
866 $count = db_result(db_query("SELECT COUNT(domain_id) FROM {domain} WHERE subdomain = '%s'", $subdomain));
867 return !(bool) $count;
868 }
869
870 /**
871 * Get the domains a user is assigned to.
872 *
873 * @param $account
874 * The user account object.
875 * @param $add_roles
876 * A boolean flag indicating whether to add the default role settings to the user's domains.
877 * @param $reset
878 * A boolean flag indicating whether to reset the static array or not.
879 * @return
880 * An array of domains to which the user is assigned, in the format array($domain_id => $domain_id).
881 * Note that the default domain is -1 here, due to checkbox behavior.
882 */
883 function domain_get_user_domains($account, $add_roles = TRUE, $reset = FALSE) {
884 static $domains = array();
885 if (empty($account)) {
886 // This may happen when creating a new user.
887 return array();
888 }
889 $uid = (int) $account->uid;
890 if (!isset($domains[$uid]) || $reset) {
891 $domains[$uid] = array();
892 $result = db_query("SELECT domain_id FROM {domain_editor} WHERE uid = %d", $uid);
893 while ($data = db_fetch_object($result)) {
894 if ($data->domain_id == 0) {
895 $domains[$uid]['-1'] = -1;
896 }
897 else {
898 $domains[$uid][$data->domain_id] = $data->domain_id;
899 }
900 }
901 if ($add_roles) {
902 if (empty($account->roles)) {
903 $account->roles = array(0 => 'new user');
904 }
905 // Add the role-based additions.
906 $defaults = variable_get('domain_roles', array());
907 foreach ($account->roles as $rid => $role) {
908 $filter = array();
909 if (isset($defaults[$rid])) {
910 $filter = array_filter($defaults[$rid]);
911 }
912 if (!empty($filter)) {
913 foreach ($filter as $domain_id => $status) {
914 if ($status) {
915 $domains[$uid][$domain_id] = $domain_id;
916 }
917 }
918 }
919 }
920 }
921 }
922 return $domains[$uid];
923 }
924
925 /**
926 * Helper function for passing hook_domainload() by reference.
927 *
928 * @param $domain
929 * The domain array defined by domain_lookup().
930 * @param $reset
931 * A boolean flag to clear the static variable if necessary.
932 * @return
933 * The $domain array, modified by reference by hook_domainload() implementations.
934 */
935 function domain_api($domain, $reset = FALSE) {
936 static $_modules;
937 if (!isset($_modules) || $reset) {
938 $_modules = module_implements('domainload');
939 }
940 if (!empty($_modules)) {
941 foreach ($_modules as $module) {
942 // Cannot use module_invoke_all() since these are passed by reference.
943 $function = $module .'_domainload';
944 $function($domain);
945 }
946 }
947 return $domain;
948 }
949
950 /**
951 * Set the active domain to something other than the HTTP request.
952 *
953 * This function is used in cases where you wish to similuate the loading
954 * of a domain while on another domain.
955 *
956 * @param $domain_id
957 * The domain id of the domain to load.
958 * @param $bootstrap
959 * Boolean flag that indicates whether to run domain bootstrap load.
960 * @return
961 * No return value. The global $_domain value is altered, and domain-specific
962 * data functions are loaded.
963 */
964 function domain_set_domain($domain_id, $bootstrap = FALSE) {
965 global $_domain;
966 $_domain = domain_load($domain_id);
967 // Now re-run the bootstrap.
968 if ($bootstrap) {
969 _domain_bootstrap_invoke_all('full', $_domain);
970 }
971 }
972
973 /**
974 * Return the currently active domain.
975 *
976 * This value is stored in a global, but having a function
977 * will let us replace that with a static function in D7.
978 *
979 * @return
980 * An array of data defining the currently active domain.
981 */
982 function domain_get_domain() {
983 return $GLOBALS['_domain'];
984 }
985
986 /**
987 * Check to see if a redirect to the primary domain is needed.
988 *
989 * If TRUE, issue a redirect and print a message.
990 *
991 * @param $msg
992 * The message to print. Optional. If passed, this string must be translated and safe.
993 */
994 function domain_check_primary($msg = 'default') {
995 global $_domain;
996 $default = domain_default();
997 if ($_domain['domain_id'] != $default['domain_id']) {
998 if ($msg == 'default') {
999 drupal_set_message(t('You have been redirected: This page must be accessed from the primary domain.'));
1000 }
1001 else if (!empty($msg)) {
1002 drupal_set_message($msg);
1003 }
1004 domain_goto($default);
1005 }
1006 }
1007
1008 /**
1009 * Implement hook_domainload()
1010 *
1011 * Adds the home page 'path' and 'site_grant' boolean.
1012 */
1013 function domain_domainload(&$domain) {
1014 // Get the path to the home page for this domain.
1015 $domain['path'] = domain_get_path($domain);
1016 // Grant access to all affiliates.
1017 $domain['site_grant'] = DOMAIN_SITE_GRANT;
1018 }
1019
1020 /**
1021 * Determine an absolute path for a domain
1022 *
1023 * @param $domain
1024 * The currently active $domain array, provided by domain_lookup().
1025 * @return
1026 * The base url of the requested domain.
1027 */
1028 function domain_get_path($domain) {
1029 global $base_url;
1030 if (empty($base_url)) {
1031 return check_url($domain['scheme'] .'://'. $domain['subdomain']);
1032 }
1033 $_url = parse_url($base_url);
1034 // PHP 5 does not return an empty path element.
1035 if (!isset($_url['path'])) {
1036 $_url['path'] = '/';
1037 }
1038 // We need a trailing slash at the end of the path
1039 if (substr($_url['path'], -1) != '/') {
1040 $_url['path'] .= '/';
1041 }
1042 $path = check_url($domain['scheme'] .'://'. $domain['subdomain'] . $_url['path']);
1043 return $path;
1044 }
1045
1046 /**
1047 * Determine an absolute path to the current page
1048 *
1049 * @param $domain
1050 * The currently active $domain array, provided by domain_lookup().
1051 * @return
1052 * The absolute url to the current page on the requested domain.
1053 */
1054 function domain_get_uri($domain) {
1055 $request_uri = request_uri();
1056
1057 $modules = _domain_path_modules();
1058 if (!empty($modules) && !drupal_is_front_page()) {
1059 // If needed, let modules modify the path alias.
1060 $alias = domain_path($domain['domain_id'], $_GET['q']);
1061 // Run the result through url() for proper language and path handling.
1062 $request_uri = url($alias);
1063 }
1064
1065 $path = check_url($domain['scheme'] .'://'. $domain['subdomain'] . $request_uri);
1066 return $path;
1067 }
1068
1069 /**
1070 * Determine if we must switch the active domain.
1071 *
1072 * This function will execute a drupal_goto() to pop users to the correct
1073 * domain.
1074 *
1075 * @param $domain
1076 * The currently active $domain array, provided by domain_lookup().
1077 */
1078 function domain_goto($domain) {
1079 global $_domain;
1080 // We must be on the proper domain, see http://drupal.org/node/186153.
1081 if ($domain != -1 && $_domain['domain_id'] != $domain['domain_id']) {
1082 $path = domain_get_uri($domain);
1083 drupal_goto($path);
1084 }
1085 }
1086
1087 /**
1088 * Implement hook_nodeapi().
1089 *
1090 * This function is used to provide debugging information and to prep values from
1091 * the {domain_access} table when editing nodes. Since not all users can see the
1092 * domain access editing checkboxes, we pass some node_access values as hidden elements.
1093 */
1094 function domain_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
1095 switch ($op) {
1096 case 'prepare':
1097 case 'load':
1098 // Cannot load if the node has not been created yet.
1099 if (!isset($node->nid)) {
1100 return;
1101 }
1102 // Append the domain grants to the node for editing.
1103 $domains = domain_get_node_domains($node->nid);
1104 $node->domains = $domains['domain_id'];
1105 $node->domain_site = $domains['domain_site'];
1106 $node->subdomains = array();
1107 if ($node->domain_site) {
1108 $node->subdomains[] = t('All affiliates');
1109 }
1110 foreach ($node->domains as $gid) {
1111 if ($gid > 0) {
1112 $domain = domain_lookup($gid);
1113 $node->subdomains[] = $domain['sitename'];
1114 }
1115 else {
1116 $node->subdomains[] = variable_get('domain_sitename', variable_get('site_name', 'Drupal'));
1117 }
1118 }
1119 break;
1120 case 'view':
1121 // Search module casts both $a3 and $a4 as FALSE, not NULL.
1122 // We check that to hide this data from search and other nodeapi
1123 // calls that are neither a teaser nor a page view.
1124 if ($a3 !== FALSE || $a4 !== FALSE) {
1125 $output = '';
1126 $debug = variable_get('domain_debug', 0);
1127 if ($debug && user_access('set domain access')) {
1128 if (!empty($node->subdomains)) {
1129 $output = theme('item_list', $node->subdomains, t('Assigned domains'));
1130 $node->content['subdomains'] = array('#value' => $output, '#weight' => 20);
1131 }
1132 if (!empty($node->editors)) {
1133 $output = theme('item_list', $node->editors, t('Editors'));
1134 $node->content['editors'] = array('#value' => $output, '#weight' => 21);
1135 }
1136 if (empty($output)) {
1137 $node->content['domain'] = array('#value' => t('This node is not assigned to a domain.'), '#weight' => 22);
1138 }
1139 }
1140 }
1141 break;
1142 case 'delete':
1143 // Remove records from the {domain_access} table.
1144 db_query("DELETE FROM {domain_access} WHERE nid = %d", $node->nid);
1145 break;
1146 case 'insert':
1147 case 'update':
1148 $_SESSION['domain_nid'] = $node->nid;
1149 break;
1150 }
1151 }
1152
1153 /**
1154 * Get the best matching domain for a node link.
1155 *
1156 * @param $nid
1157 * The node id.
1158 * @return
1159 * The domain array for the best matching domain for links to this node.
1160 */
1161 function domain_get_node_match($nid) {
1162 static $domain = array();
1163 if (isset($domain[$nid])) {
1164 return $domain[$nid];
1165 }
1166 // Load the domain data for this node -- but only take the first match.
1167 $id = db_result(db_query_range("SELECT gid FROM {domain_access} WHERE nid = %d AND realm = '%s' ORDER BY gid", $nid, 'domain_id', 0, 1));
1168 $source = domain_lookup($id);
1169 drupal_alter('domain_source', $source, $nid);
1170 $domain[$nid] = $source;
1171 return $source;
1172 }
1173
1174 /**
1175 * Allow the lookup of path rewrites.
1176 *
1177 * Note that unlinke domain_get_node_match(), this function assumes
1178 * that all links will be written to the current domain.
1179 *
1180 * @param $path
1181 * The link path.
1182 * @return
1183 * The domain array for the best matching domain for links to this node.
1184 */
1185 function domain_get_path_match($path) {
1186 global $_domain;
1187 $source = $_domain;
1188 drupal_alter('domain_source_path', $source, $path);
1189 return $source;
1190 }
1191
1192 /**
1193 * Get the domains for a node.
1194 *
1195 * @param $nid
1196 * The node id.
1197 * @return
1198 * An array, consisting of two parts. 'domain_id' is an array of active domain ids. 'domain_site'
1199 * is a TRUE/FALSE boolean indicating affiliate status.
1200 */
1201 function domain_get_node_domains($nid) {
1202 static $lookup = array();
1203 if (isset($lookup[$nid])) {
1204 return $lookup[$nid];
1205 }
1206 $domains = array('domain_id' => array(), 'domain_site' => FALSE);
1207 $result = db_query("SELECT gid, realm FROM {domain_access} WHERE nid = %d AND (realm = '%s' OR realm = '%s')", $nid, 'domain_id', 'domain_site');
1208 while ($data = db_fetch_object($result)) {
1209 // Transform the 0 to -1, since {domain_access} is unsigned.
1210 ($data->gid == 0) ? $gid = -1 : $gid = $data->gid;
1211 if ($data->realm == 'domain_id') {
1212 $domains['domain_id'][$gid] = $gid;
1213 }
1214 else if ($data->realm == 'domain_site') {
1215 $domains['domain_site'] = TRUE;
1216 }
1217 }
1218 $lookup[$nid