!url.', array('!url' => $request))); } drupal_goto($request); } } // If we stripped the www. send the user to the proper domain. This should only // happen once, on an inbound link or typed URL, so the overhead is acceptable. if ($raw_domain != $_subdomain) { drupal_goto(domain_get_uri($_domain)); } // For Domain User, we check the validity of accounts, so the 'valid' flag must be TRUE. // If this check fails, we send users to the default site homepage. if (!$_domain['valid'] && !user_access('administer domains')) { $_domain = domain_default(); drupal_goto($_domain['path']); } // Set the site name to the domain-specific name. $conf['site_name'] = $_domain['sitename']; } /** * Implement hook_menu() */ function domain_menu($may_cache) { $items = array(); $admin = user_access('administer domains'); if ($may_cache) { $items[] = array( 'title' => t('Domains'), 'path' => 'admin/build/domain', 'access' => $admin, 'callback' => 'domain_admin', 'callback arguments' => array('view'), 'description' => t('Settings for the Domain Access module.'), ); $items[] = array( 'title' => t('Domain list'), 'path' => 'admin/build/domain/view', 'access' => $admin, 'type' => MENU_DEFAULT_LOCAL_TASK, 'callback' => 'domain_admin', 'callback arguments' => array('view'), 'weight' => -10, ); $items[] = array( 'title' => t('Settings'), 'path' => 'admin/build/domain/settings', 'access' => $admin, 'type' => MENU_LOCAL_TASK, 'callback' => 'domain_admin', 'callback arguments' => array('configure'), 'weight' => -8, ); $items[] = array( 'title' => t('Create domain record'), 'path' => 'admin/build/domain/create', 'access' => $admin, 'type' => MENU_LOCAL_TASK, 'callback' => 'domain_admin', 'callback arguments' => array('create'), 'weight' => -4, ); $items[] = array( 'title' => t('Node settings'), 'path' => 'admin/build/domain/advanced', 'access' => $admin, 'type' => MENU_LOCAL_TASK, 'callback' => 'domain_admin', 'callback arguments' => array('advanced'), 'weight' => -2, ); // Register the batch actions as menu callbacks $batch = module_invoke_all('domainbatch'); if (!empty($batch)) { $items[] = array( 'title' => t('Batch updating'), 'path' => 'admin/build/domain/batch', 'access' => $admin, 'type' => MENU_LOCAL_TASK, 'callback' => 'domain_admin', 'callback arguments' => array('batch'), 'weight' => 0, ); // Get the submenu items foreach ($batch as $key => $value) { $items[] = array( 'title' => $value['#form']['#title'], 'path' => 'admin/build/domain/batch/'. $key, 'access' => $admin, 'type' => MENU_CALLBACK, 'callback' => 'domain_admin', 'callback arguments' => array('batch', $key), 'weight' => $value['#weight'], ); } } } else { $items[] = array( 'title' => t('Edit domain record'), 'path' => 'admin/build/domain/edit', 'access' => $admin, 'type' => MENU_CALLBACK, 'callback' => 'domain_admin', 'callback arguments' => array('edit', arg(4)), ); $items[] = array( 'title' => t('Delete domain record'), 'path' => 'admin/build/domain/delete', 'access' => $admin, 'type' => MENU_CALLBACK, 'callback' => 'domain_admin', 'callback arguments' => array('delete', arg(4)), ); // Make sure that our default grant is set at all times. if (arg(0) == 'admin') { domain_set_default_grant(); } } return $items; } /** * Implement hook_perm() */ function domain_perm() { $perms = array('administer domains', 'assign domain editors', 'edit domain nodes', 'set domain access', 'view domain publishing'); return $perms; } /** * Implement hook_block() * * A nifty little domain-switcher block, useful during debugging. */ function domain_block($op = 'list', $delta = 0, $edit = array()) { global $_domain, $base_url; $blocks = array(); switch ($op) { case 'list': $blocks[0] = array( 'info' => t('Domain switcher'), ); $blocks[1] = array( 'info' => t('Domain access information'), ); return $blocks; break; case 'view': switch ($delta) { case 0: $block['subject'] = t('Domain switcher'); $items = array(); $domains = domain_domains(); $msg = FALSE; foreach ($domains as $domain) { if ($domain['valid']) { $title = $domain['sitename']; $allow = TRUE; } else { $title = $domain['sitename'] .' *'; $allow = FALSE; if (user_access('administer domains')) { $msg = TRUE; $allow = TRUE; } } if ($allow) { $items[] = l($title, domain_get_uri($domain)); } } $block['content'] = theme('item_list', $items); if ($msg) { $block['content'] .= t('* Inactive domain.'); } break; case 1: $block['content'] = ''; if (arg(0) == 'node' && is_numeric(arg(1))) { $block['subject'] = t('Domain access information'); $this_node = node_load(arg(1)); $output = ''; if (!empty($this_node->subdomains)) { $output .= theme('item_list', $this_node->subdomains, t('Subdomains')); } if (!empty($this_node->editors)) { $output .= theme('item_list', $this_node->editors, t('Editors')); } if (isset($this_node->domain_source)) { $this_domain = domain_lookup($this_node->domain_source); $output .= theme('item_list', array($this_domain['sitename']), t('Source domain')); } if (empty($output)) { $output = t('This node is not assigned to a domain.'); } $block['content'] = '

