You are here

scale.module in Quiz 8.4

The main file for scale.

Sponsored by: Norwegian Centre for Telemedicine Code: falcon

Scale question type for the Quiz module.

Allows the creation of scale questions (ex: likert scale)

File

question_types/scale/scale.module
View source
<?php

/**
 * The main file for scale.
 *
 * Sponsored by: Norwegian Centre for Telemedicine
 * Code: falcon
 *
 * @file
 * Scale question type for the Quiz module.
 *
 * Allows the creation of scale questions (ex: likert scale)
 */
use Drupal\scale\ScaleQuestion;

/**
 * Implements hook_help().
 */
function scale_help($path, $args) {
  if ($path == 'admin/help#scale') {
    return t('This module provides a scale question type for Quiz. It may be used to construct surveys.');
  }
}

/**
 * Implements hook_menu().
 */
function scale_menu() {
  $items['scale/collection/manage'] = array(
    'title' => 'Manage your preset collections',
    'route_name' => 'scale.collection_manage',
    'type' => MENU_SUGGESTED_ITEM,
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function scale_theme($existing, $type, $theme, $path) {
  return array(
    'scale_creation_form' => array(
      'render element' => 'form',
      'file' => 'scale.theme.inc',
    ),
    'scale_response_form' => array(
      'render element' => 'form',
      'file' => 'scale.theme.inc',
    ),
    'scale_answer_node_view' => array(
      'variables' => array(
        'alternatives' => NULL,
      ),
      'file' => 'scale.theme.inc',
    ),
    'scale_answering_form' => array(
      'render element' => 'form',
      'file' => 'scale.theme.inc',
    ),
  );
}

/**
 * Implements hook_permission().
 */
function scale_permission() {
  return array(
    'Edit global presets' => array(
      'title' => t('Administer presets'),
    ),
  );
}

/**
 * Implements hook_quiz_question_info().
 */
function scale_quiz_question_info() {
  return array(
    'scale' => array(
      'name' => t('Scale question'),
      'description' => t('Quiz questions that allow a user to choose from a scale.'),
      'question provider' => 'Drupal\\scale\\ScaleQuestion',
      'response provider' => 'Drupal\\scale\\ScaleResponse',
      'module' => 'quiz_question',
    ),
  );
}

/**
 * Implements hook_config().
 */
function scale_config() {
  $form['manage'] = array(
    '#markup' => l(t('Manage presets'), 'scale/collection/manage'),
  );
  $form['scale_max_num_of_alts'] = array(
    '#type' => 'textfield',
    '#title' => t('Maximum number of alternatives allowed'),
    '#default_value' => \Drupal::config('scale.settings')
      ->get('scale_max_num_of_alts'),
  );
  $form['#validate'][] = 'scale_config_validate';
  $form['#submit'][] = 'scale_config_submit';
  return $form;
}

/**
 * Validate the scale config form values
 */
function scale_config_validate($form, $form_state) {
  if (!_quiz_is_int($form_state['values']['scale_max_num_of_alts'], 2, 50)) {
    form_set_error('scale_max_num_of_alts', $form_state, t('The number of alternatives must be between 2 and 50'));
  }
}

/**
 * Submit the scale config form values
 */
function scale_config_submit($form, $form_state) {
  $config = \Drupal::config('scale.settings');
  $config
    ->set('scale_max_num_of_alts', $form_state['values']['scale_max_num_of_alts'])
    ->save();
}

/**
 * Implements hook_get_answer().
 */
function scale_get_answer($question_nid, $question_vid, $result_id) {
  $results = db_query('SELECT answer_id, question_vid, question_nid, result_id
    FROM {quiz_scale_user_answers}
    WHERE question_nid = :qnid AND question_vid = :qvid AND result_id = :rid', array(
    ':qnid' => $question_nid,
    ':qvid' => $question_vid,
    ':rid' => $result_id,
  ))
    ->fetchAssoc();
  return $results ? $results : FALSE;
}

/**
 * Implements hook_user_cancel().
 */
function scale_user_cancel($edit, $account, $method) {
  $results = db_query('SELECT id FROM {quiz_scale_answer_collection} ac
              JOIN {quiz_scale_user} u ON(ac.id = u.answer_collection_id)
              WHERE uid = :uid
              AND ac.for_all = :for_all
              AND ac.id NOT IN
                (SELECT answer_collection_id
                 FROM {quiz_scale_node_properties})
              AND ac.id NOT IN
                (SELECT answer_collection_id
                 FROM {quiz_scale_user}
                 WHERE NOT uid = :uid)', array(
    ':uid' => $account
      ->id(),
    ':for_all' => 0,
  ));
  foreach ($results as $result) {
    db_delete('quiz_scale_answer')
      ->condition('answer_collection_id', $result->id)
      ->execute();
    db_delete('quiz_scale_answer_collection')
      ->condition('id', $result->id)
      ->execute();
  }
  db_delete('quiz_scale_user')
    ->condition('uid', $account
    ->id())
    ->execute();
}

