diff --git a/text_captcha/phrase_captcha/phrase_captcha.admin.inc b/text_captcha/phrase_captcha/phrase_captcha.admin.inc index f8bbc252234edbfb3ac03058d74119074e2b131a..5069e9a7c3db6fa89efd8e5fd59a418d61510b23 100644 --- a/text_captcha/phrase_captcha/phrase_captcha.admin.inc +++ b/text_captcha/phrase_captcha/phrase_captcha.admin.inc @@ -1,6 +1,9 @@ 'radios', '#title' => t('Kind of words to use in the CAPTCHA phrase'), @@ -20,26 +23,26 @@ function phrase_captcha_settings_form() { '#default_value' => variable_get('phrase_captcha_words', PHRASE_CAPTCHA_GENERATE_NONSENSE_WORDS), '#required' => TRUE, ); - // form elements for the word pools + // Form elements for the word pools _text_captcha_word_pool_form_items($form, 'phrase_captcha_userdefined_word_pool', t('User defined word pool'), t('Enter the words to use in the CAPTCHA phrase (space separated, no punctuation).'), '' ); - // select form element for the number of words in the CAPTCHA phrase + // Select form element for the number of words in the CAPTCHA phrase $form['phrase_captcha_word_quantity'] = array( '#type' => 'select', '#title' => t('Number of words in the CAPTCHA phrase'), - '#default_value' => (int) variable_get('phrase_captcha_word_quantity', 5), - '#options' => array(4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10), + '#default_value' => variable_get('phrase_captcha_word_quantity', 5), + '#options' => drupal_map_assoc(array(4, 5, 6, 7, 8, 9, 10)), '#required' => TRUE, ); - // select form element for the number of additional words + // Select form element for the number of additional words $form['phrase_captcha_additional_word_quantity'] = array( '#type' => 'select', '#title' => t('Maximum number of additional words to let the user choose from'), - '#default_value' => (int) variable_get('phrase_captcha_additional_word_quantity', 1), - '#options' => array(0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5), + '#default_value' => variable_get('phrase_captcha_additional_word_quantity', 1), + '#options' => drupal_map_assoc(array(0, 1, 2, 3, 4, 5)), '#required' => TRUE, ); $form['phrase_captcha_word_selection_challenges'] = array( @@ -48,7 +51,7 @@ function phrase_captcha_settings_form() { '#options' => _phrase_captcha_available_word_challenges(), '#default_value' => _phrase_captcha_enabled_word_challenges(), ); - // add additional validation handler + // Add additional validation handler $form['#validate'][] = 'phrase_captcha_settings_form_validate'; return system_settings_form($form); @@ -59,10 +62,10 @@ function phrase_captcha_settings_form() { */ function phrase_captcha_settings_form_validate($form, &$form_state) { if ($form_state['values']['phrase_captcha_words'] == PHRASE_CAPTCHA_USER_DEFINED_WORDS) { - $word_count_minimum = (int) $form_state['values']['phrase_captcha_word_quantity'] + (int) $form_state['values']['phrase_captcha_additional_word_quantity'] + 2; + $word_count_minimum = $form_state['values']['phrase_captcha_word_quantity'] + $form_state['values']['phrase_captcha_additional_word_quantity'] + 2; _text_captcha_word_pool_validate('phrase_captcha_userdefined_word_pool', $form_state['values'], $word_count_minimum, NULL, NULL); } - // check word selection + // Check word selection if (count(array_filter($form_state['values']['phrase_captcha_word_selection_challenges'])) < 1) { form_set_error('phrase_captcha_word_selection_challenges', t('You need to select at least one word selection criterium')); } diff --git a/text_captcha/phrase_captcha/phrase_captcha.inc b/text_captcha/phrase_captcha/phrase_captcha.inc deleted file mode 100644 index d1dc3cd4dcdd920aa5a98ac7c1ef2e15fec88a1b..0000000000000000000000000000000000000000 --- a/text_captcha/phrase_captcha/phrase_captcha.inc +++ /dev/null @@ -1,143 +0,0 @@ - t('first'), 2 => t('second'), 3 => t('third'), - 4 => t('fourth'), 5 => t('fifth'), 6 => t('sixth'), 7 => t('seventh'), - 8 => t('eighth'), 9 => t('ninth'), 10 => t('tenth')); - if (array_key_exists($n, $ordinalmap)) { - return $ordinalmap[$n]; - } - else { - return "{$n}th"; - } -} - - -function _phrase_captcha_available_word_challenges() { - return array( - '_phrase_captcha_word_question_word_index' => 'Word index', - '_phrase_captcha_word_question_alphabetical_misplaced' => 'Alphabetical misplacement', - '_phrase_captcha_word_question_double_occurence' => 'Double occurence', - ); -} - -function _phrase_captcha_enabled_word_challenges() { - $word_challenges = variable_get('phrase_captcha_word_selection_challenges', array()); - if ($word_challenges) { - return array_keys(array_filter($word_challenges)); - } - else { - return array_keys(_phrase_captcha_available_word_challenges()); - } -} - -function _phrase_captcha_word_question_word_index($words) { - $key = array_rand($words, 1); - $answer = $words[$key]; - if (mt_rand(0, 1)) { - $description = t('What is the @nth word in the CAPTCHA phrase above?', - array('@nth' => _phrase_captcha_ordinal($key + 1))); - } - else { - $n = count($words) - $key; - if ($n == 1) { - $description = t('What is the last word in the CAPTCHA phrase above?'); - } - else { - $description = t('What is the @nth last word in the CAPTCHA phrase above?', - array('@nth' => _phrase_captcha_ordinal($n))); - } - } - return array($words, $description, $answer); -} - -function _phrase_captcha_word_question_alphabetical_misplaced($words) { - // sort the words - mt_rand(0, 1) ? sort($words) : rsort($words); - // pick a word and its new destination - // new destination has to be at least 2 places from the original place, - // otherwise it could lead to something like swapping two neighbours, - // in which case there is no unique answer. - $from = $to = 0; - while (abs($from - $to) < 2) { - $from = array_rand($words, 1); - $to = array_rand($words, 1); - } - // get the word - $answer = $words[$from]; - // move the word from $from to $to - unset($words[$from]); - array_splice($words, $to, 0, $answer); - // build the description - $description = t('Which word does not follow the alphabetical order in the CAPTCHA phrase above?'); - return array($words, $description, $answer); -} - -function _phrase_captcha_word_question_double_occurence($words) { - // assure single occurence of each word - $words = array_unique($words); - // pick a word - $key = array_rand($words, 1); - $answer = $words[$key]; - // replace another word with it - while (($pos = array_rand($words, 1)) == $key) { - // NOP aka NOOP aka pass - } - array_splice($words, $pos, 1, $answer); - $description = t('Which word occurs two times in the CAPTCHA phrase above?'); - return array($words, $description, $answer); -} diff --git a/text_captcha/phrase_captcha/phrase_captcha.info b/text_captcha/phrase_captcha/phrase_captcha.info index 3e7dcd33138e3666165d93598053855de24b4ea4..93278d8e7d3e1d43dc18e95037bf9aba73ec6952 100644 --- a/text_captcha/phrase_captcha/phrase_captcha.info +++ b/text_captcha/phrase_captcha/phrase_captcha.info @@ -1,5 +1,6 @@ -name = "Phrase CAPTCHA" -description = "Provides CAPTCHA that requires to pick for the right word/character out of a CAPTCHA phrase." +name = Phrase CAPTCHA +description = Provides CAPTCHA that requires to pick for the right word/character out of a CAPTCHA phrase. package = "Spam control" dependencies[] = captcha -core = 6.x +core = 7.x +configure = admin/config/people/captcha/phrase_captcha \ No newline at end of file diff --git a/text_captcha/phrase_captcha/phrase_captcha.install b/text_captcha/phrase_captcha/phrase_captcha.install index bf5e717e4c0b14be585384d94b731a0d9cebd2a0..fd7d361f1b3576dd8a4df0325646db7432679979 100644 --- a/text_captcha/phrase_captcha/phrase_captcha.install +++ b/text_captcha/phrase_captcha/phrase_captcha.install @@ -1,7 +1,13 @@ '. t('This phrase based CAPTCHA presents a CAPTCHA phrase of a given number of words and asks to pick the right word (based on counting, alphabetical order, etc).') .'