'. t('%node is published with the following Domain Access rules:', array('%node' => $this_node->title)) .'

'. $output; } return $block; break; } return $block; break; } } /** * Implement hook_user() * * Attached domain_id records to all registering users. These * are used to determine which 'domain_editor' group that users * with the 'edit domain nodes' permission are in. */ function domain_user($op, &$edit, &$account, $category = NULL) { switch ($op) { case 'form': case 'register': if (is_null($category) || $category == 'account') { global $_domain; $result = db_query("SELECT domain_id, subdomain, sitename, scheme FROM {domain}"); $options = array(); // By default, the requesting domain is assigned. if (empty($account->domain_user)) { ($_domain['domain_id'] == 0) ? $default = array(-1) : $default = array($_domain['domain_id']); } else { $default = $account->domain_user; } $options[-1] = variable_get('domain_sitename', variable_get('site_name', 'Drupal')); while ($data = db_fetch_array($result)) { $options[$data['domain_id']] = $data['sitename']; } if (user_access('assign domain editors')) { $form['domain_user'] = array( '#type' => 'fieldset', '#title' => t('Domain access'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => 1 ); $form['domain_user']['domain_user'] = array( '#type' => 'checkboxes', '#options' => $options, '#title' => t('Domain access setttings'), '#description' => t('Select the affiliates that this user belongs to. Used to grant editing permissions for users with the "edit domain nodes" permission.'), '#default_value' => $default ); } else { $form['domain_user'] = array( '#type' => 'value', '#value' => $default ); } return $form; } break; case 'validate': return array('domain_user' => $edit['domain_user']); break; case 'insert': case 'update': // If our field element is missing, do nothing. if (!isset($edit['domain_user'])) { return; } break; case 'view': if (user_access('assign domain editors') && !empty($account->domain_user)) { $output = ''; $items['domain'] = array( 'title' => t('Domain settings'), 'value' => $output, 'class' => 'status', ); return array(t('Domain status') => $items); } break; } } /** * Implement hook_cron() * * This function invokes hook_domaincron() and allows * Domain Access modules to run functions for all active affiliates. */ function domain_cron() { global $_domain; // Check to see if this function is needed at all. $modules = module_implements('domaincron'); if (!empty($modules)) { // Store the current $_domain global. $_temp = $_domain; // Get the domain list. $domains = domain_domains(); // Run the hook for each active domain. foreach ($domains as $domain) { // Set the domain-specific variables if (function_exists('_domain_conf_load')) { _domain_conf_load($domain); } // Set the global table prefix if (function_exists('_domain_prefix_load')) { _domain_prefix_load($domain); } // Set the global to the current $domain. $_domain = $domain; foreach ($modules as $module) { module_invoke($module, 'domaincron', $domain); } } // Set the $_domain global back. $_domain = $_temp; } } /** * Router function to call various administration tasks * * @param $action * The function to be performed. * @param $id * The domain_id of the record to be acted upon. */ function domain_admin($action, $id = NULL) { include_once('domain_admin.inc'); $func = 'domain_'. $action; return $func($id); } /** * Runs a lookup against the {domain} table. One of the two values must be present * * This function also calls hook_domainload(), which lets module developers overwrite * or add to the $domain array. * * @param $domain_id * The domain_id taken from {domain}. Optional. * @param $subdomain * The string representation of a {domain} entry. Optional. * @param $reset * A boolean flag to clear the static variable if necessary. * @return * An array containing the requested row from the {domain} table, plus the * elements added by hook_domainload(). Returns -1 on failure. */ function domain_lookup($domain_id = NULL, $subdomain = NULL, $reset = FALSE) { static $domains; // If both are NULL, no lookup can be run. if (is_null($domain_id) && is_null($subdomain)) { return -1; } // Create a unique key so we can static cache all requests. $key = $domain_id . $subdomain; // Run the lookup, if needed. if (!isset($domains[$key]) || $reset) { if ($subdomain) { $domain = db_fetch_array(db_query("SELECT domain_id, subdomain, sitename, scheme, valid FROM {domain} WHERE subdomain = '%s'", $subdomain)); } else if ($domain_id == 0) { $domain = domain_default(); } else { $domain = db_fetch_array(db_query("SELECT domain_id, subdomain, sitename, scheme, valid FROM {domain} WHERE domain_id = %d", $domain_id)); } // Did we get a valid result? if (isset($domain['domain_id'])) { // Let Domain Access module extensions act to override the defaults. $domains[$key] = domain_api($domain); } else { $domains[$key] = -1; } } return $domains[$key]; } /** * Assigns the default settings to domain 0, the root domain. * * This value is used throughout the modules, so needed abstraction. * * @param $reset * A boolean flag indicating whether to reset the static array or not. */ function domain_default($reset = FALSE) { static $default; if (empty($default) || $reset) { $default['domain_id'] = 0; $default['sitename'] = variable_get('domain_sitename', variable_get('site_name', 'Drupal')); $default['subdomain'] = variable_get('domain_root', ''); $default['scheme'] = variable_get('domain_scheme', 'http'); // Set the valid flag. $default['valid'] = TRUE; // Let submodules overwrite the defaults, if they wish. $default = domain_api($default); } return $default; } /** * Return all active domains (including the default) as an array. * * @param $reset * A boolean flag indicating whether to reset the static array or not. * @return * An array of all active domains, with the domain_id as the key. */ function domain_domains($reset = FALSE) { static $domains; if (empty($domains) || $reset) { $domains = array(); $domains[] = domain_default($reset); // Query the db for active domain records. $result = db_query("SELECT domain_id FROM {domain}"); while ($data = db_fetch_array($result)) { $domain = domain_lookup($data['domain_id'], NULL, TRUE); $domains[$domain['domain_id']] = $domain; } } $sort = variable_get('domain_sort', 'id'); uasort($domains, '_domain_'. $sort .'_sort'); return $domains; } /** * Helper sort function */ function _domain_id_sort($a, $b) { return ($a['domain_id'] < $b['domain_id']) ? -1 : 1; } /** * Helper sort function */ function _domain_name_sort($a, $b) { return strcmp($a['sitename'], $b['sitename']); } /** * Helper sort function */ function _domain_url_sort($a, $b) { return strcmp($a['subdomain'], $b['subdomain']); } /** * Helper sort function */ function _domain_rid_sort($a, $b) { return ($a['domain_id'] > $b['domain_id']) ? -1 : 1; } /** * Helper sort function */ function _domain_rname_sort($a, $b) { return strcmp($b['sitename'], $a['sitename']); } /** * Helper sort function */ function _domain_rurl_sort($a, $b) { return strcmp($a['subdomain'], $b['subdomain']); } /** * Helper function for passing hook_domainload() by reference. * * @param $domain * The domain array defined by domain_lookup(). * @return * The $domain array, modified by reference by hook_domainload() implementations. */ function domain_api($domain) { static $_modules; if (!isset($_modules)) { $_modules = module_implements('domainload'); } if (!empty($_modules)) { foreach ($_modules as $module) { // Cannot use module_invoke_all() since these are passed by reference. $function = $module .'_domainload'; $function($domain); } } return $domain; } /** * Implement hook_domainload() * * Adds the home page 'path' and 'site_grant' boolean. */ function domain_domainload(&$domain) { // Get the path to the home page for this domain. $domain['path'] = domain_get_path($domain); // Grant access to all affiliates. $domain['site_grant'] = DOMAIN_SITE_GRANT; } /** * Determine an absolute path for a domain * * @param $domain * The currently active $domain array, provided by domain_lookup(). */ function domain_get_path($domain) { global $base_url; if (empty($base_url)) { return $domain['scheme'] .'://'. $domain['subdomain']; } $_url = parse_url($base_url); // PHP 5 does not return an empty path element. if (!isset($_url['path'])) { $_url['path'] = '/'; } // We need a trailing slash at the end of the path if (substr($_url['path'], -1) != '/') { $_url['path'] .= '/'; } $path = $domain['scheme'] .'://'. $domain['subdomain'] . $_url['path']; return $path; } /** * Determine an absolute path to the current page */ function domain_get_uri($domain) { $path = $domain['scheme'] .'://'. $domain['subdomain'] . request_uri(); return $path; } /** * Determine if we must switch the active domain. * * This function will execute a drupal_goto() to pop users to the correct * domain. * * @param $domain * The currently active $domain array, provided by domain_lookup(). */ function domain_goto($domain) { global $_domain; // We must be on the proper domain, see http://drupal.org/node/186153. if ($domain != -1 && $_domain['domain_id'] != $domain['domain_id']) { $path = domain_get_uri($domain); drupal_goto($path); } } /** * Implement hook_nodeapi(). * * This function is used to provide debugging information and to prep values from * the {domain_access} table when editing nodes. Since not all users can see the * domain access editing checkboxes, we pass some node_access values as hidden elements. */ function domain_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { switch ($op) { case 'prepare': case 'load': // Append the domain grants to the node for editing. $node->domains = array(); $node->editors = array(); $node->domain_site = FALSE; $result = db_query("SELECT gid, realm FROM {domain_access} WHERE nid = %d AND (realm = '%s' OR realm = '%s' OR realm = '%s')", $node->nid, 'domain_id', 'domain_site', 'domain_editor'); while ($data = db_fetch_object($result)) { // Transform the 0 to -1, since {domain_access} is unsigned. ($data->gid == 0) ? $gid = -1 : $gid = $data->gid; if ($data->realm == 'domain_id') { $node->domains[$gid] = $gid; if ($gid > 0) { $domain = domain_lookup($gid); $node->subdomains[] = $domain['sitename']; } else { $node->subdomains[] = variable_get('domain_sitename', variable_get('site_name', 'Drupal')); } } else if ($data->realm == 'domain_site') { $node->domain_site = TRUE; $node->subdomains[] = t('All affiliates'); } else if ($data->realm == 'domain_editor') { $node->domain_editor = TRUE; if ($gid > 0) { $domain = domain_lookup($gid); $node->editors[] = $domain['sitename']; } else { $node->editors[] = variable_get('domain_sitename', variable_get('site_name', 'Drupal')); } } } break; case 'view': // Search module casts both $a3 and $a4 as FALSE, not NULL. // We check that to hide this data from search and other nodeapi // calls that are neither a teaser nor a page view. if ($a3 !== FALSE || $a4 !== FALSE) { $output = ''; $debug = variable_get('domain_debug', 0); if ($debug && user_access('set domain access')) { if (!empty($node->subdomains)) { $output .= '

