summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlastair Aitchison2010-04-21 13:57:13 (GMT)
committer Alastair Aitchison2010-04-21 13:57:13 (GMT)
commit81cbba4cec3ad30707dee7febdeda85f567a07ec (patch)
tree6e5ed71cae44502b3014308aa1b2a9e4aac5109f
parent0188efa0df73ad2aedb9c48d7ee47e3a7ff23d60 (diff)
by tanoshimi: Initial commit of submodules for question assignment and additional fields. Various changes to bring up to date with core 7.x-dev.
-rw-r--r--question.install5
-rw-r--r--question.module180
-rw-r--r--question_assign/question_assign.info9
-rw-r--r--question_assign/question_assign.install38
-rw-r--r--question_assign/question_assign.module201
-rw-r--r--question_fields/question_fields.info9
-rw-r--r--question_fields/question_fields.install182
-rw-r--r--question_fields/question_fields.module239
-rw-r--r--question_fields/question_fields.test83
9 files changed, 910 insertions, 36 deletions
diff --git a/question.install b/question.install
index 7ddbd2c..6f237d4 100644
--- a/question.install
+++ b/question.install
@@ -3,6 +3,8 @@
/**
* @file
* install file for Question module.
+ *
+ * Most of the definition of the node type is in this module.
*/
/**
@@ -68,7 +70,7 @@ function question_install() {
// Attach an instance of each field to the Question node type
foreach (question_field_instances() as $instance) {
- $instance['object_type'] = 'node';
+ $instance['entity_type'] = 'node';
$instance['bundle'] = $question_node['type'];
field_create_instance($instance);
}
@@ -81,7 +83,6 @@ function question_install() {
* and hook_uninstall().
*/
function question_fields() {
- $t = get_t();
return array(
'question_question' => array(
'field_name' => 'question_question',
diff --git a/question.module b/question.module
index 24005b8..37bfe9c 100644
--- a/question.module
+++ b/question.module
@@ -3,7 +3,7 @@
/**
* @file
* Allows users to submit questions to a queue that may be answered by site administrators
- *
+ *
* TODO
* Create upgrade path in hook_install_N
* Theme functions for question form
@@ -38,7 +38,7 @@ function question_permission() {
'delete questions' => array(
'title' => t('Delete questions'),
'description' => t('Delete questions from the queue.'),
- ),
+ ),
'edit own answers' => array(
'title' => t('Edit own answers'),
'description' => t('Edit own answers.'),
@@ -61,10 +61,10 @@ function question_permission() {
/**
* Implements hook_node_access().
*/
-function question_node_access($node, $op, $account) {
- // Get they type of node being accessed
+function question_node_access($node, $op, $account) {
+ // Get the type of node being accessed
$type = is_string($node) ? $node : $node->type;
- // Only affect question nodes
+ // Only affect question nodes
if ($type != 'question') {
return NODE_ACCESS_IGNORE;
}
@@ -96,12 +96,12 @@ function question_node_access($node, $op, $account) {
function question_menu() {
$items = array();
// The page for users to ask a question
- $items['question'] = array(
+ $items['question'] = array(
'title' => 'Ask a question', // note don't wrap in t() function from 6.x
'description' => 'Post a question to be answered.',
'page callback' => 'drupal_get_form',
'page arguments' => array('question_ask_form'),
- 'access callback' => 'user_access',
+ 'access callback' => 'user_access',
'access arguments' => array('ask questions'),
);
// The queue of unanswered questions
@@ -109,8 +109,8 @@ function question_menu() {
'title' => 'Questions',
'description' => 'Manage the sites question queue.',
'page callback' => 'drupal_get_form',
- 'page arguments' => array('question_queue_admin'),
- 'access callback' => 'user_access',
+ 'page arguments' => array('question_queue_admin'),
+ 'access callback' => 'user_access',
'access arguments' => array('view questions'),
'type' => MENU_LOCAL_TASK,
);
@@ -123,7 +123,16 @@ function question_menu() {
'access arguments' => array('administer site configuration'),
'type' => MENU_NORMAL_ITEM,
);
-
+ // Callback to delete a question
+ $items['question/%/delete'] = array(
+ 'title' => 'Delete',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('question_delete_confirm', 1),
+ 'access callback' => 'user_access',
+ 'access arguments' => array('delete questions'),
+ 'type' => MENU_LOCAL_TASK,
+ 'context' => MENU_CONTEXT_INLINE,
+ );
return $items;
}
@@ -173,7 +182,7 @@ function question_settings() {
'#maxlength' => 100,
'#description' => t('This is where users will end up after they submit the question form. Example: "node/454".<br/>Leave blank and the user will be returned to the front page of the site.'),
);
- // Message to display
+ // Message to display after question submitted
$form['question_submission_message'] = array(
'#type' => 'textarea',
'#weight' => -2,
@@ -182,7 +191,7 @@ function question_settings() {
'#size' => 40,
'#description' => t('This message will be shown to the user after submitting a question.<br/>Leave blank to display no message.'),
);
- // Instructions
+ // Instructions on the question ask form
$form['question_instructions'] = array(
'#type' => 'textarea',
'#weight' => -1,
@@ -247,7 +256,6 @@ function question_ask_form_validate(&$form, &$form_state) {
* Implements _form_submit.
*/
function question_ask_form_submit($form, &$form_state) {
-
// Make an array containing all the values to insert
$row = array(
'question' => $form_state['values']['question'],
@@ -275,18 +283,46 @@ function question_ask_form_submit($form, &$form_state) {
}
// Add an entry to the watchdog log
- watchdog('content', 'Question submitted: %question.', array('%question' => $form_state['values']['question']), WATCHDOG_NOTICE);
+ watchdog('Question', 'Question submitted: %question.', array('%question' => $form_state['values']['question']), WATCHDOG_NOTICE);
+}
+
+/**
+ * Themes the question form
+ */
+function theme_question_ask_form($variables) {
+ $form = $variables['form'];
+
+ $output = '';
+ $output .= drupal_render($form['instructions']);
+ $output .= drupal_render($form['questioner']);
+ $output .= drupal_render($form['age']);
+ $output .= drupal_render($form['sex']);
+ $output .= drupal_render($form['question']);
+
+ $output .= drupal_render_children($form);
+
+ return $output;
+
+}
+
+/**
+ * Implements hook_theme().
+ */
+function question_theme() {
+ return array(
+ 'question_ask_form' => array('render element' => 'form'),
+ );
}
/************************************************
* *
- * ASSIGN QUESTIONS *
+ * QUESTION QUEUE MANAGEMENT *
* *
************************************************ /
/**
- * Provide an array of all the possible operations which can be
+ * Provide an array of the bulk operations that can be
* perfomed on items in the question queue.
*
* @see hook_node_operations().
@@ -308,7 +344,7 @@ function question_question_queue_operations() {
/**
* Admin form for questions in the queue.
*
- * @see node_admin_content()
+ * @see node_admin_content().
*/
function question_queue_admin($form, &$form_state) {
if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
@@ -326,10 +362,12 @@ function question_queue_admin($form, &$form_state) {
*/
function question_question_queue_fields() {
return array(
+ // Defines the columns of data in the table
'header' => array(
'question' => array('data' => t('Question'), 'field' => 'question', 'sort' => 'desc'),
'operations' => array('data' => t('Operations')),
),
+ // Defines any functions that should be performed on items of data in each column
'options' => array(
'question' => array('#markup' => 'check_plain'),
)
@@ -337,12 +375,18 @@ function question_question_queue_fields() {
}
/**
- * List all questions currently in the queue, and allow
+ * Lists all questions currently in the queue, and allows
* bulk operations to be performed against those questions.
*
- * @see node_admin_nodes()
+ * @see node_admin_nodes().
*/
function question_queue_admin_form() {
+
+ $form['instructions'] = array(
+ '#type' => 'markup',
+ '#markup' => t('To respond to a question, click on the title.'),
+ );
+
// Build the 'Update options' dropdown.
$form['options'] = array(
'#type' => 'fieldset',
@@ -352,6 +396,7 @@ function question_queue_admin_form() {
);
$options = array();
$operations = array();
+ // Invoke all modules that define operations that can be perfomed on the question queue
foreach (module_invoke_all('question_queue_operations') as $operation => $array) {
$options[$operation] = $array['label'];
}
@@ -379,6 +424,8 @@ function question_queue_admin_form() {
$query->orderByHeader($header);
$result = $query->execute();
+ $destination = drupal_get_destination();
+
$options = array();
// Loop through all the questions
foreach ($result as $question) {
@@ -386,32 +433,98 @@ function question_queue_admin_form() {
foreach ($question as $column => $value) {
// Determine how the value in this field should be displayed
$markup = "";
- if (!empty($value)) {
- if (isset($question_queue_fields['options'][$column]['#markup'])) {
- $markup = call_user_func($question_queue_fields['options'][$column]['#markup'], $value);
- }
- else {
- $markup = check_plain($value);
- }
+ if (isset($question_queue_fields['options'][$column]['#markup'])) {
+ $markup = call_user_func($question_queue_fields['options'][$column]['#markup'], $value);
+ }
+ else {
+ $markup = check_plain($value);
}
// Add this column to the options array
$options[$question->qid][$column] = array('data' => array('#markup' => $markup));
}
+
+ // Build a list of all the accessible operations for the current question.
+ $operations = array();
if (user_access('answer questions')) {
- $options[$question->qid]['operations'] = array('data' => array('#markup' => l(t('respond'), 'node/add/question/' . $question->qid, array('title' => t('create a question node based on this submission')))));
+ $operations['respond'] = array(
+ 'title' => t('respond'),
+ 'href' => 'node/add/question/' . $question->qid,
+ 'query' => $destination,
+ );
}
- }
+ if (user_access('delete questions')) {
+ $operations['delete'] = array(
+ 'title' => t('delete'),
+ 'href' => 'question/' . $question->qid . '/delete',
+ 'query' => $destination,
+ );
+ }
+ $options[$question->qid]['operations'] = array();
+ if (count($operations) > 1) {
+ // Render an unordered list of operations links.
+ $options[$question->qid]['operations'] = array(
+ 'data' => array(
+ '#theme' => 'links__node_operations',
+ '#links' => $operations,
+ '#attributes' => array('class' => array('links', 'inline')),
+ ),
+ );
+ }
+ elseif (!empty($operations)) {
+ // Render the first and only operation as a link.
+ $link = reset($operations);
+ $options[$question->qid]['operations'] = array(
+ 'data' => array(
+ '#type' => 'link',
+ '#title' => $link['title'],
+ '#href' => $link['href'],
+ '#options' => array('query' => $link['query']),
+ ),
+ );
+ }
+ }
// Create the table
$form['questions'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
- '#empty' => t('No questions available, sorry!'),
+ '#empty' => t('No questions available.'),
);
-
+
return $form;
}
+
+
+/**
+ * Callback to delete a single question
+ */
+function question_delete_confirm($form, &$form_state, $qid) {
+ $form['qid'] = array('#type' => 'value', '#value' => $qid);
+ $question = db_query('SELECT question FROM {question_queue} WHERE qid = :qid', array(':qid' => $qid))->fetchField();
+ return confirm_form($form,
+ t('Are you sure you want to delete %question?', array('%question' => $question)),
+ 'admin/content/question',
+ t('This action cannot be undone.'),
+ t('Delete'),
+ t('Cancel')
+ );
+}
+
+/**
+ * Execute node deletion
+ */
+function question_delete_confirm_submit($form, &$form_state) {
+ if ($form_state['values']['confirm']) {
+ $qid = $form_state['values']['qid'];
+ $num_deleted = db_delete('question_queue')
+ ->condition('qid', $qid)
+ ->execute();
+ watchdog('question', 'Deleted question %qid.', array('%qid' => $qid));
+ drupal_set_message(t('The question has been deleted.'));
+ }
+}
+
/**
* Perform updates against multiple question queue items.
*
@@ -427,7 +540,7 @@ function question_queue_mass_update($questions, $updates) {
/**
* Question Queue Mass Update - helper function.
*
- * @see _node_mass_update_helper
+ * @see _node_mass_update_helper()
*/
function _question_queue_mass_update_helper($qid, $updates) {
foreach ($updates as $name => $value) {
@@ -455,7 +568,7 @@ function question_queue_admin_form_validate($form, &$form_state) {
/**
* Submit the question queue bulk update form.
*
- * @see node_admin_nodes_submit
+ * @see node_admin_nodes_submit()
*/
function question_queue_admin_form_submit($form, &$form_state) {
$operations = module_invoke_all('question_queue_operations');
@@ -503,7 +616,7 @@ function question_queue_multiple_delete_confirm($form, &$form_state, $questions)
'Are you sure you want to delete these questions?');
return confirm_form($form,
$confirm_question,
- 'admin/content', t('This action cannot be undone.'),
+ 'admin/content/question', t('This action cannot be undone.'),
t('Delete'), t('Cancel'));
}
@@ -521,7 +634,6 @@ function question_queue_multiple_delete_confirm_submit($form, &$form_state) {
watchdog('content', 'Deleted @count questions.', array('@count' => $count));
drupal_set_message(format_plural($count, 'Deleted 1 question', 'Deleted @count questions.', array('@count' => $count)));
}
- $form_state['redirect'] = 'admin/content/question/assign';
}
diff --git a/question_assign/question_assign.info b/question_assign/question_assign.info
new file mode 100644
index 0000000..7416e7b
--- /dev/null
+++ b/question_assign/question_assign.info
@@ -0,0 +1,9 @@
+; $Id$
+name = Question Assign
+description = Assign questions to be answered by specific experts.
+dependencies[] = question
+package = Question
+core = 7.x
+files[] = question_assign.module
+files[] = question_assign.install
+files[] = question_assign.test
diff --git a/question_assign/question_assign.install b/question_assign/question_assign.install
new file mode 100644
index 0000000..7d31732
--- /dev/null
+++ b/question_assign/question_assign.install
@@ -0,0 +1,38 @@
+<?php
+// $Id$
+/**
+ * @file
+ * install file for Question Assign module.
+ *
+ * Most of the definition of the node type is in this module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+
+function question_assign_schema() {
+ $schema['question_assign'] = array(
+ 'description' => 'Maps questions to users assigned to answer them.',
+ 'fields' => array(
+ 'qid' => array(
+ 'description' => 'The unique id assigned to this question.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'xid' => array(
+ 'description' => 'User ID of the expert assigned to this question.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('qid'),
+ 'indexes' => array(
+ 'xid' => array('xid'),
+ ),
+ );
+
+ return $schema;
+} \ No newline at end of file
diff --git a/question_assign/question_assign.module b/question_assign/question_assign.module
new file mode 100644
index 0000000..697743b
--- /dev/null
+++ b/question_assign/question_assign.module
@@ -0,0 +1,201 @@
+<?php
+// $Id$
+/**
+ * @file
+ * Allows questions to be assigned to 'expert' users to answer.
+ *
+ * TODO
+ * Add submit handler to delete record from assign table when question is deleted (or mass-deleted) from queue
+ * Tests!
+ */
+
+/**
+ * Implements hook_query_TAG_alter().
+ *
+ * Alter the query used to populate the question queue page
+ * to retrieve extra fields.
+ */
+function question_assign_query_question_queue_alter(QueryAlterableInterface $query) {
+ $query->leftJoin('question_assign', 'qa', 'q.qid = qa.qid');
+ $query->fields('qa', array('xid'));
+ $query->leftJoin('users', 'u', 'qa.xid = u.uid');
+ $query->addField('u', 'name', 'expert');
+}
+
+/**
+ * Provide an array of extra fields to be shown on the question queue page.
+ */
+function question_assign_question_queue_fields() {
+ return array(
+ 'header' => array(
+ 'expert' => array('data' => t('Expert'), 'field' => 'expert')
+ ),
+ 'options' => array(
+ 'expert' => array('#markup' => 'question_assign_question_queue_format_expert'),
+ )
+ );
+}
+
+/**
+ * Formats the expert column in the question queue page
+ */
+function question_assign_question_queue_format_expert($value) {
+ if ($value == '') {
+ return '-';
+ }
+ else {
+ return $value;
+ }
+}
+
+
+/**
+ * Implements hook_permission().
+ *
+ * Defines the permissions that users can have related to this module
+ */
+function question_assign_permission() {
+ return array(
+ 'answer assigned questions' => array(
+ 'title' => t('Answer assigned questions'),
+ 'description' => t('Answer questions assigned to this user.'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function question_assign_menu() {
+ $items = array();
+ // The individual expert's 'question queue'
+ $items['user/%/respond'] = array(
+ 'title' => 'Pending Questions',
+ 'description' => 'Answer questions assigned to you.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('question_assign_own_queue'),
+ 'access callback' => 'user_access',
+ 'access arguments' => array('answer assigned questions'),
+ 'type' => MENU_LOCAL_TASK,
+ );
+ $items['user/respond'] = array(
+ 'title' => 'Pending Questions',
+ 'description' => 'Answer questions assigned to you.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('question_assign_own_queue'),
+ 'access callback' => 'user_access',
+ 'access arguments' => array('answer assigned questions'),
+ );
+ return $items;
+}
+
+/**
+ * Lists all questions assigned to the logged in user.
+ */
+function question_assign_own_queue() {
+ global $user;
+
+ // Build the sortable table header.
+ $header = array(
+ 'question' => array('data' => t('Question'), 'field' => 'question'),
+ 'age' => array('data' => t('Age'), 'field' => 'q.age'),
+ 'sex' => array('data' => t('Sex'), 'field' => 'q.sex'),
+ 'respond' => array('data' => t('Expert'))
+ );
+
+ //TODO change this to only select those question
+ $query = db_select('question_queue', 'q')->extend('PagerDefault')->extend('TableSort');
+ $query->join('question_assign', 'qa', 'qa.qid = q.qid');
+ $query->condition('qa.xid', $user->uid, '=');
+ $query->fields('q');
+ $query->limit(50);
+ $result = $query->execute();
+
+ $options = array();
+ foreach ($result as $question) {
+ $options[$question->qid] = array(
+ 'question' => array('data' => array('#markup' => $question->question)),
+ 'age' => array('data' => array('#markup' => $question->age)),
+ 'sex' => array('data' => array('#markup' => $question->sex)),
+ 'respond' => array('data' => array('#markup' => l(t('respond'), 'node/add/askdrann/' . $question->qid, array('title' => t('create a question node based on this submission')))))
+ );
+ }
+ $form['questions'] = array(
+ '#type' => 'tableselect',
+ '#header' => $header,
+ '#options' => $options,
+ '#empty' => t('No content available, sorry!'),
+ );
+
+ // $form['pager'] = array('#markup' => theme('pager', NULL));
+ // $form['#theme'] = 'askdrann_queue_list_own_page';
+
+ return $form;
+}
+
+// TODO
+// Need to adjust the list of operations availablae at
+// question_queue_operations to provide an option to assign to each expert
+/**
+ * Provide an array of all the possible operations which can be
+ * perfomed on items in the question queue.
+ *
+ * @see hook_node_operations().
+ */
+function question_assign_question_queue_operations() {
+ // The operations array will always contain the 'unassign' operation
+ $operations = array(
+ 'unassign' => array(
+ 'label' => t('Unassign'),
+ 'callback' => 'question_assign_question_queue_mass_update',
+ 'callback arguments' => array('updates' => array('xid' => 0)),
+ ),
+ );
+ // It will also contain one option to assign to each user permitted to answer questions
+ $result = db_query("SELECT u.uid, u.name FROM {users} u JOIN {users_roles} ur ON u.uid = ur.uid JOIN {role_permission} rp ON ur.rid = rp.rid WHERE rp.permission = 'respond to questions'");
+ foreach ($result as $user) {
+ $operations[$user->uid] = array(
+ 'label' => t('Assign to') . ' ' . $user->name,
+ 'callback' => 'question_assign_question_queue_mass_update',
+ 'callback arguments' => array('updates' => array('xid' => $user->uid)),
+ );
+ }
+ // TEMPORARY - ASSIGN QUESTIONS TO WEBMASTER TOO!
+ $operations[] = array(
+ 'label' => t('Assign to Webmaster'),
+ 'callback' => 'question_assign_question_queue_mass_update',
+ 'callback arguments' => array('updates' => array('xid' => 1)),
+ );
+ return $operations;
+}
+
+/**
+ * Perform updates against multiple question queue items.
+ *
+ * @see node_mass_update
+ */
+function question_assign_question_queue_mass_update($questions, $updates) {
+ foreach ($questions as $qid) {
+ _question_assign_question_queue_mass_update_helper($qid, $updates);
+ }
+ $message = format_plural(count($questions),
+ 'The question has been assigned',
+ 'The questions have been assigned');
+
+ drupal_set_message(t($message));
+}
+
+/**
+ * Question Queue Mass Update - helper function.
+ *
+ * @see _node_mass_update_helper
+ */
+function _question_assign_question_queue_mass_update_helper($qid, $updates) {
+
+ // If the question has never previously been assigned we need to insert a new record into
+ // question_assign table. Otherwise, update the existing record. Therefore use db_merge().
+ db_merge('question_assign')
+ ->key(array('qid' => $qid))
+ ->fields($updates)
+ ->execute();
+}
diff --git a/question_fields/question_fields.info b/question_fields/question_fields.info
new file mode 100644
index 0000000..50b75ed
--- /dev/null
+++ b/question_fields/question_fields.info
@@ -0,0 +1,9 @@
+; $Id$
+name = Question Fields
+description = Define additional fields to capture during question submission.
+dependencies[] = question
+package = Question
+core = 7.x
+files[] = question_fields.module
+files[] = question_fields.install
+files[] = question_fields.test \ No newline at end of file
diff --git a/question_fields/question_fields.install b/question_fields/question_fields.install
new file mode 100644
index 0000000..de6941f
--- /dev/null
+++ b/question_fields/question_fields.install
@@ -0,0 +1,182 @@
+<?php
+// $Id$
+/**
+ * @file
+ * install file for Question Fields module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+
+function question_fields_schema() {
+ $schema['question_fields'] = array(
+ 'description' => 'Stores additional fields for items in the question queue.',
+ 'fields' => array(
+ 'qid' => array(
+ 'description' => 'The unique id assigned to this question.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ //'default' => 0,
+ ),
+ 'questioner' => array(
+ 'description' => 'The name of the user asking the question.',
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'not null' => FALSE,
+ 'default' => 0,
+ ),
+ 'age' => array(
+ 'description' => 'The age of the questioner.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0
+ ),
+ 'sex' => array(
+ 'description' => 'The sex of the questioner.',
+ 'type' => 'char',
+ 'length' => '1',
+ 'not null' => TRUE
+ ),
+ 'ip' => array(
+ 'description' => "User's IP address.",
+ 'type' => 'varchar',
+ 'length' => '32',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'timestamp' => array(
+ 'description' => 'Timestamp when the question was asked.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('qid'),
+ );
+ return $schema;
+}
+
+/**
+ * Returns an array of all fields created by Question Fields module.
+ *
+ * This is inside its own function so that it can be used in both hook_install()
+ * and hook_uninstall().
+ */
+function question_fields_fields() {
+ return array(
+ 'question_fields_questioner' => array(
+ 'field_name' => 'question_fields_questioner',
+ 'type' => 'text',
+ ),
+ 'question_fields_age' => array(
+ 'field_name' => 'question_fields_age',
+ 'type' => 'text',
+ ),
+ 'question_fields_sex' => array(
+ 'field_name' => 'question_fields_sex',
+ 'type' => 'text',
+ ),
+ );
+}
+
+/**
+ * Returns an array of all instances created by this content type.
+ * The instance tells Drupal which widget to use when entering data into a field
+ * and how to display it in different view modes.
+ *
+ * This is inside its own function so that it can be used in both hook_install()
+ * and hook_uninstall().
+ */
+function question_fields_field_instances() {
+ $t = get_t();
+ return array(
+ 'questioner' => array(
+ 'field_name' => 'question_fields_questioner',
+ 'label' => $t('Questioner'),
+ 'description' => $t('The person asking the question. Can be the user\'s id (uid), username, or an email address.'),
+ 'cardinality' => 1,
+ 'widget' => array(
+ 'type' => 'text_textfield',
+ 'weight' => -5,
+ ),
+ 'display' => array(
+ 'full' => array(
+ 'label' => 'hidden',
+ 'settings' => array(),
+ 'weight' => -5,
+ ),
+ 'teaser' => array(
+ 'label' => 'hidden',
+ 'type' => 'hidden',
+ 'settings' => array(),
+ 'weight' => -5,
+ ),
+ ),
+ ),
+
+ 'age' => array(
+ 'field_name' => 'question_fields_age',
+ 'label' => $t('Age'),
+ 'description' => $t('The age of the person asking the question. Can be the user\'s id (uid), username, or an email address.'),
+ 'cardinality' => 1,
+ 'widget' => array(
+ 'type' => 'text_textfield',
+ 'weight' => -5,
+ ),
+ 'display' => array(
+ 'full' => array(
+ 'label' => 'hidden',
+ 'settings' => array(),
+ 'weight' => -5,
+ ),
+ 'teaser' => array(
+ 'label' => 'hidden',
+ 'type' => 'hidden',
+ 'settings' => array(),
+ 'weight' => -5,
+ ),
+ ),
+ ),
+
+ 'sex' => array(
+ 'field_name' => 'question_fields_sex',
+ 'label' => $t('Sex'),
+ 'description' => $t('Sex.'),
+ 'cardinality' => 1,
+ 'widget' => array(
+ 'type' => 'text_textfield',
+ 'weight' => -5,
+ ),
+ 'display' => array(
+ 'full' => array(
+ 'label' => 'hidden',
+ 'settings' => array(),
+ 'weight' => -5,
+ ),
+ 'teaser' => array(
+ 'label' => 'hidden',
+ 'type' => 'hidden',
+ 'settings' => array(),
+ 'weight' => -5,
+ ),
+ ),
+ ),
+ );
+}
+
+function question_fields_install() {
+ // Create all the fields for a Question node
+ foreach (question_fields_fields() as $field) {
+ field_create_field($field);
+ }
+
+ // Attach an instance of each field to the Question node type
+ foreach (question_fields_field_instances() as $instance) {
+ $instance['entity_type'] = 'node';
+ $instance['bundle'] = 'question';
+ field_create_instance($instance);
+ }
+}
diff --git a/question_fields/question_fields.module b/question_fields/question_fields.module
new file mode 100644
index 0000000..a9bba32
--- /dev/null
+++ b/question_fields/question_fields.module
@@ -0,0 +1,239 @@
+<?php
+// $Id$
+/**
+ * @file
+ * Defines additional fields to be captured on question submission.
+ *
+ * Make each field selectable - none/optional/required
+ * Add field settings page as LOCAL_MENU_TASK to question settings page
+ * Tests!
+ */
+
+/************************************************
+ * *
+ * USER QUESTION SUBMISSION *
+ * *
+ ************************************************/
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Add fields to collect additional information on the
+ * question submission form
+ */
+function question_fields_form_question_ask_form_alter(&$form, &$form_state) {
+ // User's name
+ $form['questioner'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Your name'),
+ '#size' => 40,
+ '#maxlength' => 60,
+ '#description' => t('Your name, username, or email address'),
+ );
+ // For registered users, retrieve their account name
+ if (user_is_logged_in()) {
+ global $user;
+ $form['questioner']['#default_value'] = $user->name;
+ }
+ // User's age
+ $form['age'] = array(
+ '#type' => 'select',
+ '#title' => t('Your age'),
+ '#default_value' => '',
+ '#options' => array(
+ '' => t('Please select...'),
+ '7' => t('7 years old'),
+ '8' => t('8 years old'),
+ '9' => t('9 years old'),
+ '10' => t('10 years old'),
+ '11' => t('11 years old'),
+ '12' => t('12 years old'),
+ '13' => t('13 years old'),
+ '14' => t('14 years old'),
+ '15' => t('15 years old'),
+ '16' => t('16 years old'),
+ '17' => t('17 years old'),
+ '18' => t('18 years old'),
+ '19' => t('19 years old'),
+ '20' => t('20 years old')
+ ),
+ '#weight' => -9,
+ '#description' => t("Please enter your age."),
+ );
+ // User's sex
+ $form['sex'] = array(
+ '#type' => 'radios',
+ '#title' => t('Your sex'),
+ '#options' => array(
+ 'M' => t('Male'),
+ 'F' => t('Female')
+ ),
+ '#weight' => -8,
+ '#description' => t("Are you a boy or a girl?"),
+ );
+
+ // Add validation and submit handlers for the new fields
+ $form['#validate'][] = 'question_fields_question_ask_form_validate';
+ $form['#submit'][] = 'question_fields_question_ask_form_submit';
+}
+
+/**
+ * Implements _form_validate().
+ *
+ * Validate new fields added to the question submission form
+ */
+function question_fields_question_ask_form_validate(&$form, &$form_state) {
+ if ($form_state['values']['questioner'] == '') {
+ form_set_error('age', t('Please tell me how old you are.'));
+ }
+ if ($form_state['values']['age'] == '') {
+ form_set_error('age', t('Please tell me how old you are.'));
+ }
+ if ($form_state['values']['sex'] == '') {
+ form_set_error('sex', t('Please tell me if you are a boy or a girl.'));
+ }
+}
+
+/**
+ * Implements _form_submit().
+ */
+function question_fields_question_ask_form_submit($form, &$form_state) {
+ // Make an array containing all the values to insert
+ $row = array(
+ 'qid' => $form_state['storage']['qid'],
+ 'questioner' => $form_state['values']['questioner'],
+ 'age' => $form_state['values']['age'],
+ 'sex' => $form_state['values']['sex'],
+ 'ip' => ip_address(),
+ 'timestamp' => REQUEST_TIME,
+ );
+ // Insert the row
+ db_insert('question_fields')->fields($row)->execute();
+}
+
+/************************************************
+ * *
+ * QUESTION QUEUE MANAGEMENT *
+ * *
+ ************************************************ /
+
+/**
+ * Implements hook_query_TAG_alter().
+ *
+ * Alter the query used to populate the question queue page
+ * to retrieve extra fields.
+ */
+function question_fields_query_question_queue_alter(QueryAlterableInterface $query) {
+ $query->leftJoin('question_fields', 'qf', 'q.qid = qf.qid');
+ $query->fields('qf', array('questioner', 'age', 'sex', 'ip', 'timestamp'));
+}
+
+/**
+ * Provide an array of extra fields to be shown on the question queue page.
+ */
+function question_fields_question_queue_fields() {
+ return array(
+ 'header' => array(
+ 'questioner' => array('data' => t('Questioner'), 'field' => 'questioner', 'sort' => 'desc'),
+ 'age' => array('data' => t('Age'), 'field' => 'age'),
+ 'sex' => array('data' => t('Sex'), 'field' => 'sex'),
+ 'timestamp' => array('data' => t('Timestamp'), 'field' => 'timestamp'),
+ 'ip' => array('data' => t('IP Address'), 'field' => 'ip'),
+ ),
+ 'options' => array(
+ 'timestamp' => array('#markup' => 'question_fields_question_queue_format_timestamp'),
+ )
+ );
+}
+
+/**
+ * Formats the timestamp column for display on the question queue page
+ */
+function question_fields_question_queue_format_timestamp($value) {
+ if (!$value) {
+ return;
+ }
+ else {
+ return format_date($value, 'custom', 'd/m/Y');
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Alter the question queue form.
+ */
+function question_fields_form_question_queue_admin_alter(&$form, &$form_state) {
+ // Add an additional submit handler
+ $form['#submit'][] = 'question_fields_question_queue_multiple_delete_confirm_submit';
+}
+/**
+ * Delete rows from question_assign table when corresponding
+ * questions are deleted from the queue.
+ */
+function question_fields_question_queue_multiple_delete_confirm_submit($form, &$form_state) {
+ if ($form['operation']['#value'] = 'delete' && $form_state['values']['confirm']) {
+ foreach ($form_state['values']['questions'] as $qid => $value) {
+ $num_deleted = db_delete('question_fields')
+ ->condition('qid', $qid)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Alter the form for deletion of a single question.
+ */
+function question_fields_form_question_delete_confirm_alter(&$form, &$form_state) {
+ // Add an additional submit handler
+ $form['#submit'][] = 'question_fields_question_queue_delete_confirm_submit';
+}
+
+/**
+ * Delete row from question_assign table when corresponding
+ * question is deleted from the queue.
+ */
+function question_fields_question_queue_delete_confirm_submit($form, &$form_state) {
+ if ($form_state['values']['confirm']) {
+ $qid = $form_state['values']['qid'];
+ $num_deleted = db_delete('question_fields')
+ ->condition('qid', $qid)
+ ->execute();
+ }
+}
+
+/************************************************
+ * *
+ * RESPOND TO QUESTIONS *
+ * *
+ ************************************************/
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Alter the node creation form to populate defaults for the new fields.
+ */
+function question_fields_form_question_node_form_alter(&$form, &$form_state) {
+ if (arg(2)=='question' && is_numeric(arg(3))) {
+ $result = db_query('SELECT * FROM {question_fields} WHERE qid = :qid', array(':qid' => arg(3)))->fetchObject();
+ $form['question_fields_age']['und']['0']['value']['#default_value'] = isset($result->age) ? $result->age : '';
+ $form['question_fields_sex']['und']['0']['value']['#default_value'] = isset($result->sex) ? $result->sex : '';
+ $form['question_fields_questioner']['und']['0']['value']['#default_value'] = isset($result->questioner) ? $result->questioner : '';
+ }
+ // Add additional submit handler
+ $form['#submit'][] = 'question_fields_question_node_form_submit';
+}
+
+/**
+ * Implements _form_submit().
+ */
+function question_fields_question_node_form_submit(&$elements, &$form_state) {
+ // If this node came from the queue, delete the queue item...
+ if (isset($elements['qid'])) {
+ $num_deleted = db_delete('question_fields')
+ ->condition('qid', $elements['qid']['#value'])
+ ->execute();
+ }
+}
diff --git a/question_fields/question_fields.test b/question_fields/question_fields.test
new file mode 100644
index 0000000..fb31b0a
--- /dev/null
+++ b/question_fields/question_fields.test
@@ -0,0 +1,83 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Simpletests for Question Fields 7.x.
+ *
+ * Verify Question fields functionality.
+ */
+
+/**
+ * Functionality tests for question module.
+ */
+class QuestionFieldsTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Question Fields',
+ 'description' => 'Verify functionality of the question fields module.',
+ 'group' => 'Question',
+ );
+ }
+
+ function setUp() {
+ // Enable the module.
+ parent::setUp('question', 'question_fields');
+ // Create a user that has the privilege to ask questions
+ $this->question_user = $this->drupalCreateUser(array('ask questions'));
+ // Create a user that has the privilege to manage questions
+ $this->question_admin_user = $this->drupalCreateUser(array('view questions', 'answer questions', 'delete questions'));
+ // Create a site admin user
+ $this->site_admin_user = $this->drupalCreateUser(array('administer site configuration'));
+ }
+
+ /**
+ * Verify the functionality of the example module.
+ */
+ function testQuestionCreation() {
+
+ // Login as a site admin user
+ $this->drupalLogin($this->question_user);
+
+ // Check that unauthorised users cannot ask questions
+ $this->drupalGet('question');
+
+ $this->assertFieldByName('questioner', '', t("Question submission form contains field for questioner name"));
+ $this->assertFieldByName('age', '', t("Question submission form contains field for age"));
+ $this->assertFieldByName('sex', '', t("Question submission form contains field for sex"));
+
+ // Ask a question
+ $this->drupalPost(
+ 'question', // The menu item of the form
+ array(
+ 'question' => 'This is a test question',
+ 'questioner' => 'Bob',
+ 'age' => '10',
+ 'sex' => 'M',
+ ),
+ t('Submit Question')
+
+ );
+
+ // Check that there is one question in the database
+ $count = db_query('SELECT COUNT(*) FROM {question_queue} q INNER JOIN {question_fields} qf ON q.qid = qf.qid')->fetchField();
+ $this->assertEqual($count, 1, t("There is one question with additional fields in the question queue table."));
+
+ // Login as question admin user
+ $this->drupalLogout($this->question_user);
+ $this->drupalLogin($this->question_admin_user);
+
+ // Check that the questions appear on the queue page
+ $this->drupalGet('admin/content/question');
+ $this->assertPattern("/Bob.*10.*M/s", t("Additional field values found on question queue page"));
+
+ // Go to the page to answer the first question
+ $this->drupalGet('node/add/question/1');
+ $this->assertFieldByName('question_fields_questioner[und][0][value]', 'Bob', t("The questioner field is correctly pre-populated on node creation form"));
+ $this->assertFieldByName('question_fields_age[und][0][value]', '10', t("The age field is correctly pre-populated on node creation form"));
+ $this->assertFieldByName('question_fields_sex[und][0][value]', 'M', t("The questioner field is correctly pre-populated on node creation form"));
+
+ }
+}
+