| 1 |
<?php
|
| 2 |
/* $Id: crossite.module,v 1.2 2007/05/16 02:52:03 hanenkamp Exp $ */
|
| 3 |
|
| 4 |
// This is a Drupal module for allowing a person to share nodes across a
|
| 5 |
// Drupal "multi-site" install. This can be done natively from Drupal without
|
| 6 |
// any help from this module.
|
| 7 |
//
|
| 8 |
// This module helps categorize shared nodes in a multi-site configuration
|
| 9 |
// using taxonomy.
|
| 10 |
|
| 11 |
/**
|
| 12 |
* Returns a list of available configuration paths. This is done by finding
|
| 13 |
* all the directory names under ./sites, excluding "all".
|
| 14 |
*
|
| 15 |
* @return
|
| 16 |
* An array of configuration paths that can be used to locate settings files.
|
| 17 |
*/
|
| 18 |
function crossite_get_all_sites() {
|
| 19 |
$files = file_scan_directory('./sites', '.*', array('.', '..', 'CVS', '.svn', 'all'), 0, FALSE);
|
| 20 |
$keys = array_keys($files);
|
| 21 |
array_walk($keys, create_function('&$str', '$str = substr($str, 8);'));
|
| 22 |
|
| 23 |
return $keys;
|
| 24 |
}
|
| 25 |
|
| 26 |
/**
|
| 27 |
* Returns the name of the current site being used. This is derived from the
|
| 28 |
* output of conf_path(), there may be a better way to do this.
|
| 29 |
*
|
| 30 |
* @return
|
| 31 |
* The name of the site config directory, which is used to lookup values in
|
| 32 |
* the crossite_tids and crossite_bases settings. The return value is just the
|
| 33 |
* file name and does not include the sites component.
|
| 34 |
*/
|
| 35 |
function crossite_get_current_site() {
|
| 36 |
$conf_path = conf_path();
|
| 37 |
return substr($conf_path, 6);
|
| 38 |
}
|
| 39 |
|
| 40 |
/**
|
| 41 |
* Returns the base URL for the named site. The $base_url variable must be set
|
| 42 |
* correctly in each of the site configuration files.
|
| 43 |
*
|
| 44 |
* This function caches the result so that it doesn't need to be calculated
|
| 45 |
* more than once. It is assumed that a given site's base URL does not change.
|
| 46 |
* If you change the base URL of a given site, you should make sure to empty the
|
| 47 |
* cache table in the database so this value will be recalculated.
|
| 48 |
*
|
| 49 |
* @param $site
|
| 50 |
* The site to retrieve the base URL of. If not given, assumes 'default'.
|
| 51 |
*
|
| 52 |
* @return
|
| 53 |
* The absolute base URL of the site named by the configuration path.
|
| 54 |
*/
|
| 55 |
function crossite_get_base_url($site = 'default') {
|
| 56 |
$site_cache = 'crossite_base_url[' . $site . ']';
|
| 57 |
if ($cached = cache_get($site_cache, 'cache')) {
|
| 58 |
return $cached->data;
|
| 59 |
}
|
| 60 |
|
| 61 |
include './sites/' . $site . '/settings.php';
|
| 62 |
cache_set($site_cache, 'cache', $base_url, CACHE_PERMANENT);
|
| 63 |
return $base_url;
|
| 64 |
}
|
| 65 |
|
| 66 |
function crossite_mapping() {
|
| 67 |
$tids = array();
|
| 68 |
|
| 69 |
if ($cached = cache_get('crossite_tids')) {
|
| 70 |
$tids = unserialize($cached->data);
|
| 71 |
}
|
| 72 |
|
| 73 |
else {
|
| 74 |
$result = db_query('SELECT tid, conf_path FROM {crossite_domains_tids}');
|
| 75 |
while ($row = db_fetch_object($result)) {
|
| 76 |
$tids[ $row->tid ] = $row->conf_path;
|
| 77 |
}
|
| 78 |
cache_set('crossite_tids', 'cache', serialize($tids), CACHE_TEMPORARY);
|
| 79 |
}
|
| 80 |
|
| 81 |
return $tids;
|
| 82 |
}
|
| 83 |
|
| 84 |
/**
|
| 85 |
* Implements hook_view(). If the node being viewed does not belong to this site
|
| 86 |
* and the crossite_redirect option is set to TRUE, then a this implementation
|
| 87 |
* will cause a redirect to be performed. The redirect will go to either the
|
| 88 |
* site that the node belongs to or to the default site if it belongs to no
|
| 89 |
* particular site. If this site is the default site, then no redirect will be
|
| 90 |
* performed if the redirect would return to this site.
|
| 91 |
*/
|
| 92 |
function crossite_nodeapi(&$node, $op, $extra = NULL, $page = FALSE) {
|
| 93 |
switch($op) {
|
| 94 |
case 'load':
|
| 95 |
$tids = crossite_mapping();
|
| 96 |
|
| 97 |
$sites = array();
|
| 98 |
$terms = taxonomy_node_get_terms($node->nid);
|
| 99 |
|
| 100 |
// Find all the sites that match the node
|
| 101 |
foreach ($tids as $tid => $conf_path) {
|
| 102 |
|
| 103 |
if (array_key_exists($tid, $terms)) {
|
| 104 |
$sites[$conf_path] = 1;
|
| 105 |
}
|
| 106 |
}
|
| 107 |
|
| 108 |
// Add 'default' if no other site matched
|
| 109 |
if (empty($sites)) {
|
| 110 |
$sites['default'] = 1;
|
| 111 |
}
|
| 112 |
|
| 113 |
// Set the 'sites' to just the keys
|
| 114 |
return array( 'sites' => array_keys($sites) );
|
| 115 |
|
| 116 |
case 'view':
|
| 117 |
// No redirect unless this is the node's very own page
|
| 118 |
if (!$page) {
|
| 119 |
return;
|
| 120 |
}
|
| 121 |
|
| 122 |
// Lookup this site
|
| 123 |
$this_site = crossite_get_current_site();
|
| 124 |
|
| 125 |
// Use the sites associated with the node during load. However, do NOT
|
| 126 |
// assume that it hasn't been tampered with. If this array comes out empty,
|
| 127 |
// make sure to drop 'default' back into it. This will help to prevent a
|
| 128 |
// redirect cycle.
|
| 129 |
$sites = !empty($node->sites) ? $node->sites : array('default');
|
| 130 |
|
| 131 |
// This node belongs to the current site: do not redirect
|
| 132 |
if (in_array($this_site, $sites)) {
|
| 133 |
return;
|
| 134 |
}
|
| 135 |
|
| 136 |
// Otherwise, go to the first site in the list
|
| 137 |
$to_site = $sites[0];
|
| 138 |
$base_url = crossite_get_base_url($to_site);
|
| 139 |
|
| 140 |
// This error occurs when crossite_tids is setup, but crossite_bases
|
| 141 |
// is not.
|
| 142 |
if (!isset($base_url)) {
|
| 143 |
watchdog('crossite',
|
| 144 |
t('The crossite configuration indicates a redirect to %site, but no '.
|
| 145 |
'base URL was found for to get there. You need to edit the settings.php '.
|
| 146 |
'file for that site.',
|
| 147 |
array(
|
| 148 |
'%site' => $to_site,
|
| 149 |
)), WATCHDOG_ERROR);
|
| 150 |
|
| 151 |
// An error, but we can show the node as is.
|
| 152 |
return;
|
| 153 |
}
|
| 154 |
|
| 155 |
// Get the base URL and use it to construct a redirect.
|
| 156 |
$query_string = array();
|
| 157 |
parse_str($_SERVER['QUERY_STRING'], $query_string);
|
| 158 |
if (isset($query_string['q'])) {
|
| 159 |
unset($query_string['q']);
|
| 160 |
}
|
| 161 |
|
| 162 |
$rel_url = url('node/'.$node->nid);
|
| 163 |
header('HTTP/1.1 301 Moved Permanently');
|
| 164 |
header('Location: '.$base_url.'/?q=node/'.$node->nid, http_build_query($query_string));
|
| 165 |
exit;
|
| 166 |
}
|
| 167 |
}
|
| 168 |
|
| 169 |
/**
|
| 170 |
* Implements hook_menu().
|
| 171 |
*/
|
| 172 |
# function crossite_menu($may_cache) {
|
| 173 |
# $items = array();
|
| 174 |
#
|
| 175 |
# # if ($may_cache) {
|
| 176 |
# # $items[] = array(
|
| 177 |
# # 'path' => 'admin/content/crossite',
|
| 178 |
# # 'title' => t('Crossite Terms'),
|
| 179 |
# # 'access' => user_access('administer crossite'),
|
| 180 |
# # 'callback' => 'crossite_admin_terms',
|
| 181 |
# # 'type' => MENU_NORMAL_ITEM,
|
| 182 |
# # );
|
| 183 |
# # }
|
| 184 |
# #
|
| 185 |
# # else {
|
| 186 |
# # if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'crossite' && is_numeric(arg(3))) {
|
| 187 |
# # $did = arg(3);
|
| 188 |
# # $items[] = array(
|
| 189 |
# # 'path' => 'admin/content/crossite/new'
|
| 190 |
# # 'title' => t('Add'),
|
| 191 |
# # 'access' => user_access('administer crossite'),
|
| 192 |
# # 'callbak' =>
|
| 193 |
# #
|
| 194 |
# # }
|
| 195 |
#
|
| 196 |
# return $items;
|
| 197 |
# }
|
| 198 |
|
| 199 |
/**
|
| 200 |
* Provides a form for administering terms.
|
| 201 |
*/
|
| 202 |
# function crossite_admin_terms() {
|
| 203 |
# $header = array(
|
| 204 |
# array('data' => t('Term'), 'field' => 'name'),
|
| 205 |
# array('data' => t('Site'), 'field' => 'conf_path'),
|
| 206 |
# );
|
| 207 |
#
|
| 208 |
# $sql = '
|
| 209 |
# SELECT did, name, conf_path
|
| 210 |
# FROM {crossite_domains_tids} c INNER JOIN {term_data} t ON c.tid = t.tid';
|
| 211 |
# $sql .= tablesort_sql($header);
|
| 212 |
# $result = pager_query($sql, 20);
|
| 213 |
#
|
| 214 |
# while ($domain_tid = db_fetch_object($result)) {
|
| 215 |
# $rows[] = array(
|
| 216 |
# l($domain_tid->name, 'admin/content/crossite/'.$domain_tid->did),
|
| 217 |
# $domain_tid->conf_path,
|
| 218 |
# );
|
| 219 |
# }
|
| 220 |
#
|
| 221 |
# if (count($rows)) {
|
| 222 |
# $output = theme('table', $header, $rows);
|
| 223 |
#
|
| 224 |
# $pager = theme('pager', NULL, 20, 0);
|
| 225 |
# if (!empty($pager)) {
|
| 226 |
# $output .= $pager;
|
| 227 |
# }
|
| 228 |
# }
|
| 229 |
#
|
| 230 |
# else {
|
| 231 |
# $output .= '<p>'.t('No term has been linked with a site.').'</p>';
|
| 232 |
# }
|
| 233 |
#
|
| 234 |
# return $output;
|
| 235 |
# }
|
| 236 |
|
| 237 |
function crossite_form_alter($form_id, &$form) {
|
| 238 |
if ($form_id == 'taxonomy_form_term' && user_access('administer crossite')) {
|
| 239 |
// Can't use weight because it either puts the extra bits of the form
|
| 240 |
// above everything, after Name, or after Submit. I want it after Weight and
|
| 241 |
// before submit.
|
| 242 |
$form_keys = array_keys($form);
|
| 243 |
$submit_index = array_search('submit', $form_keys);
|
| 244 |
$form_start = array_splice($form, 0, $submit_index);
|
| 245 |
|
| 246 |
$all_sites = crossite_get_all_sites();
|
| 247 |
$available_conf_paths = array_combine($all_sites, $all_sites);
|
| 248 |
|
| 249 |
if (isset($form['#parameters'][2]['tid'])) {
|
| 250 |
$result = db_query("
|
| 251 |
SELECT conf_path
|
| 252 |
FROM {crossite_domains_tids}
|
| 253 |
WHERE tid = %d", $form['#parameters'][2]['tid']);
|
| 254 |
|
| 255 |
while ($conf = db_fetch_array($result)) {
|
| 256 |
$current_settings[] = $conf['conf_path'];
|
| 257 |
}
|
| 258 |
}
|
| 259 |
|
| 260 |
$crossite_form['crossite_fieldset'] = array(
|
| 261 |
'#type' => 'fieldset',
|
| 262 |
'#title' => t('Crossite Settings'),
|
| 263 |
'#collapsible' => TRUE,
|
| 264 |
'#collapsed' => TRUE,
|
| 265 |
);
|
| 266 |
|
| 267 |
$crossite_form['crossite_fieldset']['crossite_conf_path'] = array(
|
| 268 |
'#type' => 'checkboxes',
|
| 269 |
'#title' => t('Nodes with this term belong to the selected sites'),
|
| 270 |
'#description' => t('If none are checked, this term does not modify site membership.'),
|
| 271 |
'#default_value' => $current_settings,
|
| 272 |
'#options' => $available_conf_paths,
|
| 273 |
);
|
| 274 |
|
| 275 |
$form = array_merge($form_start, $crossite_form, $form);
|
| 276 |
}
|
| 277 |
}
|
| 278 |
|
| 279 |
function crossite_taxonomy($op, $type, $form_values = NULL) {
|
| 280 |
if ($type == 'term') {
|
| 281 |
if ($op == 'insert' || $op == 'update') {
|
| 282 |
$tid = $form_values['tid'];
|
| 283 |
$crossite_conf_path = $form_values['crossite_conf_path'];
|
| 284 |
|
| 285 |
db_query('DELETE FROM {crossite_domains_tids} WHERE tid = %d', $tid);
|
| 286 |
|
| 287 |
foreach ($crossite_conf_path as $conf_path) {
|
| 288 |
if ($conf_path) {
|
| 289 |
$result = db_query("INSERT INTO {crossite_domains_tids}(tid, conf_path) VALUES (%d, '%s')", $tid, $conf_path);
|
| 290 |
}
|
| 291 |
}
|
| 292 |
}
|
| 293 |
|
| 294 |
else {
|
| 295 |
db_query('DELETE FROM {crossite_domains_tids} WHERE tid = %d', $tid);
|
| 296 |
}
|
| 297 |
|
| 298 |
// Update the cache
|
| 299 |
cache_clear_all('crossite_tids');
|
| 300 |
crossite_mapping();
|
| 301 |
}
|
| 302 |
}
|
| 303 |
|
| 304 |
?>
|