Subdomains

'; $node->content['subdomains'] = array('#value' => $output, '#weight' => 20); } if (!empty($node->editors)) { $output = '

Editors

'; $node->content['editors'] = array('#value' => $output, '#weight' => 21); } if (empty($output)) { $node->content['domain'] = array('#value' => t('This node is not assigned to a domain.'), '#weight' => 22); } } } break; case 'insert': case 'update': // Store these records in our own table as well. $grants = domain_node_access_records($node); _domain_write_records($node->nid, $grants); break; case 'delete': // Remove records from the {domain_access} table. db_query("DELETE FROM {domain_access} WHERE nid = %d", $node->nid); break; } } /** * Implement hook_node_grants. * * Informs the node access system what permissions the user has. By design * all users are in the realm defined by the currently active domain_id. */ function domain_node_grants($account, $op) { global $_domain; // Do we need to use complex rules? $rules = variable_get('domain_access_rules', FALSE); // By design, all users can see content sent to all affiliates, // but the $_domain['site_grant'] can be set to FALSE. if ($op == 'view') { if ($_domain['site_grant']) { $grants['domain_site'][] = 0; if ($rules) { $grants['domain_site']['group'] = 'domain'; } } // Grant based on active subdomain. $grants['domain_id'][] = $_domain['domain_id']; if ($rules) { $grants['domain_id']['group'] = 'domain'; } // In special cases, we grant the ability to view all nodes. That is, // we simply get out of the way of other node_access rules. // We do this with the universal 'domain_all' grant. if ($op == 'view' && domain_grant_all()) { // If no other node access modules are present, return our grant. // Otherwise, we just step out of the way. if ($rules) { return array(); } else { return array('domain_all' => array(0)); } } } else { // Special permissions for editors $editors = variable_get('domain_editors', DOMAIN_EDITOR_RULE); if ($editors && user_access('edit domain nodes', $account)) { if (!empty($account->domain_user)) { foreach ($account->domain_user as $id) { if (abs($id) > 0) { if ($id > 0) { $grants['domain_editor'][] = $id; } else { $grants['domain_editor'][] = 0; } // Advanced rules let us access check unpublished nodes for editing. if ($rules) { $grants['domain_editor']['check'] = TRUE; } } } } } } // Let Domain Access module extensions act to override the defaults. static $_modules; if (!isset($_modules)) { $_modules = module_implements('domaingrants'); } if (!empty($_modules)) { foreach ($_modules as $module) { // Cannot use module_invoke_all() since these are passed by reference. $function = $module .'_domaingrants'; $function($grants, $account, $op); } } return $grants; } /** * Implement hook_node_access_records() * * Set permissions for a node to be written to the database. By design * if no options are selected, the node is assigned to the main site. */ function domain_node_access_records($node) { if (domain_disabling()) { return; } // Define the $grants array. $grants = array(); // If the form is hidden, we are passed the 'domains_raw' variable. // We need to append unique values from this variable to the existing // stored values. See the logic for 'view domain publshing' in domain_form_alter(). if (is_array($node->domains_raw)) { if (!isset($node->domains)) { $node->domains = array(); } foreach ($node->domains_raw as $value) { // Only add this if it is not present already. if (!in_array($value, $node->domains)) { $node->domains[$value] = $value; } } } // If set, grant access to the core site, otherwise // The grant must be explicitly given to a domain. if ($node->domain_site) { $grants[] = array( 'realm' => 'domain_site', 'gid' => 0, 'grant_view' => TRUE, 'grant_update' => FALSE, 'grant_delete' => FALSE, 'priority' => 0, // If this value is > 0, then other grants will not be recorded ); } // Special permissions for editors, if activated. $editors = variable_get('domain_editors', DOMAIN_EDITOR_RULE); if (!empty($node->domains)) { foreach ($node->domains as $key => $value) { // We can't use a 0 value in an $options list, so convert -1 to 0. if (abs($value) > 0) { ($key == -1) ? $key = 0 : $key = $key; $grants[] = array( 'realm' => 'domain_id', 'gid' => $key, 'grant_view' => TRUE, 'grant_update' => FALSE, 'grant_delete' => FALSE, 'priority' => 0, ); if ($editors) { $grants[] = array( 'realm' => 'domain_editor', 'gid' => $key, 'grant_view' => FALSE, 'grant_update' => TRUE, 'grant_delete' => TRUE, 'priority' => 0, ); } } } } // At least one option must be present, and it is the default site // this prevents null values in the form. // If we are enabling the module for the first time, we set the // default domain of all existing nodes to the root domain. if (empty($grants)) { $grants[] = array( 'realm' => 'domain_id', 'gid' => 0, 'grant_view' => TRUE, 'grant_update' => FALSE, 'grant_delete' => FALSE, 'priority' => 0, ); if ($editors) { $grants[] = array( 'realm' => 'domain_editor', 'gid' => 0, 'grant_view' => FALSE, 'grant_update' => TRUE, 'grant_delete' => TRUE, 'priority' => 0, ); } } // Let Domain Access module extensions act to override the defaults. static $_modules; if (!isset($_modules)) { $_modules = module_implements('domainrecords'); } if (!empty($_modules)) { foreach ($_modules as $module) { // Cannot use module_invoke_all() since these are passed by reference. $function = $module .'_domainrecords'; $function($grants, $node); } } return $grants; } /** * Store node_access records in the {domain_access{} table. * * @param $nid * The node id being acted upon. * @param $grants * The grants passed by hook_node_access_records(). */ function _domain_write_records($nid, $grants = array()) { if ($nid > 0 && !empty($grants)) { db_query("DELETE FROM {domain_access} WHERE nid = %d", $nid); foreach ($grants as $grant) { db_query("INSERT INTO {domain_access} (nid, gid, realm) VALUES (%d, %d, '%s')", $nid, $grant['gid'], $grant['realm']); } } } /** * Upon enabling this module, store the default view grant * in the {node_access} table. * @see domain_grant_all() */ function domain_enable() { domain_enabling(TRUE); // Thanks to the new way that batch processing of node grants is handled, we have to // manually define our records if none are present. $count = db_result(db_query("SELECT COUNT(*) FROM {domain_access}")); if ($count == 0) { $rule = variable_get('domain_behavior', DOMAIN_INSTALL_RULE); $edit = variable_get('domain_editors', DOMAIN_EDITOR_RULE); $site = DOMAIN_SITE_GRANT; $nids = db_query("SELECT nid FROM {node}"); while ($nid = db_fetch_object($nids)) { if (!empty($site)) { db_query("INSERT INTO {domain_access} (nid, gid, realm) VALUES (%d, %d, '%s')", $nid->nid, 0, 'domain_site'); } if (!empty($rule)) { // By design, all nodes are assigned to the master domain. db_query("INSERT INTO {domain_access} (nid, gid, realm) VALUES (%d, %d, '%s')", $nid->nid, 0, 'domain_id'); // Editor rules only apply is nodes are assigned to a domain. if (!empty($edit)) { variable_set('domain_editors', TRUE); db_query("INSERT INTO {domain_access} (nid, gid, realm) VALUES (%d, %d, '%s')", $nid->nid, 0, 'domain_editor'); } } } } // Rebuild the node access table with our rules. node_access_rebuild(); // Set the default 'domain_all' grant for special pages. domain_set_default_grant(); } /** * Ensure that the 'domain_all' grant is present. */ function domain_set_default_grant() { $check = db_result(db_query("SELECT COUNT(nid) FROM {node_access} WHERE realm = 'domain_all' AND gid = 0")); if (!$check) { db_query("INSERT INTO {node_access} VALUES (0, 0, 'domain_all', 1, 0, 0)"); } } /** * Writes the default grants when the module is first enabled. */ function domain_enabling($set = NULL) { static $enabling = FALSE; if ($set !== NULL) { $enabling = $set; } return $enabling; } /** * Implement hook_disable() */ function domain_disable() { domain_disabling(TRUE); // Rebuild the node access table. Other modules have to fend for themselves here. node_access_rebuild(); // If {node_access} just contains the 'all' grant, then reset it to default. $result = db_result(db_query("SELECT COUNT(realm) FROM {node_access} WHERE realm <> 'all'")); if ($result == 0) { db_query("DELETE FROM {node_access}"); db_query("INSERT INTO {node_access} VALUES (0, 0, 'all', 1, 0, 0)"); } } /** * Simple function to make sure we don't respond with grants when disabling ourselves. */ function domain_disabling($set = NULL) { static $disabling = FALSE; if ($set !== NULL) { $disabling = $set; } return $disabling; } /** * Implement hook_form_alter() * * This function is crucial, as it appends our node access elements to the node edit form. * For users without the "set domain access" permission, this happens silently. */ function domain_form_alter($form_id, &$form) { // There are forms that we never want to alter, and they are passed here. $forms = module_invoke_all('domainignore'); if (in_array($form_id, $forms)) { return; } // Set a message if we are on an admin page. domain_warning_check($form_id); // If SEO is turned on, then form actions need to be absolute paths // to the currently active domain. See http://drupal.org/node/196217. $seo = variable_get('domain_seo', 0); if ($seo) { global $_domain; $action = parse_url($form['#action']); if ($action['query']) { $action['path'] .= '?'; } // We cannot reset this if it has already been set by another module. // See http://drupal.org/node/306551 if (empty($action['host'])) { $form['#action'] = check_url($_domain['scheme'] .'://'. $_domain['subdomain'] . $action['path'] . $action['query']); } } // Save our settings for the default domain. if ($form_id == 'system_site_information_settings' && arg(2) != 'domain') { $form['#submit']['domain_form_sitename_submit'] = array(); } // Apply to all node editing forms, but make sure we are not on the CCK field configuration form. if ($form['#id'] == 'node-form' && !$form['#node']->cck_dummy_node_form) { global $_domain, $user; // By default, the requesting domain is assigned. $default = array($_domain['domain_id']); // How is core content handled for this site? $behavior = variable_get('domain_behavior', DOMAIN_INSTALL_RULE); if ($_domain['domain_id'] == 0) { $default[] = -1; } // Some options will be passed as hidden values, we need to run some checks on those. if ($form['#node']->nid) { $raw = $form['#node']->domains; } else { $raw = $default; } $options = array(); foreach (domain_domains() as $data) { // Cannot pass zero in checkboxes. ($data['domain_id'] == 0) ? $key = -1 : $key = $data['domain_id']; // The domain must be valid. if ($data['valid'] || user_access('administer domains')) { $options[$key] = $data['sitename']; } } // If the user is a site admin, show the form, otherwise pass it silently. if (user_access('set domain access')) { $form['domain'] = array( '#type' => 'fieldset', '#title' => t('Domain access options'), '#collapsible' => TRUE, '#collapsed' => FALSE ); $form['domain']['domain_site'] = array( '#type' => 'checkbox', '#prefix' => t('