'; + case 'admin/config/people/captcha/phrase_captcha': + return '

' . t('This phrase based CAPTCHA presents a CAPTCHA phrase of a given number of words and asks to pick the right word (based on counting, alphabetical order, etc).') . '

'; } } /** - * Implementation of hook_menu(). + * Implements hook_menu(). */ function phrase_captcha_menu() { $items = array(); - // add an administration tab for phrase_captcha - $items['admin/user/captcha/phrase_captcha'] = array( + // Add an administration tab for phrase_captcha + $items['admin/config/people/captcha/phrase_captcha'] = array( 'title' => 'Phrase CAPTCHA', 'file' => 'phrase_captcha.admin.inc', 'page callback' => 'drupal_get_form', @@ -40,43 +38,42 @@ function phrase_captcha_menu() { } /** - * Implementation of hook_captcha + * Implements hook_captcha(). */ -function phrase_captcha_captcha($op, $captcha_type='') { +function phrase_captcha_captcha($op, $captcha_type = '') { switch ($op) { case 'list': return array('Phrase CAPTCHA'); case 'generate': if ($captcha_type == 'Phrase CAPTCHA') { - require_once('phrase_captcha.inc'); - // generate words - $words = _phrase_captcha_generate_words((int) variable_get('phrase_captcha_word_quantity', 5)); - // pick a random word selection challenge + // Generate words + $words = _phrase_captcha_generate_words(variable_get('phrase_captcha_word_quantity', 5)); + // Pick a random word selection challenge $word_challenges = _phrase_captcha_enabled_word_challenges(); $key = array_rand($word_challenges); $function = $word_challenges[$key]; list($phrase_words, $question, $solution) = call_user_func($function, $words); - // build options list - $all_words = array_merge($words, _phrase_captcha_generate_words((int) variable_get('phrase_captcha_additional_word_quantity', 1))); + // Build options list + $all_words = array_merge($words, _phrase_captcha_generate_words(variable_get('phrase_captcha_additional_word_quantity', 1))); shuffle($all_words); $options = array(); foreach ($all_words as $word) { $options[$word] = $word; } - // store the answer and build the form elements + // Store the answer and build the form elements $captcha = array(); $captcha['solution'] = $solution; $captcha['form']['captcha_phrase'] = array( '#type' => 'markup', - '#value' => '"'. implode(' ', $phrase_words) .'"', + '#value' => '"' . implode(' ', $phrase_words) . '"', '#weight' => -2, ); $captcha['form']['captcha_response'] = array( '#type' => 'radios', '#title' => $question, '#options' => $options, - // extra class needed for additional CSS'ing of the options - '#attributes' => array('class' => 'text-captcha-word-list-radios'), + // Extra class needed for additional CSS'ing of the options + '#attributes' => array('class' => array('text-captcha-word-list-radios')), // The following entry '#DANGEROUS_SKIP_CHECK' is needed to prevent // that Drupal checks during validation phase if a submitted option // is in the list of possible options. (see includes/form.inc) @@ -86,9 +83,166 @@ function phrase_captcha_captcha($op, $captcha_type='') { '#DANGEROUS_SKIP_CHECK' => TRUE, // '#required' => TRUE, ); - // additional text CAPTCHA CSS rules - drupal_add_css(drupal_get_path('module', 'phrase_captcha') .'/../text_captcha.css'); + // Add css to form + $captcha['#attached']['css'] = array( + drupal_get_path('module', 'phrase_captcha') . '/../text_captcha.css', + ); return $captcha; } } } + + +/** + * Function for generating a random nonsense word of a given number of characters + */ +function _phrase_captcha_generate_nonsense_word($characters) { + $vowels = 'bcdfghjklmnpqrstvwxyz'; + $consonants = 'aeiou'; + $vowel_max = drupal_strlen($vowels) - 1; + $consonant_max = drupal_strlen($consonants) - 1; + $word = ''; + $o = mt_rand(0, 1); // Randomly start with vowel or consonant + for ($i = 0; $i < $characters ; ++$i) { + if (($i + $o) % 2) { + $word .= $consonants[mt_rand(0, $consonant_max)]; + } + else { + $word .= $vowels[mt_rand(0, $vowel_max)]; + } + } + return $word; +} + +/** + * Function for generating an array of words + */ +function _phrase_captcha_generate_words($num) { + $words = array(); + if (variable_get('phrase_captcha_words', PHRASE_CAPTCHA_GENERATE_NONSENSE_WORDS) == PHRASE_CAPTCHA_USER_DEFINED_WORDS) { + // Use user defined words + $uwords = _text_captcha_word_pool_get_content('phrase_captcha_userdefined_word_pool', NULL, '', TRUE); + switch ($num) { + case 0: break; + case 1: + $words[] = $uwords[array_rand($uwords, $num)]; + break; + default: + $keys = array_rand($uwords, $num); + foreach ($keys as $key) { + $words[] = $uwords[$key]; + } + break; + } + } + else { + // Generate nonsense words + for ($w=0; $w < $num; ++$w) { + $words[] = _phrase_captcha_generate_nonsense_word(mt_rand(3, 7)); + } + } + return $words; +} + +/** + * Function that returns a textual represention of an ordinal + */ +function _phrase_captcha_ordinal($n) { + $ordinalmap = array( + 1 => t('first'), + 2 => t('second'), + 3 => t('third'), + 4 => t('fourth'), + 5 => t('fifth'), + 6 => t('sixth'), + 7 => t('seventh'), + 8 => t('eighth'), + 9 => t('ninth'), + 10 => t('tenth') + ); + if (array_key_exists($n, $ordinalmap)) { + return $ordinalmap[$n]; + } + else { + return "{$n}th"; + } +} + +/** + * Return array with availible word challenges + */ +function _phrase_captcha_available_word_challenges() { + return array( + '_phrase_captcha_word_question_word_index' => 'Word index', + '_phrase_captcha_word_question_alphabetical_misplaced' => 'Alphabetical misplacement', + '_phrase_captcha_word_question_double_occurence' => 'Double occurence', + ); +} + +function _phrase_captcha_enabled_word_challenges() { + $word_challenges = variable_get('phrase_captcha_word_selection_challenges', array()); + if ($word_challenges) { + return array_keys(array_filter($word_challenges)); + } + else { + return array_keys(_phrase_captcha_available_word_challenges()); + } +} + +function _phrase_captcha_word_question_word_index($words) { + $key = array_rand($words, 1); + $answer = $words[$key]; + if (mt_rand(0, 1)) { + $description = t('What is the @nth word in the CAPTCHA phrase above?', + array('@nth' => _phrase_captcha_ordinal($key + 1))); + } + else { + $n = count($words) - $key; + if ($n == 1) { + $description = t('What is the last word in the CAPTCHA phrase above?'); + } + else { + $description = t('What is the @nth last word in the CAPTCHA phrase above?', + array('@nth' => _phrase_captcha_ordinal($n))); + } + } + return array($words, $description, $answer); +} + +function _phrase_captcha_word_question_alphabetical_misplaced($words) { + // Sort the words + mt_rand(0, 1) ? sort($words) : rsort($words); + // Pick a word and its new destination + // New destination has to be at least 2 places from the original place, + // otherwise it could lead to something like swapping two neighbours, + // in which case there is no unique answer. + $from = 0; + $to = 0; + while (abs($from - $to) < 2) { + $from = array_rand($words, 1); + $to = array_rand($words, 1); + } + // Get the word + $answer = $words[$from]; + // Move the word from $from to $to + unset($words[$from]); + array_splice($words, $to, 0, $answer); + // bBuild the description + $description = t('Which word does not follow the alphabetical order in the CAPTCHA phrase above?'); + return array($words, $description, $answer); +} + +function _phrase_captcha_word_question_double_occurence($words) { + // Assure single occurence of each word + $words = array_unique($words); + // Pick a word + $key = array_rand($words, 1); + $answer = $words[$key]; + // Replace another word with it + while (($pos = array_rand($words, 1)) == $key) { + // NOP aka NOOP aka pass + } + array_splice($words, $pos, 1, $answer); + $description = t('Which word occurs two times in the CAPTCHA phrase above?'); + return array($words, $description, $answer); +}