/**
 * Form for changing and deleting the current users preset answer collections.
 *
 * Users with the Edit global presets permissions can also add new global
 * presets here.
 *
 * @param $form_state
 * @return form
 */
function scale_manage_collection_form($form, &$form_state) {
  $user = \Drupal::currentUser();
  $edit_globals_access = $user
    ->hasPermission('Edit global presets');

  // We create an instance of ScaleQuestion. We want to use some of its methods.
  $scale_question = new Drupal\scale\ScaleQuestion(new \stdClass());
  $collections = $scale_question
    ->getPresetCollections($edit_globals_access);

  // If user is allowed to edit global answer collections he is also allowed to add new global presets
  if ($edit_globals_access) {
    $new_col = new \stdClass();
    $new_col->for_all = 1;
    $new_col->name = t('New global collection(available to all users)');
    $collections['new'] = $new_col;
  }
  $form = array();
  if (count($collections) == 0) {
    $form['no_col'] = array(
      '#markup' => t("You don't have any preset collections."),
    );
    return $form;
  }

  // Populate the form
  foreach ($collections as $col_id => $obj) {
    $form["collection{$col_id}"] = array(
      '#type' => 'fieldset',
      '#title' => $collections[$col_id]->name,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#group' => 'scale_manage_collection_form',
    );
    $alternatives = isset($collections[$col_id]->alternatives) ? $collections[$col_id]->alternatives : array();
    for ($i = 0; $i < variable_get('scale_max_num_of_alts', 10); $i++) {
      $form["collection{$col_id}"]["alternative{$i}"] = array(
        '#title' => t('Alternative !i', array(
          '!i' => $i + 1,
        )),
        '#size' => 60,
        '#maxlength' => 256,
        '#type' => 'textfield',
        '#default_value' => isset($alternatives[$i]) ? $alternatives[$i] : '',
        '#required' => $i < 2 && $col_id != 'new',
      );
    }
    if ($col_id != 'new') {
      if ($edit_globals_access) {
        $form["collection{$col_id}"]['for_all'] = array(
          '#type' => 'checkbox',
          '#title' => t('Available to all users'),
          '#default_value' => $collections[$col_id]->for_all,
        );
      }
      $form["collection{$col_id}"]['to-do'] = array(
        '#type' => 'radios',
        '#title' => t('What will you do?'),
        '#default_value' => '0',
        '#options' => array(
          t('Save changes, do not change questions using this preset'),
          t('Save changes, and change your own questions who uses this preset'),
          t('Delete this preset(This will not affect existing questions)'),
        ),
      );
    }
    else {
      $form["collection{$col_id}"]["to-do"] = array(
        '#type' => 'value',
        '#value' => 3,
      );
      $form["collection{$col_id}"]["for_all"] = array(
        '#type' => 'value',
        '#value' => 1,
      );
    }
  }
  $form['process'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  $form['#submit'][] = 'scale_collection_form_submit';
  if ($edit_globals_access) {
    $form['#validate'][] = 'scale_collection_form_validate';
  }
  $form['#tree'] = TRUE;
  return $form;
}

/**
 * Handles the scale collection form.
 *
 * @param $form
 * @param $form_state
 */
function scale_collection_form_submit($form, &$form_state) {
  $user = \Drupal::currentUser();
  $changed = 0;
  $deleted = 0;
  foreach ($form_state['values'] as $key => $alternatives) {
    if ($col_id = _scale_get_col_id($key)) {
      $s_q = new ScaleQuestion(new \stdClass());
      $s_q
        ->initUtil($col_id);
      switch ($alternatives['to-do']) {

        // @todo: Rename to-do to $op
        case 0:

        //Save, but don't change
        case 1:

          //Save and change existing questions
          $new_col_id = $s_q
            ->saveAnswerCollection(FALSE, $alternatives, 1);
          if (isset($alternatives['for_all'])) {
            _scale_set_for_all($new_col_id, $alternatives['for_all']);
          }
          if ($new_col_id == $col_id) {
            break;
          }
          $changed++;

          // We save the changes, but don't change existing questions
          if ($alternatives['to-do'] == 0) {

            // The old version of the collection shall not be a preset anymore
            _scale_unpreset_collection($col_id, $user
              ->id());

            // If the old version of the collection doesn't belong to any questions it is safe to delete it.
            $s_q
              ->deleteCollectionIfNotUsed($col_id);
            if (isset($alternatives['for_all'])) {
              _scale_set_for_all($new_col_id, $alternatives['for_all']);
            }
          }
          elseif ($alternatives['to-do'] == 1) {

            // Update all the users questions where the collection is used
            $nids = db_query('SELECT nid FROM {node} WHERE uid = :uid', array(
              ':uid' => 1,
            ))
              ->fetchCol();
            db_update('quiz_scale_node_properties')
              ->fields(array(
              'answer_collection_id' => $new_col_id,
            ))
              ->condition('answer_collection_id', $nids, 'IN')
              ->execute();
            db_delete('quiz_scale_user')
              ->condition('answer_collection_id', $col_id)
              ->condition('uid', $user
              ->id())
              ->execute();
            $s_q
              ->deleteCollectionIfNotUsed($col_id);
          }
          break;
        case 2:

          //Delete
          $got_deleted = $s_q
            ->deleteCollectionIfNotUsed($col_id);
          if (!$got_deleted) {
            _scale_unpreset_collection($col_id, $user
              ->id());
          }
          $deleted++;
          break;
        case 3:

          //New
          if (drupal_strlen($alternatives['alternative0']) > 0) {
            $new_col_id = $s_q
              ->saveAnswerCollection(FALSE, $alternatives, 1);
            _scale_set_for_all($new_col_id, $alternatives['for_all']);
            drupal_set_message(t('New preset has been added'));
          }
          break;
      }
    }
  }
  if ($changed > 0) {
    drupal_set_message(t('!changed presets have been changed.', array(
      '!changed' => $changed,
    )));
  }
  if ($deleted > 0) {
    drupal_set_message(t('!deleted presets have been deleted.', array(
      '!deleted' => $deleted,
    )));
  }
}

/**
 * Validates the scale collection form
 */
function scale_collection_form_validate($form, &$form_state) {

  // If the user is trying to create a new collection
  if (drupal_strlen($form_state['values']['collectionnew']['alternative0']) > 0) {

    // If the new collection don't have two alternatives
    if (drupal_strlen($form_state['values']['collectionnew']['alternative1']) == 0) {

      // This can't be replaced by adding #required to the form elements. If we did so we would always have to
      // create a new collection when we press submit
      form_set_error('collectionnew][alternative1', t('New preset must have atleast two alternatives.'));
    }
  }
}

/**
 * Searches a string for the answer collection id
 *
 * @param $string
 * @return answer collection id
 */
function _scale_get_col_id($string) {
  $res = array();
  $success = preg_match('/^collection([0-9]{1,}|new)$/', $string, $res);
  return $success > 0 ? $res[1] : FALSE;
}

/**
 * Make sure an answer collection isn't a preset for a given user.
 *
 * @param $col_id
 *  Answer_collection_id
 * @param $user_id
 */
function _scale_unpreset_collection($col_id, $user_id) {
  db_delete('quiz_scale_user')
    ->condition('answer_collection_id', $col_id)
    ->condition('uid', $user_id)
    ->execute();
  if (user_access('Edit global presets')) {
    db_update('quiz_scale_answer_collection')
      ->fields(array(
      'for_all' => 0,
    ))
      ->execute();
  }
}

/**
 * Make an answer collection (un)available for all question creators.
 *
 * @param $new_col_id
 *  Answer collection id
 * @param $for_all
 *  0 if not for all,
 *  1 if for all
 */
function _scale_set_for_all($new_col_id, $for_all) {
  db_update('quiz_scale_answer_collection')
    ->fields(array(
    'for_all' => $for_all,
  ))
    ->condition('id', $new_col_id)
    ->execute();
}

Functions

Namesort descending Description
scale_collection_form_submit Handles the scale collection form.
scale_collection_form_validate Validates the scale collection form
scale_config Implements hook_config().
scale_config_submit Submit the scale config form values
scale_config_validate Validate the scale config form values
scale_get_answer Implements hook_get_answer().
scale_help Implements hook_help().
scale_manage_collection_form Form for changing and deleting the current users preset answer collections.
scale_menu Implements hook_menu().
scale_permission Implements hook_permission().
scale_quiz_question_info Implements hook_quiz_question_info().
scale_theme Implements hook_theme().
scale_user_cancel Implements hook_user_cancel().
_scale_get_col_id Searches a string for the answer collection id
_scale_set_for_all Make an answer collection (un)available for all question creators.
_scale_unpreset_collection Make sure an answer collection isn't a preset for a given user.