Publishing options:'), '#suffix' => '

', '#title' => t('Send to all affiliates'), '#required' => FALSE, '#description' => t('Select if this content can be shown to all affiliates. This setting will override the options below.'), '#default_value' => (isset($form['#node']->nid)) ? $form['#node']->domain_site : variable_get('domain_node_'. $form['#node']->type, $behavior), ); $form['domain']['domains'] = array( '#type' => 'checkboxes', '#title' => t('Publish to'), '#options' => $options, '#required' => TRUE, '#description' => t('Select which affiliates can access this content.'), '#default_value' => (isset($form['#node']->nid)) ? $form['#node']->domains : $default, ); } // If the user has limited permissions, show that form or obey the settings. else { if (user_access('view domain publishing')) { $action = variable_get('domain_options', 0); $user_domains = array(); $default_options = array(); if (!isset($user->domain_user)) { $user->domain_user = array(); } foreach ($user->domain_user as $key => $value) { if (abs($value) > 0) { $user_domains[] = $value; } } $first_domain = current($user_domains); $user_options = array(); foreach ($options as $key => $value) { if (in_array($key, $user_domains)) { $user_options[$key] = $value; } } // Raw data checks for published nodes. foreach ($raw as $key => $value) { if (in_array($value, $user_domains)) { $default_options[] = $value; } // This is only used in case 3, below. It means that some options // are present that the user cannot access but that must be preserved. else { $raw_options[] = $value; } } // Act on the behavior desired by the site admin. switch ($action) { // 1 == go to the default domain. case 1: $root = domain_default(); if ($root['domain_id'] != $_domain['domain_id']) { domain_goto($root); } break; // 2 == go to the user's assigned domain. case 2: $domain = domain_lookup($first_domain); // If the domain is invalid, go to the primary domain. if ($domain == -1 || $domain['valid'] == 0) { domain_goto(domain_default()); } else if ($domain['domain_id'] != $_domain['domain_id']) { domain_goto($domain); } break; // 3 == show checkboxes of available domains. case 3: // If the user has no available domains, then they cannot post. if (empty($user_options)) { $form = array(); return drupal_access_denied(); } $form['domain'] = array( '#type' => 'fieldset', '#title' => t('Affiliate publishing options'), '#collapsible' => TRUE, '#collapsed' => FALSE ); // We must preserve publishing options that the user cannot access, but only for // existing nodes. if ($form['#node']->nid) { $raw = $raw_options; } else { $raw = array(); } // If the raw options are being passed, then no input is technically required. (empty($raw)) ? $required = TRUE : $required = FALSE; $form['domain']['domains'] = array( '#type' => 'checkboxes', '#title' => t('Publish to'), '#options' => $user_options, '#required' => $required, '#description' => t('Select which affiliates can access this content.'), '#default_value' => (isset($form['#node']->nid)) ? $form['#node']->domains : $default_options, ); // Show the options that cannot be changed. $list = array(); if ($form['#node']->domain_site) { $list[]['data'] = t('All affiliates'); } if (!empty($raw)) { foreach ($raw as $did) { ($did == -1) ? $id = 0 : $id = $did; $raw_domains = domain_lookup($id); $list[]['data'] = $raw_domains['sitename']; } } if (!empty($list)) { $form['domain']['domains_notes'] = array( '#value' => ''. theme('item_list', $list) .'
'. t('This content has also been published to these affiliates.') .'
', ); } break; } } // These form elements are hidden from non-privileged users, by design. $form['domain_site'] = array( '#type' => 'value', '#value' => (isset($form['#node']->nid)) ? $form['#node']->domain_site : variable_get('domain_node_'. $form['#node']->type, $behavior), ); // Domains that have been assigned and cannot be changed. $form['domains_raw'] = array( '#type' => 'value', '#value' => $raw, ); } // THIS SECTION BREAKS CCK if we don't check for cck_dummy_node_form! See http://drupal.org/node/186624 // and note the !$form['#node']->cck_dummy_node_form in the IF check at the top of the function. // Some editors cannot administer nodes, so we have to add these form elements. if (variable_get('domain_editors', DOMAIN_EDITOR_RULE) == 1 && user_access('edit domain nodes')) { $access = variable_get('domain_form_elements', array('options', 'delete', 'comment_settings', 'path')); foreach ($access as $item) { $form[$item]['#access'] = TRUE; } } } } /** * Update the default domain's sitename. */ function domain_form_sitename_submit($form_id, $form_values) { variable_set('domain_sitename', $form_values['site_name']); drupal_set_message(t('Primary domain settings updated.')); } /** * Activate the hidden grant for searches. * * @param $reset * A boolean flag indicating whether to reset the static variable or not. * @return * TRUE or FALSE, depending on whether the grants should be executed for this page. */ function domain_grant_all($reset= FALSE) { static $grant; if (!isset($grant) || $reset) { $grant = FALSE; // Search is the easy case, so we check it first. if (variable_get('domain_search', 0) && arg(0) == 'search') { $grant = TRUE; } // On cron runs, we normally have to disable Domain Access. See http://drupal.org/node/197488. if (!$grant && variable_get('domain_cron_rule', 1)) { $ref = explode('/', request_uri()); $script = array_pop($ref); if ($script == 'cron.php') { $grant = TRUE; } } if (!$grant) { // We check the paths registered by the special pages setting. $pages = variable_get('domain_grant_all', "user/*/track"); $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\($|\|)/'), array('|', '.*', '\1'. preg_quote(variable_get('site_frontpage', 'node'), '/') .'\2'), preg_quote($pages, '/')) .')$/'; // Compare with the internal and path alias (if any). $page_match = preg_match($regexp, $_GET['q']); if (!$page_match) { $path = drupal_get_path_alias($_GET['q']); if ($path != $_GET['q']) { $page_match = preg_match($regexp, $path); } } if ($page_match) { $grant = TRUE; } } } return $grant; } /** * Implement hook_domaininstall() */ function domain_domaininstall() { // Check to see if the hook_url_alter() patch is installed. if (url('domain_access_test_path') != url('domain_access_path_test')) { drupal_set_message(t('The custom_url_rewrite_outbound() patch is not installed. Some features are not available. See the Patches to Drupal Core section of INSTALL.txt', array('!url' => base_path() . drupal_get_path('module', 'domain') .'/INSTALL.txt'))); } } /** * Implement hook_domainupdate() */ function domain_domainupdate($op, $domain = array(), $edit = array()) { switch ($op) { case 'delete': if ($domain != -1) { // Remove domain-specific entries from the {node_access} table and clear the cache. db_query("DELETE FROM {node_access} WHERE realm = 'domain_id' AND gid = %d", $domain['domain_id']); db_query("DELETE FROM {node_access} WHERE realm = 'domain_editor' AND gid = %d", $domain['domain_id']); db_query("DELETE FROM {domain_access} WHERE realm = 'domain_id' AND gid = %d", $domain['domain_id']); db_query("DELETE FROM {domain_access} WHERE realm = 'domain_editor' AND gid = %d", $domain['domain_id']); } break; } // In all cases, we need to force a menu rebuild, which also clears the cache. menu_rebuild(); } /** * Implement hook_domainbatch() */ function domain_domainbatch() { // Change all the domain names at once. $batch = array(); $batch['subdomain'] = array( '#form' => array( '#title' => t('Domains'), '#type' => 'textfield', '#size' => 40, '#maxlength' => 80, '#description' => t('Enter the host value of the domain. No http:// or slashes.'), '#required' => TRUE, ), '#domain_action' => 'domain', '#meta_description' => t('Edit all domain values.'), '#variable' => 'domain_root', '#validate' => 'domain_batch_validate', '#data_type' => 'string', '#weight' => -10, ); //Change all the sitenames at once. $batch['sitename'] = array( '#form' => array( '#title' => t('Site names'), '#type' => 'textfield', '#size' => 40, '#maxlength' => 80, '#description' => t('The site name to display for this domain.'), '#required' => TRUE, ), '#domain_action' => 'domain', '#meta_description' => t('Edit all domain site names.'), '#variable' => 'domain_sitename', '#validate' => 'domain_batch_validate', '#data_type' => 'string', '#weight' => -10, ); // Change all the schemes at once. $batch['scheme'] = array( '#form' => array( '#title' => t('URL schemes'), '#type' => 'radios', '#options' => array('http' => 'http://', 'https' => 'https://'), '#description' => t('The URL scheme for accessing this domain.') ), '#domain_action' => 'domain', '#meta_description' => t('Edit all domain URL schemes.'), '#system_default' => variable_get('domain_scheme', 'http://'), '#variable' => 'domain_scheme', '#data_type' => 'string', '#weight' => -10, ); // Change all the valid flags at once. $batch['valid'] = array( '#form' => array( '#title' => t('Valid domains'), '#type' => 'radios', '#options' => array(1 => t('Active'), 0 => t('Inactive')), '#description' => t('Allows users to access this domain.') ), '#domain_action' => 'domain', '#meta_description' => t('Edit all domain status flags.'), '#system_default' => 1, '#data_type' => 'integer', '#weight' => -10, ); return $batch; } /** * Validate handler for hook_domainbatch() */ function domain_batch_validate($form_values) { $case = $form_values['variable']; $batch = $form_values['domain_batch']; switch ($case) { case 'domain_root': foreach ($batch as $key => $value) { $subdomain = strtolower(urlencode($value)); $check = db_result(db_query("SELECT COUNT(domain_id) FROM {domain} WHERE subdomain = '%s' AND domain_id <> %d", $value, $key)); if ($check > 0 || ($key > 0 && $value == variable_get('domain_root', ''))) { form_set_error('domain_batch', t('Each domain value must be unique.')); } } break; case 'domain_sitename': foreach ($batch as $key => $value) { $check = db_result(db_query("SELECT COUNT(domain_id) FROM {domain} WHERE sitename = '%s' AND domain_id <> %d", $value, $key)); if ($check > 0 || ($key > 0 && $value == variable_get('domain_sitename', 'Drupal'))) { form_set_error('domain_batch', t('Each site name value must be unique.')); } } break; } # exit; } /** * Implement hook_simpletest() */ function domain_simpletest() { $module_name = 'domain'; $dir = drupal_get_path('module', $module_name) .'/tests'; $tests = file_scan_directory($dir, '\.test$'); return array_keys($tests); } /** * Sets a message to the site admin. * * If our module changes $conf settings, they may be reflected * on admin pages when we don't want them to be. */ function domain_warning_check($form_id) { static $_warning; // If $_POST is set, we are submitting the form and should not set a message. if (!$_POST && empty($_warning)) { global $_domain; // Add the list of forms $forms = array(); $forms = module_invoke_all('domainwarnings'); if (arg(2) != 'domain' && in_array($form_id, $forms)) { $default = domain_default(); if ($_domain['domain_id'] != $default['domain_id']) { $_path = domain_get_uri($default); drupal_set_message(t('You are viewing #this. This form may need to be entered from !domain', array('#this' => $_domain['subdomain'], '!url' => $_path, '!domain' => $default['subdomain']))); } } $_warning = TRUE; } } /** * Implement hook_db_rewrite_sql(). * * If enabled, force admins to use Domain Access rules. */ function domain_db_rewrite_sql($query, $primary_table, $primary_field, $args) { global $_domain; $admin_force = variable_get('domain_force_admin', FALSE); // In any of the following cases, do not enforce any rules. if (!$admin_force || $primary_field != 'nid' || !user_access('administer nodes') || domain_grant_all()) { return; } $domain_id = (int) $_domain['domain_id']; $return = array( 'join' => "INNER JOIN {domain_access} da_admin ON $primary_table.nid = da_admin.nid", 'where' => "(da_admin.gid = 0 AND da_admin.realm = 'domain_site') OR (da_admin.gid = $domain_id AND da_admin.realm = 'domain_id')", ); return $return; } /** * Implement hook_node_access_explain for devel.module */ function domain_node_access_explain($row) { global $_domain; $active = $_domain['subdomain']; $domain = domain_lookup($row->gid); $return = t('Domain Access -- '); switch ($row->realm) { case 'domain_all': if (domain_grant_all() == TRUE) { $return .= t('True: Allows content from all domains to be shown.'); } else { $return .= t('False: Only allows content from the active domain (%domain) or from all affiliates.', array('%domain' => $active)); } break; case 'domain_site': $return .= t('Viewable on all affiliate sites.'); break; case 'domain_id': $return .= t('Viewable on %domain.', array('%domain' => $domain['subdomain'])); break; case 'domain_editor': $return .= t('Editable by %domain editors.', array('%domain' => $domain['subdomain'])); break; default: // This is not our grant, do not return anything. $return = NULL; break; } return $return; } /** * Validates a domain string. * @param string $subdomain * The string to check for domain validity * @return array * List of error messages or empty array. */ function domain_validate($subdomain) { $error_list = array(); // Validate the domains format generically for now. $error = domain_valid_domain($subdomain); if (!empty($error)) { $error_list[] = $error; } // Make sure domain is unique if (!domain_unique_domain($subdomain)) { $error_list[] = t('The domain value must be unique.'); } return $error_list; } /** * Validate the domain against all correctable errors. * * Note that we decided not to check for valid TLDs here. * * @param $subdomain * Domain string to check. * @return string * Empty if valid, error message on invalid. */ function domain_valid_domain($subdomain) { $error_list = array(); // Check for one colon only. if (substr_count($subdomain, ':') > 1) { $error_list[] = t('Only one colon (:) is allowed.'); } // If a colon, make sure it is only followed by numbers. else if (substr_count($subdomain, ':') == 1) { $parts = explode(':', $subdomain); $port = (int) $parts[1]; if (strcmp($port, $parts[1])) { $error_list[] = t('The port protocol must be an integer.'); } } // The domain cannot begin or end with a period. if (substr($subdomain, 0, 1) == '.') { $error_list[] = t('The domain must not begin with a dot (.)'); } // The domain cannot begin or end with a period. if (substr($subdomain, -1) == '.') { $error_list[] = t('The domain must not end with a dot (.)'); } // Check for valid characters $pattern = '/^[a-z0-9\.\-:]*$/i'; if (!preg_match($pattern, $subdomain)) { $error_list[] = t('Only alphanumeric characters, dashes, and a colon are allowed.'); } if (!empty($error_list)) { return t('The domain string is invalid:') . theme('item_list', $error_list); } } /** * Validate the domain against existing domains. * * @param $subdomain * Domain string to check * @return bool * TRUE if unique; FALSE if duplicate. */ function domain_unique_domain($subdomain) { $count = db_result(db_query("SELECT COUNT(domain_id) FROM {domain} WHERE subdomain = '%s'", $subdomain)); return !(bool) $count; }