answers.module in Answers 7.4
Same filename and directory in other branches
The Answers module.
File
answers.moduleView source
<?php
/**
* @file
* The Answers module.
*/
define('ANSWERS_TRANS_UCQUESTIONS', 'answers_trans_ucquestions');
define('ANSWERS_TRANS_LCQUESTIONS', 'answers_trans_lcquestions');
define('ANSWERS_TRANS_UCQUESTION', 'answers_trans_ucquestion');
define('ANSWERS_TRANS_LCQUESTION', 'answers_trans_lcquestion');
define('ANSWERS_TRANS_UCASKED', 'answers_trans_ucasked');
define('ANSWERS_TRANS_LCASKED', 'answers_trans_lcasked');
define('ANSWERS_TRANS_UCANSWERS', 'answers_trans_ucanswers');
define('ANSWERS_TRANS_LCANSWERS', 'answers_trans_lcanswers');
define('ANSWERS_TRANS_UCANSWER', 'answers_trans_ucanswer');
define('ANSWERS_TRANS_LCANSWER', 'answers_trans_lcanswer');
define('ANSWERS_TRANS_UCANSWERED', 'answers_trans_ucanswered');
define('ANSWERS_TRANS_LCANSWERED', 'answers_trans_lcanswered');
module_load_include('inc', 'answers', 'includes/answers.lock');
/**
* Implements hook_menu().
*/
function answers_menu() {
$items = array();
$items['admin/config/content/answers'] = array(
'title' => 'Answers',
'description' => 'Configure how the question/answer service operates',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'answers_settings',
),
'access arguments' => array(
'administer content types',
),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/config/content/answers/settings'] = array(
'type' => MENU_DEFAULT_LOCAL_TASK,
'title' => 'Settings',
);
$items['admin/config/content/answers/orphan'] = array(
'title' => 'Orphans',
'description' => 'Report of answers without questions.',
'page callback' => 'answers_orphan_report',
'file' => 'includes/answers.display.inc',
'access arguments' => array(
'administer content types',
),
'type' => MENU_LOCAL_TASK,
'weight' => 15,
);
return $items;
}
/**
* Implements hook_menu_alter().
*/
function answers_menu_alter(&$items) {
// Remove 'answers' from the 'add content' menu item in the 'navigation' menu.
// Thanks to http://drupal.stackexchange.com/questions/17643/how-to-hide-a-content-type-on-the-node-add-page
if (isset($items['node/add/answers-answer'])) {
$items['node/add/answers-answer']['type'] = MENU_DEFAULT_LOCAL_TASK;
}
}
/**
* Implements hook_permission().
*/
function answers_permission() {
return array(
'manage answers content' => array(
'title' => t('Manage !answers content', answers_translation()),
'description' => t('Edit any !question or !answer content.', answers_translation()),
),
);
}
/**
* Returns the form definition for answers configuration page.
*/
function answers_settings() {
$form = array();
$form['additional_settings'] = array(
'#type' => 'vertical_tabs',
'#attached' => array(
'js' => array(
drupal_get_path('module', 'answers') . '/js/answers_admin.js',
),
),
);
$form['answers_question_lock_settings'] = array(
'#type' => 'fieldset',
'#title' => t('Lock Settings'),
'#weight' => -100,
'#collapsible' => TRUE,
'#group' => 'additional_settings',
);
$form['answers_question_lock_settings']['answers_question_lock_message'] = array(
'#type' => 'textfield',
'#title' => t('!Question lock message', answers_translation()),
'#description' => t('Text to use to notify user that a !question is locked', answers_translation()),
'#default_value' => variable_get('answers_question_lock_message', t('Note: This question is locked.')),
);
$form['renaming'] = array(
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#title' => t('Branding'),
'#group' => 'additional_settings',
);
$form['renaming']['question'] = array(
'#type' => 'fieldset',
'#title' => t('Questions'),
'#weight' => -1,
'#collapsible' => FALSE,
'#collapsed' => FALSE,
);
$form['renaming']['question'][ANSWERS_TRANS_UCQUESTIONS] = array(
'#type' => 'textfield',
'#title' => t('Questions'),
'#description' => t('Word to use in the interface for the upper case plural word !Questions', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_UCQUESTIONS, 'Questions'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['question'][ANSWERS_TRANS_LCQUESTIONS] = array(
'#type' => 'textfield',
'#title' => t('questions'),
'#description' => t('Word to use in the interface for the lower case plural word !questions', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_LCQUESTIONS, 'questions'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['question'][ANSWERS_TRANS_UCQUESTION] = array(
'#type' => 'textfield',
'#title' => t('Question'),
'#description' => t('Word to use in the interface for the upper case singular word !Question', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_UCQUESTION, 'Question'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['question'][ANSWERS_TRANS_LCQUESTION] = array(
'#type' => 'textfield',
'#title' => t('question'),
'#description' => t('Word to use in the interface for the lower case singular word !question', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_LCQUESTION, 'question'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['question'][ANSWERS_TRANS_UCASKED] = array(
'#type' => 'textfield',
'#title' => t('Submitted', answers_translation()),
'#description' => t('Word to use in the interface for the upper case word !Question_submitted', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_UCASKED, 'Asked'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['question'][ANSWERS_TRANS_LCASKED] = array(
'#type' => 'textfield',
'#title' => t('submitted', answers_translation()),
'#description' => t('Word to use in the interface for the lower case word !question_submitted', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_LCASKED, 'asked'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['answer'] = array(
'#type' => 'fieldset',
'#title' => t('Answers'),
'#weight' => -1,
'#collapsible' => FALSE,
'#collapsed' => FALSE,
);
$form['renaming']['answer'][ANSWERS_TRANS_UCANSWERS] = array(
'#type' => 'textfield',
'#title' => t('Answers'),
'#description' => t('Word to use in the interface for the upper case plural word !Answers', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_UCANSWERS, 'Answers'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['answer'][ANSWERS_TRANS_LCANSWERS] = array(
'#type' => 'textfield',
'#title' => t('answers'),
'#description' => t('Word to use in the interface for the lower case plural word !answers', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_LCANSWERS, 'answers'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['answer'][ANSWERS_TRANS_UCANSWER] = array(
'#type' => 'textfield',
'#title' => t('Answer'),
'#description' => t('Word to use in the interface for the upper case singular word !Answer', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_UCANSWER, 'Answer'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['answer'][ANSWERS_TRANS_LCANSWER] = array(
'#type' => 'textfield',
'#title' => t('answer'),
'#description' => t('Word to use in the interface for the lower case singular word !answer', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_LCANSWER, 'answer'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['answer'][ANSWERS_TRANS_UCANSWERED] = array(
'#type' => 'textfield',
'#title' => t('Submitted', answers_translation()),
'#description' => t('Word to use in the interface for the upper case word !Answer_submitted', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_UCANSWERED, 'Answered'),
'#size' => 20,
'#maxlength' => 20,
);
$form['renaming']['answer'][ANSWERS_TRANS_LCANSWERED] = array(
'#type' => 'textfield',
'#title' => t('submitted', answers_translation()),
'#description' => t('Word to use in the interface for the lower case word !answer_submitted', answers_translation()),
'#default_value' => variable_get(ANSWERS_TRANS_LCANSWERED, 'answered'),
'#size' => 20,
'#maxlength' => 20,
);
return system_settings_form($form);
}
/**
* Implements hook_init().
*
* Redirect to its related question when visiting an answer page, scrolling to
* the answer.
*/
function answers_init() {
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == '') {
$node = node_load(arg(1));
if ($node != '') {
$node = entity_metadata_wrapper('node', $node);
if ($node->type
->value() == 'answers_answer') {
drupal_goto('node/' . $node->answers_related_question
->value()->nid, array(
'fragment' => 'node-' . $node->nid
->value(),
'alias' => TRUE,
));
}
}
}
}
/**
* Implements hook_node_info().
*/
function answers_node_info() {
return array(
'answers_question' => array(
'name' => t('!Question', answers_translation()),
'base' => 'answers',
'description' => t('A !question which can be !answer_submitted by other users.', answers_translation()),
'title_label' => t('Title'),
'locked' => TRUE,
),
'answers_answer' => array(
'name' => t('!Answer', answers_translation()),
'base' => 'answers',
'description' => t('An !answer provided to !question !question_submitted by a member of the community.', answers_translation()),
'has_title' => FALSE,
'locked' => TRUE,
),
);
}
/**
* Implements hook_node_access().
*
* Grant users with 'manage answers content' the ability to perform any op on
* any question or answer.
*/
function answers_node_access($node, $op, $account) {
$type = is_string($node) ? $node : $node->type;
if (($type == 'answers_answer' || $type == 'answers_question') && user_access('manage answers content', $account)) {
return NODE_ACCESS_ALLOW;
}
return NODE_ACCESS_IGNORE;
}
/**
* Implements hook_form().
*/
function answers_form($node, $form_state) {
return node_content_form($node, $form_state);
}
/**
* Implements hook_form_alter().
*/
function answers_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'answers_answer_node_form') {
// Disallow adding an answer that is not attached to a question.
$node = $form['#node'];
if (!isset($node->answers_related_question[LANGUAGE_NONE][0]['target_id'])) {
drupal_set_message(t('You cannot post an !answer without a !question.', answers_translation()), 'error');
drupal_not_found();
exit;
}
// Disallow manually assigning an answer to a question.
hide($form['answers_related_question']);
$form['body'][LANGUAGE_NONE][0]['#title'] = '';
}
elseif ($form_id == 'answers_question_node_form') {
// Disallow manually setting the question lock.
hide($form['question_locks']);
}
}
/**
* Implements hook_node_view().
*
* Add the view for answers to a question.
* Add the new answer form.
* Add the operations links to an answer node.
*/
function answers_node_view($node, $view_mode = 'full') {
module_load_include('inc', 'answers', 'includes/answers.lock');
if ($node->type == 'answers_question') {
if ($view_mode == 'full') {
if (answers_question_locked_p($node)) {
$node->content['lock_message'] = array(
'#markup' => variable_get('answers_question_lock_message', ''),
'#prefix' => '<div class="answers-question-locked-message">',
'#suffix' => '</div>',
'#weight' => -100,
);
}
if (answers_create_answer_permission_p($node)) {
global $user;
// Create an empty placeholder for an answer node.
$node_answer = array(
'uid' => $user->uid,
'name' => isset($user->name) ? $user->name : '',
'type' => 'answers_answer',
'language' => LANGUAGE_NONE,
);
// Set the node reference field of the answer to point to its question.
$node_answer['answers_related_question'][LANGUAGE_NONE][0]['target_id'] = $node->nid;
$form_state['build_info']['args'][] = (object) $node_answer;
form_load_include($form_state, 'inc', 'node', 'node.pages');
// Create a form to edit/save the placeholder answer.
$answer_form = drupal_build_form('answers_answer_node_form', $form_state);
// Add the form to the question page.
$node->content['new_answer_form'] = $answer_form;
$node->content['new_answer_form']['#weight'] = 150;
$node->content['new_answer_form']['new_answer_form_title'] = array(
'#theme' => 'html_tag',
'#tag' => 'h2',
'#attributes' => array(
'class' => 'new-answer-form-title',
),
'#value' => t('Your !Answer', answers_translation()),
'#weight' => -100,
);
}
}
// Include the answers in the content when view_mode is
// either 'search_index' or 'full'.
if ($view_mode == 'search_index' || $view_mode == 'full') {
$node->content['answers_list'] = array(
'#type' => 'markup',
'#markup' => views_embed_view('question_answers', 'default', $node->nid),
'#weight' => 49,
);
}
}
if ($view_mode == 'full' && $node->type == 'answers_answer') {
if (node_access('update', $node)) {
$node->content['links']['#links']['answers-answer-edit'] = array(
'title' => t('Edit'),
'href' => 'node/' . $node->nid . '/edit',
);
}
if (node_access('delete', $node)) {
$node->content['links']['#links']['answers-answer-delete'] = array(
'title' => t('Delete'),
'href' => 'node/' . $node->nid . '/delete',
);
}
}
}
/**
* Checks permissions access.
*
* Return NULL if the current user does not have permission to create an answer
* to the question.
*/
function answers_create_answer_permission_p($question) {
module_load_include('inc', 'answers', 'includes/answers.lock');
global $user;
$locked = answers_question_locked_p($question);
return node_access('create', 'answers_answer') && (!$locked || user_access('manage answers content', $user));
}
/**
* Implements hook_node_delete().
*
* Delete answers of some question when this one is deleted. On cascade.
*
* TODO: This is going to be obsolete (hopefully) when the patch been worked in:
* http://drupal.org/node/1368386 is commited to entityreference
*/
function answers_node_delete($node) {
if ($node->type == 'answers_question') {
$nids = db_query('SELECT entity_id FROM {field_data_answers_related_question} WHERE answers_related_question_target_id = :nid', array(
':nid' => $node->nid,
))
->fetchCol();
node_delete_multiple($nids);
}
}
/**
* Return all questions.
*/
function answers_all_questions() {
$query = new EntityFieldQuery();
$entities = $query
->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'answers_question')
->execute();
return empty($entities) ? $entities : entity_load('node', array_keys($entities['node']));
}
/**
* Return all answers to a question.
*
* @param object $question
* A fully loaded question node.
*/
function answers_question_answers($question) {
$query = new EntityFieldQuery();
$entities = $query
->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'answers_answer')
->fieldCondition('answers_related_question', 'target_id', $question->nid, '=')
->execute();
return empty($entities) ? array() : entity_load('node', array_keys($entities['node']));
}
/**
* Return the question to which an answer responds.
*
* @param object $answer
* Either an answer node or an answer nid.
*/
function answers_answer_question($answer) {
return entity_metadata_wrapper('node', $answer)->answers_related_question
->value();
}
/**
* Implements hook_views_api().
*/
function answers_views_api() {
return array(
'api' => 3.0,
'path' => drupal_get_path('module', 'answers'),
);
}
/**
* Implements hook_theme_registry_alter().
*
* See:
* http://www.metachunk.com/blog/adding-module-path-drupal-7-theme-registry.
*/
function answers_theme_registry_alter(&$theme_registry) {
$mod_path = drupal_get_path('module', 'answers');
// Munge on a copy.
$theme_registry_copy = $theme_registry;
_theme_process_registry($theme_registry_copy, 'phptemplate', 'theme_engine', 'pow', $mod_path);
$theme_registry += array_diff_key($theme_registry_copy, $theme_registry);
$hooks = array(
'node',
);
foreach ($hooks as $h) {
_answers_insert_after_first_element($theme_registry[$h]['theme paths'], $mod_path);
}
}
/**
* Helper function for re-ordering arrays (needed by theme_registry_alter).
*/
function _answers_insert_after_first_element(&$a, $element) {
if (is_array($a)) {
$first_element = array_shift($a);
array_unshift($a, $first_element, $element);
}
}
/**
* Implements hook_preprocess_node().
*/
function answers_preprocess_node(&$vars) {
_answers_check_type_theming_suggestion($vars, 'node__answers');
}
/**
* Implements hook_preprocess_comment().
*/
function answers_preprocess_comment(&$vars) {
_answers_check_type_theming_suggestion($vars, 'comment__node_answers');
}
/**
* Implements hook_preprocess_comment_wrapper().
*/
function answers_preprocess_comment_wrapper(&$vars) {
_answers_check_type_theming_suggestion($vars, 'comment_wrapper__node_answers');
}
/**
* Implements hook_views_data().
*/
function answers_views_data() {
$data['views']['answers_count'] = array(
'title' => t('!Answers Count', answers_translation()),
'help' => t('Shows the number of answers to a question.'),
'area' => array(
'handler' => 'AnswersCountViewsHandler',
),
);
$data['answers']['table']['group'] = t('!Answers', answers_translation());
$data['answers']['table']['join'] = array(
'#global' => array(),
);
$data['answers']['new_answers'] = array(
'title' => t('Has new content'),
'field' => array(
'handler' => 'AnswersNewContentViewsHandler',
'help' => t('Show a marker if the !question is new or has new !answers since last access.', answers_translation()),
),
);
$data['answers']['new_answers_since_last_login'] = array(
'title' => t('Has new content since last login'),
'field' => array(
'handler' => 'AnswersNewContentSinceLastLoginViewsHandler',
'help' => t('Show a marker if the !question is new or has new !answers since last login.', answers_translation()),
),
);
return $data;
}
/**
* Helper function for preprocess hooks.
*/
function _answers_check_type_theming_suggestion(&$vars, $theme_hook_suggestions) {
if ($vars['node']->type == 'answers_answer' || $vars['node']->type == 'answers_question') {
$vars['theme_hook_suggestions'][] = $theme_hook_suggestions;
}
}
/**
* Implements hook_views_post_render().
*/
function answers_views_post_render(&$view, &$output, &$cache) {
if ($view->name == 'question_answers') {
if (count($view->result)) {
foreach ($view->result as $object) {
node_tag_new($object);
}
}
}
}
/**
* Returns an array of common translation placeholders.
*/
function answers_translation() {
static $trans;
if (!isset($trans)) {
$trans = array(
'!Questions' => check_plain(variable_get(ANSWERS_TRANS_UCQUESTIONS, 'Questions')),
'!questions' => check_plain(variable_get(ANSWERS_TRANS_LCQUESTIONS, 'questions')),
'!Question' => check_plain(variable_get(ANSWERS_TRANS_UCQUESTION, 'Question')),
'!question' => check_plain(variable_get(ANSWERS_TRANS_LCQUESTION, 'question')),
'!Question_submitted' => check_plain(variable_get(ANSWERS_TRANS_UCASKED, 'Asked')),
'!question_submitted' => check_plain(variable_get(ANSWERS_TRANS_LCASKED, 'asked')),
'!Answers' => check_plain(variable_get(ANSWERS_TRANS_UCANSWERS, 'Answers')),
'!answers' => check_plain(variable_get(ANSWERS_TRANS_LCANSWERS, 'answers')),
'!Answer' => check_plain(variable_get(ANSWERS_TRANS_UCANSWER, 'Answer')),
'!answer' => check_plain(variable_get(ANSWERS_TRANS_LCANSWER, 'answer')),
'!Answer_submitted' => check_plain(variable_get(ANSWERS_TRANS_UCANSWERED, 'Answered')),
'!answer_submitted' => check_plain(variable_get(ANSWERS_TRANS_LCANSWERED, 'answered')),
);
// Calling all modules implementing hook_answers_translation_alter():
drupal_alter('answers_translation', $trans);
}
return $trans;
}