'subdomain_check_server', 'access callback' => TRUE, 'file' => 'subdomain_debug.inc', 'type' => MENU_CALLBACK, ); $items['~subdomain-test/subdomain/test'] = array( 'page callback' => 'subdomain_check_server', 'access callback' => TRUE, 'file' => 'subdomain_debug.inc', 'type' => MENU_CALLBACK, ); $items['admin/build/path/subdomain'] = array( 'title' => t('Subdomain settings'), 'description' => t('Configure automated subdomain path alias settings.'), 'page callback' => 'drupal_get_form', 'page arguments' => array('subdomain_admin_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_LOCAL_TASK, 'weight' => 11, 'file' => 'subdomain_debug.inc', ); return $items; } function subdomain_type() { return variable_get('subdomain_type', SUBDOMAIN_USER); } function subdomain_form_alter(&$form, $form_state, $form_id) { if ($form_id == 'taxonomy_form_term') { $form['#validate'][] = 'subdomain_taxonomy_form_validate'; } // Temporary Hack for compatability with ajax_comments module // TODO: revisit with module redesign elseif ($form_id == 'comment_form' && $form['submit']['#id'] == 'ajax-comments-submit' && subdomain_current_subdomain()) { $form['#action'] = str_replace(parse_url($form['#action'], PHP_URL_HOST), $_SERVER['HTTP_HOST'], $form['#action']); } } function subdomain_taxonomy_form_validate($form, &$form_state) { if (subdomain_type() == SUBDOMAIN_VOCAB && variable_get('subdomain_vocab', 0) == $form_state['values']['vid'] && subdomain_reserved(trim($form_state['values']['name']))) { form_set_error('name', t('@term is a reserved keyword and cannot be used as a term name', array('@term' => $form_state['values']['name']))); } } /** * Implementation of hook_nodeapi() */ function subdomain_nodeapi(&$node, $op, $teaser, $page) { switch ($op) { case 'validate': if (subdomain_type() == SUBDOMAIN_GROUP) { $type = node_get_types('type',$node); $type_name = strtolower($type->name); $type_title = strtolower($type->title_label); if (module_exists('og') && og_is_group_type($node->type)) { $node->title = trim($node->title); // Allow only letters, numbers, and spaces if (variable_get('subdomain_allow_onlyalpha', FALSE) && !preg_match('!^[a-zA-Z0-9 ]+$!', $node->title)) { form_set_error('title', t("The @type_name's @type_title can contain only letters, numbers and spaces", array('@type_name'=>$type_name, '@type_title'=> $type_title))); } // Disallow reserved subdomains elseif (subdomain_reserved($node->title)) { form_set_error('title', t("Sorry. '@node_title' is not available for use. Please enter a different @type_title for your @type_name.", array('@node_title' => $node->title, '@type_name'=>$type_name, '@type_title'=> $type_title))); } // Ensure group name is unique elseif (db_result(db_query("SELECT 1 FROM {node} WHERE nid <> %d AND type = '%s' AND title = '%s'", $node->nid, $node->type, $node->title))) { form_set_error('title', t("There is already a @type_name with that @type_title. Please enter a different @type_name", array('@type_name'=>$type_name, '@type_title'=> $type_title))); } // Disallow changes to group name (to avoid SEO issues and path alias getting out of sync with group content aliases) if ($node->nid) { $currentgroupname = db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $node->nid)); if (strtolower($currentgroupname) != strtolower($node->title)) { if (variable_get('subdomain_allowchange', SUBDOMAIN_ALLOWCHANGE_NO) == SUBDOMAIN_ALLOWCHANGE_NO || (variable_get('subdomain_allowchange', SUBDOMAIN_ALLOWCHANGE_NO) == SUBDOMAIN_ALLOWCHANGE_ADMINONLY && !user_access('administer site configuration'))) { form_set_error('title', t("Sorry, @type_name @type_titles are permanent and may not be changed. You can change capitalization only.", array('@type_name'=>$type_name, '@type_title'=> $type_title))); } } } } } // Disallow node titles that start with the subdomain token prefix character (else they'll be inadvertantly rewritten as subdomains) if (substr(trim($node->title), 0, strlen(SUBDOMAIN_TOKEN_PREFIX)) == SUBDOMAIN_TOKEN_PREFIX) { form_set_error('title', t("Sorry, @type_titles can't start with a @token_prefix.", array('@type_title'=> $type_title, '@token_prefix' => SUBDOMAIN_TOKEN_PREFIX))); } break; case 'load': if (subdomain_type() == SUBDOMAIN_GROUP && module_exists('og') && og_is_group_type($node->type)) { $node->subdomain = subdomain_raw_to_subdomain($node->title); } break; } } /** * Implementation of hook_user */ function subdomain_user($op, &$edit, &$user_edit, $category = NULL) { if ($op == 'validate' && subdomain_type() == SUBDOMAIN_USER && $category == 'account') { // If configured, disallow username changes if subdomains are set to Node Authors if (is_numeric($user_edit->uid) && $user_edit->name != $edit['name']) { if (variable_get('subdomain_allowchange', SUBDOMAIN_ALLOWCHANGE_NO) == SUBDOMAIN_ALLOWCHANGE_NO || (variable_get('subdomain_allowchange', SUBDOMAIN_ALLOWCHANGE_NO) == SUBDOMAIN_ALLOWCHANGE_ADMINONLY && !user_access('administer site configuration'))) { form_set_error('name', t('Sorry. Usernames are permanent and cannot be changed')); } } // If configured, allow only letters and spaces in usernames if (variable_get('subdomain_allow_onlyalpha', FALSE) && !preg_match('!^[a-zA-Z0-9 ]+$!', $edit['name'])) { form_set_error('name', t('User Names can contain only letters, numbers and spaces')); } elseif (subdomain_reserved($edit['name'])) { form_set_error('name', t('%name is a reserved word and cannot be used as a username', array('%name' => $edit['name']))); } } } /** * Implementation of hook_token_list() */ function subdomain_token_list($type = 'all') { if ($type == 'node') { $tokens['node']['subdomain'] = t('Subdomain token (place at the start of the path)'); } if ($type == 'taxonomy') { $tokens['taxonomy']['subdomain'] = t('Subdomain token (place at the start of the path)'); } elseif ($type == 'user') { $tokens['user']['subdomain'] = t('Subdomain token (place at the start of the path)'); } return $tokens; } /** * Implementation of hook_token_values() */ function subdomain_token_values($type, $object = NULL) { $values['subdomain'] = NULL; switch ($type) { case 'node': switch (subdomain_type()) { case SUBDOMAIN_GROUP: if (module_exists('og') && og_is_group_type($object->type)) { $values['subdomain'] = subdomain_build_token($object->title, $object->nid); } elseif (isset($object->og_groups)) { // 1st Group in og_groups array is used for subdomain if ($groupname = db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $object->og_groups[0]))) { $values['subdomain'] = subdomain_build_token($groupname, $object->og_groups[0]); }; } else { $values['subdomain'] = ''; } break; case SUBDOMAIN_USER: if (!empty($object->name)) { $values['subdomain'] = subdomain_build_token($object->name, $object->uid); } break; case SUBDOMAIN_TYPE: $values['subdomain'] = subdomain_build_token($object->type); break; case SUBDOMAIN_VOCAB: if (is_array($object->taxonomy)) { foreach ($object->taxonomy as $tid) { $term_name = db_result(db_query("SELECT name FROM {term_data} WHERE tid = %d AND vid = %d", $tid, variable_get('subdomain_vocab', 0))); if ($term_name && !subdomain_reserved($term_name)) { $values['subdomain'] = subdomain_build_token($term_name, $tid); break; } } } break; } break; case 'taxonomy': if (subdomain_type() == SUBDOMAIN_VOCAB && $object->vid == variable_get('subdomain_vocab', 0)) { $values['subdomain'] = subdomain_build_token($object->name, $object->tid); } break; case 'user': if (subdomain_type() == SUBDOMAIN_USER) { $values['subdomain'] = subdomain_build_token($object->name, $object->uid); } } return $values; } /** * Implementation of hook_admin_settings() */ function subdomain_admin_settings() { $check_requirements_result = subdomain_check_requirements(); drupal_add_js(drupal_get_path('module', 'subdomain') . '/subdomain.js'); $reserved_default = implode("\n", array('www', 'static')); $options[SUBDOMAIN_USER] = t('Node authors and author content'); $options[SUBDOMAIN_TYPE] = t('Content types'); if (module_exists('taxonomy')) { $vocabs = taxonomy_get_vocabularies(); if (is_array($vocabs) && count($vocabs)) { foreach ($vocabs as $vocab) { $vocab_options[$vocab->vid] = $vocab->name; } if (is_array($vocab_options)) { $options[SUBDOMAIN_VOCAB] = t('Taxonomy Vocabulary and associated content'); } } } if (module_exists('og')) { $options[SUBDOMAIN_GROUP] = t('Organic groups and group content'); } $form['subdomain_settings']['requirements'] = array( '#type' => 'fieldset', '#title' => 'Subdomain Prerequisites', '#description' => t('To enable subdomains on your website, you must first configure the following 5 items. If something isn\'t working, refer to the suggested solutions and make sure you have completed each step outlined in readme.txt.'), ); $form['subdomain_settings']['requirements']['body'] = array( '#type' => 'markup', '#value' => $check_requirements_result, ); $form['subdomain_settings']['subdomain_type'] = array( '#type' => 'select', '#title' => t('Create subdomains for'), '#description' => !isset($options[SUBDOMAIN_GROUP]) || !isset($options[SUBDOMAIN_VOCAB]) ? t('To use Taxonomy or Organic Group mode, enable and configure those modules') : '', '#options' => $options, '#default_value' => subdomain_type() ); if (module_exists('taxonomy') && is_array($vocab_options)) { $form['subdomain_settings']['subdomain_vocab'] = array( '#type' => 'select', '#title' => t('With the selected vocabulary\'s terms'), '#options' => $vocab_options, '#default_value' => variable_get('subdomain_vocab', NULL), '#prefix' => '' ); } $form['subdomain_settings']['subdomain_style'] = array( '#type' => 'select', '#title' => t('Using'), '#options' => array(SUBDOMAIN_STYLE_NAME => t('Group or Node Author name'), SUBDOMAIN_STYLE_CUSTOM => t('Custom...')), '#default_value' => variable_get('subdomain_style', SUBDOMAIN_STYLE_NAME) ); $form['subdomain_settings']['subdomain_custom'] = array( '#type' => 'textfield', '#title' => t('Custom subdomain'), '#description' => t('You may use the tokens %name% and %id% for group-name/user-name and group-nid/user-uid respectively'), '#default_value' => variable_get('subdomain_custom', NULL), '#prefix' => '
', '#suffix' => '
' ); $form['subdomain_settings']['subdomain_reserved'] = array( '#type' => 'textarea', '#title' => t('But disallow the following subdomains'), '#rows' => 10, '#description' => t('Enter 1 subdomain per line'), '#default_value' => variable_get('subdomain_reserved', $reserved_default) ); $form['subdomain_settings']['advanced']['subdomain_allow_onlyalpha'] = array( '#type' => 'checkbox', '#title' => t('Restrict group names to letters and numbers'), '#default_value' => variable_get('subdomain_allow_onlyalpha', FALSE), '#description' => t('Restricting names to letters and numbers ensures the subdomain is the same as the group name, since anything else will be stripped from the URL.'), ); $form['subdomain_settings']['advanced']['subdomain_allowchange'] = array( '#type' => 'select', '#title' => t('Allow changes to group names?'), '#options' => array(SUBDOMAIN_ALLOWCHANGE_NO => 'No', SUBDOMAIN_ALLOWCHANGE_YES => 'Yes', SUBDOMAIN_ALLOWCHANGE_ADMINONLY => 'Site administrators only'), '#default_value' => variable_get('subdomain_allowchange', SUBDOMAIN_ALLOWCHANGE_YES), '#description' => t("Disallow changes to ensure that the links/URLs to content on your subdomains doesn't change. This will help prevent links from external sites and search engines breaking."), ); $form['subdomain_settings']['subdomain_prepend_www'] = array( '#type' => 'checkbox', '#title' => t('Prepend all non-subdomain URLs with www'), '#default_value' => variable_get('subdomain_prepend_www', FALSE), '#description' => t('Useful if your SSL certificate is for www.example.com'), ); $form['subdomain_settings']['subdomain_next'] = array( '#type' => 'markup', '#value' => t('

After saving, configure automated alias settings and place the [subdomain] token at the start of any paths you want on a subdomain

') ); $form['subdomain_settings']['expert'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t('Advanced Settings'), ); $form['subdomain_settings']['expert']['subdomain_ajax_paths'] = array( '#type' => 'textarea', '#title' => t('Ajax callback paths that may appear on subdomain content'), '#description' => t('Enter each path on a separate line. Ajax callbacks on subdomain content will break unless their paths are specified here'), '#default_value' => variable_get('subdomain_ajax_paths', ''), ); return system_settings_form($form); } function subdomain_admin_settings_validate($form, &$form_state) { if ($form_state['values']['subdomain_style'] == SUBDOMAIN_STYLE_CUSTOM) { $custom = trim($form_state['values']['subdomain_custom']); if (empty($custom)) { form_set_error('subdomain_custom', t('You forgot to specify a custom subdomain rewrite')); } else { // get valid tokens out of the way $custom = str_replace(SUBDOMAIN_CUSTOM_TOKEN_NAME, '', $custom); $custom = str_replace(SUBDOMAIN_CUSTOM_TOKEN_ID, '', $custom); // Only allow letters and spaces in custom rewrite if (!preg_match('!^[a-zA-Z ]+$!', $custom)) { form_set_error('subdomain_custom', t('Apart from the @token_name and @token_id tokens, the custom rewrite can only contain letters and spaces', array('@token_name' => SUBDOMAIN_CUSTOM_TOKEN_NAME, '@token_id' => SUBDOMAIN_CUSTOM_TOKEN_ID))); } } } elseif (!empty($form_state['values']['subdomain_custom'])) { // Erase custom subdomain token, if "custom" isn't the selected subdomain style form_set_value($form['subdomain_settings']['subdomain_custom'], NULL, $form_state); } if ($form_state['values']['subdomain_type'] != SUBDOMAIN_VOCAB && isset($form['subdomain_settings']['subdomain_vocab'])) { // Erase selected vocab if taxonomy vocab isn't the selected subdomain type form_set_value($form['subdomain_settings']['subdomain_vocab'], NULL, $form_state); } } /** * Returns True if $subdomain is reserved */ function subdomain_reserved($name = NULL) { $subdomain = subdomain_raw_to_subdomain($name); $reserved_subdomain = strtolower(ereg_replace(chr(13) . chr(10), "\n", variable_get('subdomain_reserved', ''))); if (in_array($subdomain, explode("\n", $reserved_subdomain))) { return TRUE; } } function subdomain_base_domain() { static $base_domain; if (empty($base_domain)) { global $cookie_domain; $base_domain = ltrim($cookie_domain, '.'); } return $base_domain; } /** * If host includes subdomain, rewrite URI and lookup internal path */ function subdomain_url_inbound_alter(&$result, $path, $path_language) { global $cookie_domain; static $executed = FALSE; // Only execute if: // - Subdomain is present in HTTP_HOST // - 1st call to this function or Subdomain module is performing internal test // - Not an AJAX callback if ((!$executed || $path == 'subdomain/test') && strlen($_SERVER['HTTP_HOST']) > strlen($cookie_domain) && !subdomain_ajax_path($path)) { $subdomain_length = strpos($_SERVER['HTTP_HOST'], $cookie_domain); $subdomain = substr($_SERVER['HTTP_HOST'], 0, $subdomain_length); // Exit if subdomain is reserved and url shouldn't be touched if (subdomain_reserved($subdomain)) { $executed = true; return; } // Rewrite $GLOBAL['base_url'] without the subdomain so that we don't interfere // with other modules like imagecache (substr removes leading period) $GLOBALS['base_url'] = 'http://'. substr($cookie_domain,1); if ($path == variable_get('site_frontpage', 'node') && ltrim(request_uri(),'/') != ltrim($path, '/')) { $alias_path = ''; } else { $alias_path = $path; } $alias = "~". $subdomain . ($alias_path ? "/$alias_path" : ""); // Update $_REQUEST so we don't send global redirect into an eternal loop $_REQUEST['q'] = $alias; if ($internal_path = drupal_lookup_path('source', $alias, $path_language)) { $result = $internal_path; } else { $result = $alias; } } $executed = TRUE; } /** * Looks for subdomain token in Drupal generated URLs; if present, prepends to domain */ function subdomain_url_outbound_alter(&$path, &$options) { global $cookie_domain; $options['absolute'] = TRUE; if (substr($path, 0, SUBDOMAIN_TOKEN_PREFIX_LENGTH) == SUBDOMAIN_TOKEN_PREFIX) { $path_parts = explode('/', substr($path, SUBDOMAIN_TOKEN_PREFIX_LENGTH)); $options['base_url'] = _subdomain_get_protocol() . array_shift($path_parts) .'.'. subdomain_base_domain(); $path = implode('/', $path_parts); } else { // Prepend www to non subdomain URLs if necessary $www = variable_get('subdomain_prepend_www', FALSE) ? 'www.' : ''; $options['base_url'] = _subdomain_get_protocol() . $www . subdomain_base_domain(); } // Handle AJAX paths: If we're currently on a subdomain, preserve it… if (strlen($_SERVER['HTTP_HOST']) > strlen($cookie_domain) && subdomain_ajax_path($path)) { $options['base_url'] = _subdomain_get_protocol() . $_SERVER['HTTP_HOST']; } } /** * Returns current subdomain */ function subdomain_current_subdomain() { static $subdomain; if (!isset($subdomain)) { global $cookie_domain; $cd = array_shift(explode('.', trim($cookie_domain, '.'))); $sd = array_shift(explode('.', $_SERVER['HTTP_HOST'])); $subdomain = $sd == $cd || $sd == 'www' ? '' : $sd; } return $subdomain; } function subdomain_ajax_path($path) { static $ajax_paths; if (!isset($ajax_paths)) { $ajax_paths = explode("\n", strtolower(ereg_replace(chr(13) . chr(10), "\n", variable_get('subdomain_ajax_paths', '')))); } foreach($ajax_paths as $ajax_path) { if ($ajax_path && strpos($path, $ajax_path) === 0) { return TRUE; } } return FALSE; } function subdomain_raw_to_subdomain($groupname) { return str_replace(' ', '-', trim(strtolower($groupname))); } function subdomain_subdomain_to_token($subdomain) { return SUBDOMAIN_TOKEN_PREFIX . $subdomain; } function subdomain_build_token($name, $id = '') { if (variable_get('subdomain_style', SUBDOMAIN_STYLE_NAME) == SUBDOMAIN_STYLE_NAME) { $raw = $name; } elseif ($custom = variable_get('subdomain_custom', NULL)) { $raw = str_replace(SUBDOMAIN_CUSTOM_TOKEN_NAME, $name, $custom); $raw = str_replace(SUBDOMAIN_CUSTOM_TOKEN_ID, $id, $raw); } return subdomain_subdomain_to_token(subdomain_raw_to_subdomain($raw)); } /** * Checks whether protocol is https or http * Based on code from http://php.net/manual/en/reserved.variables.server.php#89306 */ function _subdomain_get_protocol(){ if($_SERVER['https'] == 1) /* Apache */ { return 'https://'; } elseif ($_SERVER['https'] == 'on') /* IIS */ { return 'https://'; } elseif ($_SERVER['SERVER_PORT'] == 443) /* others */ { return 'https://'; } else { return 'http://'; } }