diff --git a/question.install b/question.install index 7ddbd2c639c3d625304d0473cdf52360d3d4e3bf..6f237d4c1f464574f87fc80e560143a3076b42f0 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 24005b815480fb068474b4a6cd32adc8fde83306..37bfe9c7688f91bc4868067d21ebe4ead00f0b8e 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".
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.
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 0000000000000000000000000000000000000000..7416e7b79043bd058681a39f87aefce6dd9d03cb --- /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 0000000000000000000000000000000000000000..7d317321c15daf8327d5b737246a9e73591aa486 --- /dev/null +++ b/question_assign/question_assign.install @@ -0,0 +1,38 @@ + '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 0000000000000000000000000000000000000000..697743b1a641762af68ff4618478f177427f75d6 --- /dev/null +++ b/question_assign/question_assign.module @@ -0,0 +1,201 @@ +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 0000000000000000000000000000000000000000..50b75ed744f9e8d1c838b86fa462ad20fb0f24f4 --- /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 0000000000000000000000000000000000000000..de6941f198eb7802aaf526e7cd9635334614f2f3 --- /dev/null +++ b/question_fields/question_fields.install @@ -0,0 +1,182 @@ + '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 0000000000000000000000000000000000000000..a9bba32f8aaa3adf42d7a133308a2228b85a8635 --- /dev/null +++ b/question_fields/question_fields.module @@ -0,0 +1,239 @@ + '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 0000000000000000000000000000000000000000..fb31b0a6d0d711da731133f1b0c6d7b5bd4c5051 --- /dev/null +++ b/question_fields/question_fields.test @@ -0,0 +1,83 @@ + '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")); + + } +} +