'Aggregator cron run',
'page callback' => 'aggregator_not_cron',
'access callback' => 'drupal_is_cli',
'type' => MENU_CALLBACK,
);
// Override this special case to prevent manually creating
// project issue nodes for the cvsapplications project.
$items['node/add/project-issue/cvsapplications'] = array(
'title' => '',
'page callback' => '_drupalorg_node_add_project_issue_cvsapplications',
'access callback' => 'node_access',
'access arguments' => array('create', 'project_issue'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implementation of hook_help().
*/
function drupalorg_help($path, $arg) {
switch ($path) {
case 'project/usage':
case 'project/usage/%':
return '
'. t('These statistics are incomplete; only Drupal websites using the Update Status module are included in the data. As this module is now included with the download of Drupal since version 6.x, the data is heavily biased toward newer sites. Read more information about how these statistics are calculated.', array('!update_status_url' => url('project/update_status'), '!usage_description_url' => url('node/329620'))) .'
';
}
if ($arg[0] == 'project' && $arg[1] == 'issues-term' && $arg[2] == '346') {
return ''. t('A more detailed overview of the issues can be seen in the Community Initiatives handbook section titled Upgrade Drupal.org from Drupal 5 to 6.') .'
';
}
}
/**
* Implementation of hook_cron().
*/
function drupalorg_cron() {
drupalorg_issue_counts();
}
// == Issue counter ============================================================
/**
* Pulls issue counts for various issue queues on drupal.org.
*
* Summarize issue counts for different types of issues for ISSUE_RIDS releases.
*/
function drupalorg_issue_counts() {
$issue_counts['Pending bugs'] = db_result(db_query("SELECT COUNT(*) FROM {project_issues} pi INNER JOIN {node} n ON pi.nid = n.nid WHERE n.status = 1 AND pid = 3060 AND category = 'bug' AND sid = 1 AND priority IN (1,2,4) AND rid IN (". ISSUE_RIDS .")"));
$issue_counts['Critical issues'] = db_result(db_query("SELECT COUNT(*) FROM {project_issues} pi INNER JOIN {node} n ON pi.nid = n.nid WHERE n.status = 1 AND pid = 3060 AND category IN ('bug', 'task') AND sid IN (1,8,13,14) AND priority = 1 AND rid IN (". ISSUE_RIDS .")"));
$issue_counts['Patch queue'] = db_result(db_query("SELECT COUNT(*) FROM {project_issues} pi INNER JOIN {node} n ON pi.nid = n.nid WHERE n.status = 1 AND pid = 3060 AND sid IN (8,13,14) AND rid IN (". ISSUE_RIDS .")"));
$issue_counts['Patches to review'] = db_result(db_query("SELECT COUNT(*) FROM {project_issues} pi INNER JOIN {node} n ON pi.nid = n.nid WHERE n.status = 1 AND pid = 3060 AND sid IN (8) AND rid IN (". ISSUE_RIDS .")"));
variable_set('drupalorg_issue_counts', $issue_counts);
}
// == Altering of various forms ================================================
/**
* Implementation of hook_form_alter().
*/
function drupalorg_form_alter(&$form, $form_state, $form_id) {
// List of forms to check for overrides, and the corresponding permissions.
$override_forms = array(
'book_node_form' => 'revert revisions',
'forum_node_form' => 'administer nodes',
'page_node_form' => 'administer nodes',
'simplenews_node_form' => 'create newsletter',
'story_node_form' => 'administer nodes',
);
// Override the access for attachments if it's a forbidden form,
// and the user does not have sufficient permissions.
if (in_array($form_id, array_keys($override_forms)) && !user_access($override_forms[$form_id])) {
if (isset($form['attachments']['#access'])) {
$form['attachments']['#access'] = FALSE;
}
}
// Add a description for the Priority and Status values.
if ($form_id == 'project_issue_node_form' || $form_id == 'comment_form' && !empty($form['original_issue']['issue_info'])) {
$priority_status_description = array(
// The "standard" class adds a clear so the description is positioned
// below the floated pull-downs. "fieldset-description" is from
// bluebeach to make the font size smaller.
'#prefix' => '',
'#value' => t('Descriptions of the
Priority and
Status values can be found in the
Issue queue handbook.', array('!priority_url' => '/node/45111', '!status_url' => '/node/156119', '!issue_queue_url' => '/node/317')),
'#suffix' => '
',
);
}
if ($form_id == 'project_issue_node_form' ||
($form_id == 'comment_form' && !empty($form['original_issue']['issue_info']))) {
// We want this fieldset at the very bottom, just above the buttons, but
// we need different weights for that depending on if it's a new issue or
// a follow-up comment.
if ($form_id == 'project_issue_node_form') {
$form['taxonomy']['#weight'] = 35;
$form['issue_info']['description'] = $priority_status_description;
}
else {
$form['taxonomy']['#weight'] = 4;
$form['original_issue']['issue_info']['description'] = $priority_status_description;
}
// Prefill values from $_GET.
if (isset($_GET['version'])) {
$form['project_info']['rid']['#default_value'] = $_GET['version'];
}
if (isset($_GET['component'])) {
$form['project_info']['component']['#default_value'] = $_GET['component'];
}
if (isset($_GET['categories'])) {
$form['issue_info']['category']['#default_value'] = $_GET['categories'];
}
if (isset($_GET['priorities'])) {
$form['issue_info']['priority']['#default_value'] = $_GET['priorities'];
}
if (isset($_GET['assigned'])) {
$form['issue_info']['assigned']['#default_value'] = $_GET['assigned'];
}
if (isset($_GET['status'])) {
$form['issue_info']['sid']['#default_value'] = $_GET['status'];
}
if (isset($_GET['title'])) {
$form['issue_details']['title']['#default_value'] = $_GET['title'];
}
if (isset($_GET['body'])) {
$form['issue_details']['body']['#default_value'] = $_GET['body'];
}
if (isset($_GET['tags'])) {
$form['taxonomy']['#collapsed'] = FALSE;
$form['taxonomy']['tags'][reset(array_keys($form['taxonomy']['tags']))]['#default_value'] = $_GET['tags'];
}
}
// Add security update related checks to release forms.
if ($form_id == 'project_release_node_form') {
drupalorg_project_release_node_form_alter($form, $form_state);
}
// Add CVS maintainer newsletter signup to users when getting a CVS account.
if ($form_id == 'cvs_user_edit_form') {
$form['#submit'][] = 'drupalorg_cvs_user_edit_submit';
}
// Deprecate the "license" field, as it must always be GPLv2+ anyway.
if ($form_id == 'project_project_node_form') {
if (! $form['project']['license']['#default_value']) {
$form['project']['license'] = array(
'#type' => 'item',
'#title' => $form['project']['license']['#title'],
'#value' => t('@link_name', array(
'@link_url' => CVS_LICENSE_LINK,
'@link_name' => CVS_LICENSE,
)),
);
}
}
// Core search index is not used, so clear off wipe option.
if ($form_id == 'search_admin_settings') {
unset($form['status']['wipe']);
}
// Ensure that wildcard email addresses are not abused.
if ($form_id == 'user_register') {
$form['#validate'][] = 'drupalorg_register_mail_validate';
}
// Add home page option to user access rule adding and editing.
// Make sure if we edit a homepage option, we keep using that as a default.
if ($form_id == 'user_admin_access_add_form' || $form_id == 'user_admin_access_edit_form') {
if ($form['#parameters'][2]['type'] == 'homepage') {
$form['type']['#default_value'] = 'homepage';
}
$form['type']['#options']['homepage'] = t('Homepage');
}
// Ensure nice Drupal home page addresses
if ($form_id == 'user_profile_form') {
$form['#validate'][] = 'drupalorg_profile_user_edit_validate';
// Hack to make the language list a multiselect field (there is no UI
// for this in profile module). We need to hack around that profile only
// ever stores select field values as strings, so we need to explode what
// was in there for our multiselect form.
if (isset($form['Personal information']['profile_languages'])) {
$form['Personal information']['profile_languages']['#multiple'] = TRUE;
$form['Personal information']['profile_languages']['#default_value'] = explode('; ', $form['Personal information']['profile_languages']['#default_value']);
$form['#submit'] = array_merge(array('drupalorg_profile_fix_languages'), $form['#submit']);
}
}
// Book node form modifications.
if ($form_id == 'book_node_form') {
// Force a revision log entry when editing existing book nodes.
if (isset($form['revision_information']['log']) && arg(1) != 'add') {
$form['revision_information']['log']['#required'] = TRUE;
$form['revision_information']['#collapsed'] = FALSE;
}
// Remove the IA vocabs from handbooks other than the Theming Guide.
if ($form['#node']->book['bid'] != 338057) {
$form['taxonomy'][40]['#access'] = FALSE;
$form['taxonomy']['tags'][42]['#access'] = FALSE;
}
else {
// Add a note for the IA stuff.
$form['taxonomy']['#description'] = t('The Theming guide has some additional vocabularies not found on other handbook pages,information type and subject matter, which are being used to assist our IA project. For more information on how to properly use these two vocabularies, please refer to the !guidelines.', array('!guidelines' => l('guidelines', 'node/548322')));
}
}
// Remove the news vocabulary on forum forms, if the user is not an admin.
// Used to mark news forum topics with tags.
if ($form_id == 'forum_node_form') {
$form['taxonomy'][DRUPALORG_NEWS_VID]['#access'] = user_access('administer nodes');
}
// Add extra choices to the dblog settings selector
if ($form_id == 'dblog_admin_settings') {
$form['dblog_row_limit']['#options'] = drupal_map_assoc(array(100, 1000, 10000, 100000, 250000, 500000, 1000000));
}
// Hook into the CVS application form submit handler.
if ($form_id == 'cvs_application_form') {
$form['#submit'] = array_merge(
array('drupalorg_cvs_app_submit'),
$form['#submit']
);
}
}
// == Security release restrictions ============================================
/**
* Alter release node forms properly for security updates.
*
* Ensure that only privileged users can modify a security release tag and when
* people add this tag, they get to know about the process.
*/
function drupalorg_project_release_node_form_alter(&$form, $form_state) {
if (!empty($form['taxonomy'])) {
$vid = drupalorg_get_release_type_vid();
$security_tid = drupalorg_get_security_update_tid();
if (!empty($form['taxonomy'][$vid])) {
$form['taxonomy'][$vid]['#weight'] = 10;
if (empty($form['nid']['#value'])) {
// Adding a new release.
if (!empty($form['project_release']['rebuild']['#value'])) {
// This is a -dev, don't let anyone mark it a security update.
_drupalorg_remove_security_update($form);
}
else {
// Regular release
$form['taxonomy'][$vid]['#description'] = t('What is a release type?', array('@handbook_url' => DRUPALORG_RELEASE_TYPE_HANDBOOK_URL));
// Add wrapper div for js/drupal.release.node.js
$form['taxonomy'][$vid]['#prefix'] = '';
$form['taxonomy'][$vid]['#suffix'] = '
';
$form['#validate'][] = 'drupalorg_security_release_form_validate';
$confirm_class = 'security-update-confirm';
// Hide the confirmation checkbox on page load unless 'Security
// update' is already selected.
if (empty($form_state['values']['taxonomy'][$vid]) || (array_search($security_tid, $form_state['values']['taxonomy'][$vid]) === FALSE)) {
$confirm_class .= ' js-hide';
}
$form['security_update_confirm'] = array(
'#type' => 'checkbox',
'#title' => t('Are you sure you want to mark this release as a Security update?', array('@security_update_url' => DRUPALORG_SECURITY_UPDATE_HANDBOOK_URL)),
'#prefix' => '',
'#suffix' => '
',
'#weight' => -2,
'#description' => t('If you select %security_update, your release will not be published without the manual intervention of the Drupal Security Team. You should have already contacted the Security Team to coordinate a security advisory (SA) for your release before you committed any security-related patches.', array('%security_update' => t('Security update'), '@security_url' => DRUPALORG_SECURITY_TEAM_URL, '@contact_url' => DRUPALORG_SECURITY_CONTACT_URL)),
'#default_value' => !empty($form_state['values']['security_update_confirm']),
);
$drupalorg_path = drupal_get_path('module', 'drupalorg');
drupal_add_js($drupalorg_path .'/js/drupalorg.release_node.js');
drupal_add_css($drupalorg_path .'/drupalorg.css');
}
}
else {
// Editing an existing release.
if (array_search($security_tid, $form['taxonomy'][$vid]['#default_value']) !== FALSE) {
// If this release is already marked as a Security update, don't
// let regular users change it any futher.
if (!user_access('administer projects')) {
$form['taxonomy'][$vid]['#disabled'] = TRUE;
$form['taxonomy'][$vid]['#value'] = $form['taxonomy'][$vid]['#default_value'];
}
$form['taxonomy'][$vid]['#description'] = t('What is a release type? Since this release is already marked as a %security_update, you can no longer change the release type. If you believe you need to do so for some reason, you should contact the Drupal Security Team.', array('@handbook_url' => DRUPALORG_RELEASE_TYPE_HANDBOOK_URL, '%security_update' => t('Security update'), '@security_url' => DRUPALORG_SECURITY_TEAM_URL, '@contact_url' => DRUPALORG_SECURITY_CONTACT_URL));
}
else {
// Not a Security update, remove that option entirely if this is
// either a -dev snapshot or a non-admin user.
if (!empty($form['#node']->rebuild) || !user_access('administer projects')) {
_drupalorg_remove_security_update($form);
}
$form['taxonomy'][$vid]['#description'] = t('What is a release type?', array('@handbook_url' => DRUPALORG_RELEASE_TYPE_HANDBOOK_URL));
}
}
}
}
}
/**
* Utility function to remove the security update term on node forms.
*/
function _drupalorg_remove_security_update(&$form) {
$vid = drupalorg_get_release_type_vid();
$security_tid = drupalorg_get_security_update_tid();
foreach ($form['taxonomy'][$vid]['#options'] as $i => $option) {
if (!empty($option->option)) {
$tid = key($option->option);
if ($tid == $security_tid) {
unset($form['taxonomy'][$vid]['#options'][$i]);
return;
}
}
}
}
/**
* Set the project_release_type_vid Drupal variable and return its value.
*/
function drupalorg_get_release_type_vid() {
static $vid = 0;
if (empty($vid)) {
$vid = variable_get('project_release_type_vid', 0);
if (empty($vid)) {
$vid = db_result(db_query("SELECT v.vid FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = 'project_release' AND v.name = 'Release type'"));
variable_set('project_release_type_vid', $vid);
}
}
return $vid;
}
/**
* Set the project_release_type_security_update_tid Drupal variable and return its value.
*/
function drupalorg_get_security_update_tid() {
static $tid = 0;
if (empty($tid)) {
$tid = variable_get('project_release_type_security_update_tid', 0);
if (empty($tid)) {
$tid = db_result(db_query("SELECT tid FROM {term_data} WHERE vid = %d AND name = '%s'", drupalorg_get_release_type_vid(), 'Security update'));
variable_set('project_release_type_security_update_tid', $tid);
}
}
return $tid;
}
/**
* Form validation function for security release tag check.
*/
function drupalorg_security_release_form_validate($form, &$form_state) {
$vid = drupalorg_get_release_type_vid();
$security_tid = drupalorg_get_security_update_tid();
if (!empty($form_state['values']['taxonomy'][$vid][$security_tid]) && empty($form_state['values']['security_update_confirm'])) {
// Ensure the user confirms that this release should be marked security.
form_set_error('security_update_confirm', t('You must confirm you want this release to be a Security update', array('@security_update_url' => DRUPALORG_SECURITY_UPDATE_HANDBOOK_URL)));
}
}
// == CVS maintainer housekeeping ==============================================
/**
* Add the user to the CVS maintainer role, when she gets an account.
*/
function drupalorg_cvs_user_edit_submit($form, &$form_state) {
global $language;
if (isset($form_state['values']['cvs_status'])) {
$account = user_load(array('uid' => $form_state['values']['cvs_uid']));
switch ($form_state['values']['cvs_status']) {
case CVS_APPROVED:
db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $form_state['values']['cvs_uid'], DRUPALORG_CVS_USER_ROLE);
break;
case CVS_DISABLED:
db_query('DELETE FROM {users_roles} WHERE uid = %d AND rid = %d', $form_state['values']['cvs_uid'], DRUPALORG_CVS_USER_ROLE);
break;
}
// Clear cache for this user, so that the user will see new functionality.
cache_clear_all($form_state['values']['cvs_uid'] .':'. $language->language, 'cache_menu');
}
}
// == User form validations ====================================================
/**
* Try to catch wildcard email address signups, such as joe+drupal@gmail.com.
*/
function drupalorg_register_mail_validate($form, &$form_state) {
$hit = preg_match('/(.*)\+(.*)\@(.*)/', $form_state['values']['mail'], $match);
if ($hit) {
if (db_result(db_query("SELECT uid FROM {users} WHERE LOWER(mail) LIKE LOWER('%s')", $match[1] .'+%%@'. $match[3])) > 0) {
form_set_error('mail', t('An e-mail address similar to %email is already registered. Have you forgotten your password?', array('%email' => $form_state['values']['mail'], '@password' => url('user/password'))));
}
}
}
/**
* Validate all fields in the user_edit form against the list of bad words.
*
* @todo Core almost supports it with above form_alter but listings are bad
* (when the value is homepage, core does not know about it, so does not print it).
* @todo Headers are blocked by some providers so this is not accurate.
*/
function drupalorg_profile_user_edit_validate($form, &$form_state) {
if (!empty($form_state['values']['homepage']) && is_string($form_state['values']['homepage']) && (strlen($form_state['values']['homepage']) > 7)) {
$result = db_query("SELECT mask FROM {access} WHERE type = '%s' AND status = %d", 'homepage', 0);
$masks = array();
while ($mask = db_fetch_object($result)) {
// Build masks array for preg_matching.
$masks[] = '@'. strtr($mask->mask, array('.' => '\.', '%' => '.*', '_' => '.')) .'@';
}
// Check denied homepages.
foreach ($masks as $mask) {
if (preg_match($mask, $form_state['values']['homepage'])) {
form_set_error('homepage', t('Unsuitable Drupal site detected. This address cannot be set as your Drupal site link.'));
}
}
// Did not work due to several hosts rewriting headers.
/*if (!user_access('administer users')) {
// Check for Drupal-ness of website. Try only once.
$response = drupal_http_request($form_state['values']['homepage'], array(), 'GET', NULL, 1);
if ($response->headers['Expires'] != 'Sun, 19 Nov 1978 05:00:00 GMT') {
form_set_error('homepage', t("Your website does not seem to be a Drupal site. If you think we are wrong, please open an issue in the webmasters' queue."));
}
}*/
}
}
/**
* Submit handler for the user profile form, to serialize languages to a string.
*/
function drupalorg_profile_fix_languages(&$form, &$form_state) {
if (is_array($form_state['values']['profile_languages'])) {
$form_state['values']['profile_languages'] = join('; ', array_keys($form_state['values']['profile_languages']));
}
}
// == Project links ============================================================
/**
* Implemenation of hook_project_page_link_alter().
*/
function drupalorg_project_page_link_alter(&$links, $node) {
// Add "Browse the CVS repository" link for contributed projects by default.
if (!isset($links['development']['links']['browse_repository'])) {
if (isset($node->cvs['repository']) && $node->cvs['repository'] == 2 && !empty($node->cvs['directory'])) {
$links['development']['links']['browse_repository'] = l(t('Browse the CVS repository'), 'http://drupalcode.org/viewvc/drupal/contributions' . $node->cvs['directory']);
}
}
// Link to security handbook page.
$links['development']['links']['report_security_issue'] = l(t('Report a security issue'), 'security-team');
// Link to translation downloads.
if (is_array($node->taxonomy) && isset($node->taxonomy[DRUPALORG_TRANSLATION_TID])) {
// Language projects get links to their language teams.
// $links['resources']['links']['view_translations'] = l(t('Download translations'), 'http://localize.drupal.org/translate/downloads');
}
elseif (!empty($node->project_release['releases'])) {
// Otherwise only include links if we have releases and at least one supported.
$has_supported = db_result(db_query_range(db_rewrite_sql('SELECT 1 FROM {project_release_supported_versions} WHERE nid = %d AND supported = 1'), $node->nid, 0, 1));
if ($has_supported > 0) {
$links['resources']['links']['view_translations'] = l(t('Download translations'), 'http://localize.drupal.org/translate/downloads?project='. $node->project['uri']);
}
}
}
// == External search block ====================================================
/**
* Implementation of hook_block().
*
* @todo Hopefully remove as part of search migration.
*/
function drupalorg_block($op = 'list', $delta = 0, $edit = array()) {
if ($op == 'list') {
$blocks[0] = array('info' => t('External/Alternate Search Advice (only when search is disabled)'),
'weight' => 0, 'enabled' => 0, 'region' => 'header');
return $blocks;
}
else if ($op == 'view') {
switch ($delta) {
case 0:
$block = array('subject' => t('Search Engine'),
'content' => drupalorg_display_block_external_search());
break;
}
return $block;
}
}
/**
* Body for external search block.
*/
function drupalorg_display_block_external_search() {
if (!user_access('search content')) {
$form = '';
$message = 'Due to load issues the Drupal.org search occasionally has to be disabled. When this happens, you can use external search engines and a modifier like "site:drupal.org" to refine your results to Drupal. For more information see the infrastructure queue
.';
return $form . $message;
}
}
// == IRC nick search ==========================================================
/**
* Implementation of hook_search().
*
* Add support for searching for users based on the fixed IRC nickname field.
*
* @todo Possibly remove as part of search migration.
*/
function drupalorg_search($op = 'search', $keys = NULL) {
switch ($op) {
case 'name':
if (user_access('access user profiles')) {
return t('IRC nicks');
}
case 'search':
if (user_access('access user profiles')) {
$find = array();
// Replace wildcards with MySQL/PostgreSQL wildcards.
$keys = preg_replace('!\*+!', '%', $keys);
$result = pager_query("SELECT u.name, u.uid FROM {profile_values} pv INNER JOIN {users} u ON pv.uid = u.uid WHERE pv.fid = 35 AND LOWER(pv.value) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys);
while ($account = db_fetch_object($result)) {
$find[] = array('title' => $account->name, 'link' => url('user/'. $account->uid, array('html' => TRUE)));
}
return $find;
}
}
}
// == Utility functions ========================================================
/**
* Project issue URL generator for Drupal issues.
*
* @param $query
* Array of array of options to pass on in the URL:
* - version (array of release node ids)
* - status (array of status ids)
* - priorities (array of prioristy ids)
* - categories (array of category names)
* - issue_tags (comma separated issue tags)
* @param $absolute
* Set to TRUE to get an absolute URL with http://drupal.org/...
*/
function drupalorg_drupal_issue_url($query = array(), $absolute = FALSE) {
return url('project/issues/search/drupal', array('query' => $query, 'absolute' => $absolute));
}
/**
* Project issue link generator for Drupal issues.
*
* @param $text
* String to use as text for the link.
* @param $query
* A keyed array of options to pass to the query parameter of url().
* - version (An array of release node IDs)
* - status (An array of status IDs)
* - priorities (An array of priority IDs)
* - categories (An array of category names)
* @param $absolute
* Set to TRUE to get an absolute URL with http://drupal.org/...
*/
function drupalorg_drupal_issue_link($text, $query = array(), $absolute = FALSE) {
return l($text, 'project/issues/search/drupal', array('query' => $query, 'absolute' => $absolute));
}
/**
* Implementation of hook_views_api().
*/
function drupalorg_views_api() {
return array(
'api' => 2.0,
'path' => drupal_get_path('module', 'drupalorg'),
);
}
/**
* Implement hook_views_default_views_alter().
*/
function drupalorg_views_default_views_alter(&$views) {
$path = drupal_get_path('module', 'drupalorg');
require_once("$path/drupalorg.views_default.inc");
_drupalorg_views_default_views_alter($views);
}
/**
* FormAPI submit handler
*
* @param $form
* Array, the FormAPI array
* @param $form_state
* Array, FormAPI state array.
*
* This submit handler automatically creates a project issue when the user
* applies for CVS account.
*/
function drupalorg_cvs_app_submit($form, &$form_state) {
global $user;
// Easy to use link to the user's CVS form.
$user_cvs_edit_link = l('CVS edit link for '. $user->name, 'user/'. $user->uid .'/edit/cvs');
// Automagically create a project issue in the Drupal CVS Applications project.
$node = new stdClass();
$node->type = 'project_issue';
$node->uid = $user->uid;
$node->comment = 2; // Allow follow-ups.
$node->pid = variable_get('drupalorg_cvs_app_project_nid', DRUPALORG_CVS_APP_PROJECT_NID);
$node->sid = variable_get('drupalorg_cvs_app_project_sid', 16); // Status is "active (needs more info)".
$node->component = variable_get('drupalorg_cvs_app_project_component', 'Miscellaneous');
$node->category = variable_get('drupalorg_cvs_app_project_task', 'task');
$node->priority = variable_get('drupalorg_cvs_app_project_priority', 2); // Normal.
$node->title = $user->name .' ['. $form_state['values']['cvs_user'] .']'; // Set title to username of applicant (helps when ordering the summary).
$node->body = $user_cvs_edit_link ."\n\n". $form_state['values']['motivation'];
// Create the project issue node.
node_save($node);
// Save the issue nid, used during the email generation phase.
_drupalorg_cvs_app_issue_nid($node->nid);
}
/**
* Implementation of hook_mail_alter()
*/
function drupalorg_mail_alter(&$message) {
global $base_url;
$find = array(
'cvs_cvs_email_submit_applicant',
'cvs_cvs_email_submit_admin',
);
if (in_array($message['id'], $find)) {
// Find and replace the project issue of the project issue.
$message['body'] = str_replace('%cvs-project-issue', $base_url .'/node/'. _drupalorg_cvs_app_issue_nid(), $message['body']);
}
}
/**
* Used to store a variable statically between hook calls.
*
* @param $nid
* Integer, node nid number to save.
*/
function _drupalorg_cvs_app_issue_nid($nid = FALSE) {
static $stored_nid;
if ($nid !== FALSE) {
$stored_nid = $nid;
}
return $stored_nid;
}
/**
* Menu callback handler.
* Special case for node/add/project-issue/cvsapplications.
*/
function _drupalorg_node_add_project_issue_cvsapplications() {
drupal_set_title(t('CVS Applications'));
return t('The creation of issues in this project is prohibited.');
}
/**
* Implementation of hook_apachesolr_update_index().
*
* This adds our specific facets to the Apachesolr search index.
*/
function drupalorg_apachesolr_update_index(&$document, $node) {
if ($type = drupalorg_meta_type_traverse($node, drupalorg_get_meta_type_rules())) {
$document->ss_meta_type = $type;
}
}
/**
* Define rules, per site in the multi-site index, for what meta-types should be
* applied to what type of content.
*/
function drupalorg_get_meta_type_rules() {
$site = variable_get('drupalorg_site', 'main');
$rule_set = array();
// For now, make these the rules for all sites, but can be sectioned off by
// site using $site if needed.
$rule_set = array(
// Define the type of selector: in this case, type.
'type' => array(
// Define the type values that map to meta-types: in this case,
// project_project, book, project_issue, and forum.
'project_project' => array(
// Define a sub-selector to only be run after matching on the primary
// selector: in this case, taxonomy.
'taxonomy' => array(
// Define taxonomy term values and resultant meta types: in this
// case after matching a type of project_project, an associated
// tid of DRUPALORG_MODULE_TID maps to 'module' while an associated
// tid of DRUPALORG_THEME_TID maps to 'theme'.
DRUPALORG_MODULE_TID => 'module',
DRUPALORG_THEME_TID => 'theme',
),
),
'book' => 'documentation',
'project_issue' => 'forum-issues',
'forum' => 'forum-issues',
'og' => 'group',
),
);
return $rule_set;
}
/**
* Recursively iterates through an array looking for type or taxonomy filters,
* and attempting to match against values in the passed in array.
*
* This function takes an array (set in the $conf array in settings.php) and
* handles two conditionals: type and taxonomy. Type values match against the
* node type and taxonomy values match against associated taxonomy terms. If a
* node matches all the criteria, a string will be returned representing the
* meta-type of the node for use by the apachesolr module.
*
* @param object $node
* A fully-formed node object as passed from hook_apachesolr_update_index.
* @param mixed $value
* An array initially respresents the order of operations and the
* resulting meta type value. Currently supported comparisons are type and
* taxonomy. In the final iteration of drupalorg_meta_type_traverse,
* $value should be a string value rather than an array.
* @see drupalorg_get_meta_type_rules for the 'main' site to see
* an example of $value.
*
* @return mixed
* Returns NULL if no meta type is found for the particular node, otherwise,
* if a meta-type is found, a string representing that meta-type is returned.
*
* @see drupalorg_get_meta_type_rules
*/
function drupalorg_meta_type_traverse(&$node, $value) {
// If we've traversed to a point where we have a string value
// simply return it.
if (is_string($value)) {
return $value;
}
// If we have a meta type based off of the node type, check that first.
if (isset($value['type'])) {
// If our node type has an associated meta-type (or further filtering)
// recursively call this function to determine the meta-type.
if (in_array($node->type, array_keys($value['type']))) {
return drupalorg_meta_type_traverse($node, $value['type'][$node->type]);
}
}
// If we have a meta-type based off of taxonomy, check that next.
if (isset($value['taxonomy'])) {
// If we have a taxonomy term that relates to a meta-type, determine all
// matching terms in the node.
if ($keys = array_intersect(array_keys($node->taxonomy), array_keys($value['taxonomy']))) {
// We should only ever have 1 match (meta-type is singular), so do a
// sanity check. If we do only have 1 term, recursively call this function
// to determine the meta-type.
if (count($keys) == 1) {
$tid = array_pop($keys);
return drupalorg_meta_type_traverse($node, $value['taxonomy'][$tid]);
}
}
}
}