Here is the working module code I am testing with:
/**
* #file myform.module
*/
/**
* Implements hook_menu().
*/
function myform_menu() {
$items['myform'] = array(
'title' => 'myform',
'page callback' => 'drupal_get_form',
'page arguments' => array('myform'),
'access callback' => true,
'type' => MENU_NORMAL_ITEM
);
return $items;
}
/**
* Form
*/
function myform() {
$form['Value'] = array(
'#title' => t('Value'),
'#type' => 'textfield',
'#description' => t('You may not enter the same value twice. (unless you hit enter really fast).'),
'#required' => true,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit')
);
return $form;
}
/**
* Validate
*/
function myform_validate($form, &$form_state) {
if (isset($form_state['values']['Value']) && trim($form_state['values']['Value'])!=''){
// prevent duplicates
db_set_active('test');
$r = db_query("SELECT id FROM test WHERE value = '".$form_state['values']['Value']."'");
$n = $r->rowCount();
if ($n) {
form_set_error('Value', t('This value has already been submitted.'));
}
db_set_active();
}
}
/**
* Submit
*/
function myform_submit($form, &$form_state) {
for ($i=0; $i<=10000000; $i++) {
// do nothing
}
db_set_active('test');
db_insert('test')->fields(array('value'=>$form_state['values']['Value']))->execute();
db_set_active();
}
The validation hook prevents duplicate values from being inserted, unless I hit the enter key or submit button really fast in which case the same value is inserted into the database multiple times.
How do I prevent duplicate values from being inserted?
If you mean users being accidentally clicking the submit button more than once, then you should look into Hide submit button module. You can define it as dependency in your module's INFO file.
I had the exact same problem and managed to fix it using the Locking mechanisms from Drupal
In the validate function I used:
function mymodule_custom_form_validate($form, &$form_state){
if (lock_acquire('your_custom_lock_name')) {
// long operations here
} else {
form_set_error("", t("You submitted this form already."));
}
}
And in the submit function I released the lock:
function mymodule_custom_form_submit($form, &$form_state){
// submit code
lock_release('your_custom_lock_name');
}
Related
On direct URL hit it showing the result I want not to done it but when I give permission to it it should be work fine. Help me regarding this I will be very grateful to you.
This is my module provide me the code for that module:
<?php
// $Id: person.module
/**
* implements hook_menu()
*/
function person_menu(){
$items = array();
$items['person'] = array(
'title' => "Person",
'page callback' => "perso_personal_info", // after visit drupal6/person, person_personal_info() function is called
'access callback' => true, // must return true, otherwise it will not visible as menu item
'type' => MENU_NORMAL_ITEM, // drupal's default menu type
'weight' => '10', // we want to display person link below in our nav menu
);
return $items; // finally, do not forget to return $items array
}
function perso_personal_info(){
$output = 'Name: Gaurav</br>';
$output .= 'City: nanital </br>';
$output .= 'Country: india </br>';
return $output;
}
function person_permission(){
return array(
'administer my module' => array(
'title' => t('Administer my module'),
'description' => t('Perform administration tasks for my module.'),
),
); }
?>
Please provide me the the code needed; it should work fine when I set the permission for my module.
You need to update "access callback" in your hook_menu in which users permission will be checked as below:
/**
* implements hook_menu()
*/
function person_menu() {
$items = array();
$items['person'] = array(
'title' => "Person",
'page callback' => "demo_custom_personal_info", // after visit drupal6/person, person_personal_info() function is called
//'access callback' => true, // must return true, otherwise it will not visible as menu item
'access callback' => 'person_personal_info_check_access',
'type' => MENU_NORMAL_ITEM, // drupal's default menu type
'weight' => '10', // we want to display person link below in our nav menu
);
return $items; // finally, do not forget to return $items array
}
Now you will need to add below function in you module file (this function will check user's access permission which will assign from permission page)
/**
* To check user's permission
*/
function person_personal_info_check_access() {
if (user_access('administer my module')) {
return TRUE;
}
return FALSE;
}
I have made a form in D7 using form API, registered some users and retrieved their registered data from database;now what i want is to add an edit and a delete link in front of every row in the retrieved table;i have done that in PHP but how to implement same in Drupal?;anybody having any idea how to do that?
My retrieval code is:
function form_data_menu() {
$items['formdata'] = array(
'title' => 'Form Data',
'page callback' => 'form_data_form',
'access callback' => TRUE,
);
return $items;
}
function form_data_form()
{
$results = db_query('SELECT * FROM {drupal}');
$header = array(t('Id'),t('Name'),t('College'),t('Education'),t('Percentage'),t('Application'));
$rows = array();
foreach($results as $result) {
$rows[] = array(
$result->id,
$result->name,
$result->college,
$result->education,
$result->percentage,
$result->application,
);
}
return theme('table',array('header'=>$header,'rows'=>$rows));
}
I can add links though but the main problem is how to run update and delete queries in D7?
Any help will be appreciated. Thank you!
Start with the example module "dbtng_example.module".
https://www.drupal.org/project/examples
This will give an example of how to set up a module to save to the database. I used this recently to create a custom module to save data to a new database table within drupal. To figure out how to set up the hook_schema, you can look at ".install" files within the drupal core and contributed codebase.
Then to create an edit or delete link, you need to alter your hook_menu to pass in the unique id for that element:
$items['admin/structure/nates-custom-page/update/%'] = array(
'title' => 'Update entry',
'page callback' => 'drupal_get_form',
'page arguments' => array('nates_module_form_update', 5),
'weight' => -5,
'access arguments' => array('administer DFP'),
);
$items['admin/structure/nates-custom-page/delete/%'] = array(
'title' => 'Delete entry',
'page callback' => 'drupal_get_form',
'page arguments' => array('nates_module_form_delete', 5),
'access arguments' => array('administer DFP'),
);
Above is an example where I added the argument (indicated by the percentage sign in the array key, ("%"), that will represent the unique id for that item I want to edit or delete.
Then alter the edit form to load the item you want to edit by the id.
And add a delete form.
Here's an example of a delete form:
function nates_module_form_delete($form, &$form_state) {
$entry = nates_module_entry_load_by_pid(arg(5));
$form = array();
$form['pid'] = array(
'#type' => 'value',
'#value' => arg(5),
);
$output = "";
foreach($entry as $key => $value) {
$output .= "<p><strong>$key</strong>: $value</p>";
}
$form['markup'] = array(
'#type' => 'markup',
'#markup' => $output,
);
return confirm_form(
$form,
t('Are you sure you want to delete this item? '),
'example/list',
t('This action cannot be undone.'),
t('Delete'),
t('Cancel')
);
return $form;
}
Notice I return a confirmation form first.
And here's the submit handler function that processes it after they submit the request:
function nates_module_form_delete_submit($form, &$form_state) {
global $user;
nates_module_entry_delete($form_state['values']['pid']);
drupal_set_message(t("Deleted entry"));
}
Then, edit your listing page to add the edit links and delete links:
function nates_module_list() {
$output = '';
// Get all entries in the dfp_amobee2 table.
if ($entries = nates_module_entry_load_all()) {
$rows = array();
foreach ($entries as $entry) {
// Sanitize the data before handing it off to the theme layer.
$entry->editlink = l('edit','admin/structure/nates-custom-page/update/' . $entry->pid, array('query' => array('destination' => 'admin/structure/nates-custom-page')));
$entry->deletelink = l('delete','admin/structure/nates-custom-page/delete/' . $entry->pid, array('query' => array('destination' => 'admin/structure/nates-custom-page')));
unset($entry->pid);
if(!empty($entry->tid)) {
$entry->alias = l($entry->alias, 'taxonomy/term/'.$entry->tid);
}
else if(isset($entry->pages)) {
if(drupal_valid_path($entry->pages)) {
$entry->alias = l(drupal_lookup_path('alias', $entry->pages),$entry->pages);
} else {
$entry->alias = check_markup($entry->pages);
}
} else {
$entry->alias = "";
}
unset($entry->pages);
if(!$entry->tid) {
$entry->tid = "";
}
$rows[] = (array) $entry;
}
// Make a table for them.
$header = array(t('path'), t('tid'), t('dfp tag machine_name'),t('amobee_as'), '', '');
$output .= '<p>' . t('If the "tid" is filled in, amobee_as will be used on taxonomy pages and on node pages (where the main term id matches). If the path is filled it, will be used on that path. Path supercedes "tid" when there is a conflict.') . '</p>';
$output .= theme('table', array('header' => $header, 'rows' => $rows));
}
else {
drupal_set_message(t('No entries have been added yet.'));
}
return $output;
}
I'm trying to learn to write drupal modules and am working on writing a simple math module http://nodeone.se/en/the-math-question-module
I've got the GUI down fine but seem to be having issues with the actual math. I never seem to get the correct answer because I think it loads a new set of questions before it checks the current answer.
Here's what I have so far:
<?php
/**
* #file
* Tests users on their math skills through a series of question and answers
*/
/**
* Implements hook_menu().
*/
function math_question_menu() {
// add new navigation menu item
$items['math_question'] = array(
'title' => 'Math questions',
'description' => 'Test you math skills with these questions.',
'page callback' => 'drupal_get_form',
'page arguments' => array('math_question_page'),
'access callback' => 'user_access',
'access arguments' => array('administer_site_configuration'),
);
return $items;
}
$val1 = rand(1,10);
$val2 = rand(1,10);
variable_set('val1', $val1);
variable_set('val2', $val2);
/**
* Builds the form for configuring Math Questions.
*/
function math_question_page() {
global $user;
$num1 = variable_get('val1');
$num2 = variable_get('val2');
$total = $num1 + $num2;
variable_set('total',$total);
$form['math_question'] = array(
'#type' => 'item',
'#markup' => 'What is ' . $num1 . ' + ' . $num2 . ', ' .
check_plain($user->name) . '?',
);
$form['answer'] = array(
'#type' => 'textfield',
'#title' => t('Answer'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Check my answer'),
);
return $form;
}
function math_question_page_validate($form, $form_state) {
if (empty($form['answer']['#value'])) {
form_error($form['answer'], t('This field is required.'));
}
if ($form['answer']['#value'] ==
variable_get('total')) {
$msg = t('Good job!');
$type = 'status';
}
else {
$msg = t('Try again...');
$type = 'error';
}
drupal_set_message(check_plain($msg), $type);
}
modified code:
.
.
/**
* Builds the form for configuring Math Questions.
*/
function math_question_page() {
global $user;
$val1 = rand(1,10);
$val2 = rand(1,10);
$total = $val1 + $val2;
$form['math_question'] = array(
'#type' => 'item',
'#markup' => 'What is ' . $val1 . ' + ' . $val2 . ', ' .
check_plain($user->name) . '?',
);
$form['answer'] = array(
'#type' => 'textfield',
'#title' => t('Answer'),
);
$form['total'] = array(
'#type' => 'hidden',
'#value' => $total,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Check my answer'),
);
return $form;
}
function math_question_page_validate($form, $form_state) {
if (empty($form['answer']['#value'])) {
form_error($form['answer'], t('This field is required.'));
}
if ($form['answer']['#value'] ==
$form['total']['#value']) {
$msg = t('Good job!');
$type = 'status';
}
else {
$msg = t('Try again...');
$type = 'error';
}
drupal_set_message(check_plain($msg), $type);
}
In the validation function math_question_page_validate(), you should access the values from $form_state variable, not from $form.
eg:
if (!$form_state['values']['answer'] == $form_state['values']['total']) {
form_set_error('answer', $error);
}
You can also store the answer in $form_state variable to pass it to the validation function (no need to store the value in $form). if you store it as $form_state['total'], you can access it in the same way (given below).
if (!$form_state['total'] == $form_state['values']['answer']) {form_set_error('answer', $error);}
Thank you very much papirrin for teaching this.
I suggest to use variable_get() to keep the correct answer safe. The random value you keep in the form will not survive the form regeneration.
My working code here.
/**
* Builds the form for configuring Math Questions.
*/
function math_question_page($form, &$form_state) {
global $user;
$val1 = rand(1,10);
$val2 = rand(1,10);
$total = $val1 + $val2;
// Store the correct answer safely away from this form
if (!variable_get('math_question_answer',FALSE)) {
variable_set('math_question_answer', $total);
}
$form['math_question'] = array(
'#type' => 'item',
'#markup' => 'What is ' . $val1 . ' + ' . $val2 . ', ' .
check_plain($user->name) . '?',
);
$form['answer'] = array(
'#type' => 'textfield',
'#title' => t('Answer'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Check my answer'),
);
$form_state['redirect'] = FALSE;
return $form;
}
function math_question_page_submit($form, &$form_state) {
if (empty($form['answer']['#value'])) {
form_set_error('answer', t('This field is required.'));
}
else {
if ($form_state['values']['answer'] == variable_get('math_question_answer')) {
$msg = t('Good job!');
$type = 'status';
}
else {
$msg = t('Try again...');
$type = 'error';
}
drupal_set_message(check_plain($msg), $type);
}
variable_del('math_question_answer');
}
Drupal redirects after form submission so your form fields (altogether with your form) are recreated before the validation is called. See details about that in another question.
Your form is created with math_question_page, which does not take any form_state arguments. If you had form_state, then you could detect that math_question_page has not been submitted yet and just keep the state of the last call (last random values).
In order to call math_question_page with a form_state argument, you can use drupal_build_form instead of drupal_get_form in your menu.
Instead of storing the $total in the global variable 'total', try storing it as a hidden field on your form (unless you are worried about your users cheating). You can write:
'#type' => 'hidden,
to create a hidden 'total' field and then check for that value in validate so prevent total to be overwritten.
I am learning to write drupal modules too (Started the tutorial series 2 weeks ago).
I tried to do this same module , and i think that the implementation which saves the random values in the database is NOT correct. The first thing asked in the exercise is this :
Any user visiting the /question path will get a simple add-two-numbers question, such as "What is 4 + 9?".
So acces arguments for the page should be 'access arguments' => array('access content') not 'access arguments' => array('administer_site_configuration').
This comes with the following issue : If there are 2 (or more) users browsing at the same moment this page , the latest user overwrites the values in the database , so the other users won't be able to complete the "question" because his answer will not be equal to the answer in the database. Saving values to database is NOT a option in this case...
Meantime I am trying to make it work without the database , but i have the same situation as this question's starter , the form overwrites the values when i submit it (so , error thrown because the answer is wrong ).
If i'll manage to complete it i'll post the code here , or if someone else managed to complete it without variable_set() and variable_get() , i ask them to post the code here :)
I am developing simple guestbook module for one of my customers and as a spam prevention I decided to use an array of simple questions. For this I have to pick random question from the array and set the ID of this question (array element ID) in form so I can check the answer in submit handler. The problem is that the form is regenerated before the submit handler is evaluated so the random value changes.
Shortly: I am getting other value of $form_state['values']['queston_id'] in submit handler function that it is in form. Why is that and how can I change this?
Thanks a lot!
This is my module:
function gb_menu() {
$items = array();
$items['gb'] = array(
'title' => t('Guestbook'),
'description' => t('Guestbook page'),
'page callback' => 'gb_guestbook',
//'page arguments' => array('gb_guestbook'),
'access arguments' => array('view guestbook'),
);
return $items;
}
function gb_guestbook() {
dpm('generating page');
$page = NULL;
$page .= drupal_render(drupal_get_form('gb_guestbook_form'));
$page .= 'list of guestbook messages here';
return $page;
}
function gb_guestbook_form($form, &$form_state) {
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#description' => t('Please, enter your name.'),
);
$form['message'] = array(
'#type' => 'textarea',
'#cols' => 5,
'#title' => t('Message'),
'#description' => t('Please, enter your message.'),
);
$questions = gb_get_question();
$question_id = rand(0, count($questions)-1);
$question = $questions[$question_id];
$form['question_id'] = array(
'#type' => 'hidden',
'#value' => $question_id,
);
$form['spam'] = array(
'#title' => $question['question'],
'#description' => t('Please, answer the simple question so we know that you are a human.'),
'#type' => 'textfield',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function gb_guestbook_form_submit($form, &$form_state) {
// spam check
$values = $form_state['values'];
$questions = gb_get_question();
$answers = $questions[$values['question_id']]['answers'];
if (!in_array(strtolower($values['spam']), $answers)) {
drupal_set_message(t('You did not reply the answer correctly. Are you sure it was not typo?'), 'error');
}
else {
drupal_set_message(t('Thanks for the contribution!'), 'status');
// processing input
}
}
function gb_get_question() {
return array(
array (
'question' => t('What is the capital of Slovakia?'),
'answers' => array('bratislava'),
),
array (
'question' => t('What is the name of this band?'),
'answers'=> array('divozel'),
),
array (
'question' => t('What is the biggest town in east part of Slovakia?'),
'answers' => array('košice','kosice'),
),
);
}
You should probably do this check in gb_guestbook_form_VALIDATE instead.
I'd do this in a validation hook. Also another issue in the function gb_guestbook_form_submit
You get the questions but not the answers array and then you are trying to compare them. This could also be the source of your issue, you capture questions but not answers and then compare to the empty array of answers.
Wrap your random question generator in a conditional statement that checks if your question has been defined in the form yet.
$question_id = NULL;
$questions = gb_get_question();
if (!isset($form['question_id'])) {
$question_id = rand(0, count($questions)-1);
}
else {
$question_id = $form['question_id']['#value'];
}
$question = $questions[$question_id];
Not sure why my other answer isn't working for you, but here's another approach. The idea would be to store the question id in $form_state['storage'].
First, correct your form declaration so it passes in $form_state as a reference.
function gb_guestbook_form($form, &$form_state) {
Now check the $form_state['storage'] array for your question id, and set it if you haven't already, before you set the form value question_id.
$questions = gb_get_question();
if (!isset($form_state['storage']['question_id'])) {
$form_state['storage']['question_id'] = array_rand($questions);
}
$form['question_id'] = array(
'#type' => 'hidden',
'#value' => $form_state['storage']['question_id'],
);
Your problem (and mine) is describe here:
http://www.sparklepod.com/myblog/drupal-form-same-form-for-build-and-validation/
In short. On submit the form is rebuild before the validation code is run. For me the solutions in the blog is not working for my Drupal 7 site.
What I have done is added the following line to the forum build:
$form_state['cache'] = true;
That will cache the form and the on _submit I will set it to false.
I want to create an auto-complete form in my custom module that will be loaded in a block. Drupal doesn't seem to be loading the necessary Javascript libraries to work properly. How do I know what needs to be loaded and how/where do I tell Drupal to load these libraries?
hook_block_view:
function my_module_block_view($delta = '') {
//The $delta parameter tells us which block is being reqested.
switch ($delta) {
case 'my_module_my_block':
$block['subject'] = t('Block Subject');
$block['content'] = drupal_get_form('my_module_my_form');
break;
}
return $block;
}
Form code:
function my_module_my_form($form, &$form_state) {
$form = array();
$form['term'] = array(
'#type' => 'textfield',
'#autocomplete_path' => 'my-module-autocomplete'
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Add',
);
return $form;
}
The form loads, the field is there, but auto-complete isn't working :(
If I call the my-module-autocomplete path I do get a valid response back when compared with a Content Type edit form. The ajax spinner in the input field never appears so the ajax isn't being called. Realistically all I want is the autocomplete field...the submit will be handled manually.
It's probably because you're reseting $form to an empty array at the beginning of the function. In Drupal 7 there's a bunch of stuff added to that element before it's passed through to your form function (that's why $form is passed to your function whereas in Drupal 6 it wasn't).
Just remove $form = array(); and it should work, other than that your code looks perfect.
the following should work;
function mymodule_block_info() {
$blocks['mymodule'] = array(
// The name that will appear in the block list.
'info' => t('My Module'),
// Default setting.
'cache' => DRUPAL_NO_CACHE,
);
return $blocks;
}
function mymodule_block_view($delta = ''){
switch($delta){
case 'mymodule':
if(user_access('access content')){ //good idea to check user perms here
$block['subject'] = t('My Module');
$block['content'] = 'Hi :)';
$block['content'] = drupal_get_form('mymodule_form');
return $block;
}
break;
}
}
function mydmodule_menu() {
$items['module/autocomplete'] = array(
'page callback' => 'mymodule_autocomplete',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK
);
return $items;
}
function mymodule_form($form, &$form_state) {
$form['greenentry'] = array(
'#type' => 'textfield',
'#title' => t('Enter'),
'#autocomplete_path' => 'mymodule/autocomplete',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function mymodule_autocomplete($string) {
$matches = array();
// Some fantasy DB table which holds cities
$query = db_select('cities', 'c');
// Select rows that match the string
$return = $query
->fields('c', array('city'))
->condition('c.city', '%' . db_like($string) . '%', 'LIKE')
->range(0, 10)
->execute();
// add matches to $matches
foreach ($return as $row) {
$matches[$row->url] = check_plain($row->url);
}
// return for JS
drupal_json_output($matches);
}
this code is so pretty to add an auto-complete filed in block. But i just found a tiny notice here. if someone get an error
An ajax error occurred. http result code 200
then just add
exit();
after the line
drupal_json_output($matches);
hence fix the issue.