In a custom module I want to have a page defined in hook_menu, that shows the add form for a specific content type, with some modifications to the form.
So far it's working, and even saving the new node, but only with the default values I'm setting in the code, i.e. it's not picking up anything the user types into the form. I checked and $form_state['input'] contains the inputted values, but $form_state['values'] doesn't, so the new node gets saved wrong.
Here's the relevant code:
function mymodule_menu() {
return array(
'admin/content/myadd/%' => array(
'title' => 'my custom add page',
'page callback' => 'mymodule_node_add',
'page arguments' => array(3),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
),
);
}
function mymodule_node_add() {
module_load_include('inc', 'node', 'node.pages');
//I'm doing a print here instead of returning because I'm calling this page
//in an AJAX popup, so I don't want the whole page to output, only the form.
print render(drupal_get_form('mymodule_node_add_form'));
}
function mymodule_node_add_form($form, &$form_state) {
if (!isset($form_state['node']) {
global $user;
$node = (object) array(
'uid' => $user->uid,
'type' => 'mycontenttype',
'language' => LANGUAGE_NONE,
);
//this is setting a default value
$node->myfield = array(LANGUAGE_NONE => array(array('value' => arg(3))));
$form_state['build_info']['args'] = array($node);
$form = drupal_build_form('mycontenttype_node_form', $form_state);
$form['actions']['submit']['#submit'][0] = 'mymodule_node_add_form_submit';
//there's a lot more customization of the form here, like adding fields, etc.
}
return $form;
}
function mymodule_node_add_form_submit($form, &$form_state) {
//here's where $form_state['input'] is correct but $form_state['values'] isn't.
$node = node_form_submit_build_node($form, $form_state);
node_save($node);
$form_state['values']['nid'] = $node->nid;
$form_state['nid'] = $node->nid;
$form_state['redirect'] = 'some/other/page';
}
So, am I doing something wrong here? Should I be concerned about form ids being wrong? (my form's id is mymodule_node_add_form, but the actual form might output mycontenttype_node_form), would this affect me?
You want hook_form_alter() (see api.drupal.org). I would try to use the existing content type's form and simply alter it with hook_form_alter(). I would also try to first get it working as a standard, non-AJAX page, so you can get all the advantages of dpm() and other debugging techniques. When you have it down solid, then modify it to take advantage of the AJAX techniques.
mymodule_form_alter(&$form, &$form_state, $form_id) {
// use this with your devel module turned on to verify
// your $form_id and contents of all forms that load on a given page
dpm($form);
// once you verify your $form_id, you can begin accessing your form and altering it
switch( $form_id ) {
case 'my_target_form_id' :
// this part is just pseudocode, I haven't memorized the $form structure,
// you can get it from your dpm().
if( $form['node']->type == 'my_target_content_type' ) {
$form['actions']['submit']['#submit'][0] = 'mymodule_node_add_form_submit';
}
break;
}
}
Related
What I need to accomplish is adding a link to a node to field widgets (the node contains the formatted instructions to properly compile the field).
So far I've been able to add the node reference field in the field config form, using Third Party Settings:
function mymodule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if ($form_id === 'field_config_edit_form' && $form_state->getFormObject()->getEntity()->get('entity_type') == 'myentity') {
$field = $form_state->getFormObject()->getEntity();
$help_page_id = $field->getThirdPartySetting('mymodule', 'help_page_id');
$form['help_page_id'] = array(
'#type' => 'entity_autocomplete',
'#title' => t('Help page'),
'#target_type' => 'node',
'#selection_handler' => 'default',
'#selection_settings' => array(
'target_bundles' => array('help_page'),
),
'#default_value' => $help_page_id ? Node::load($help_page_id) : NULL,
'#weight' => 100,
);
$form['#entity_builders'][] = 'mymodule_form_field_config_edit_form_builder';
}
}
Now I'm having troubles retrieving this information when showing the form, and altering the field widget in order to display the link.
Using hook_field_widget_form_alter, I cannot get the custom configuration value from the arguments I have:
function mymodule_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
}
I guess I have to load the field configuration entity, but I don't know how.
I didn't test it, but something like this should work:
function mymodule_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
// Add a suffix to widget form elements for all fields of type entity_reference.
$field_definition = $context['items']->getFieldDefinition();
if ($field_definition->getType() == 'entity_reference') {
// TODO: Render the node link to $link
$element['#suffix'] = $link;
}
}
I've added a custom content type, "Property" (as in, a building). I've added a text field to this content type. At /admin/structure/types/manage/property/fields, this field is displayed as:
Label: Property Region
Machine Name: field_property_region
Field Type: Text
Widget: Text field
When an admin user goes to add a new Property, I'd like this field to display autocomplete suggestions using existing values from other Property nodes. However, no autocomplete circle is displayed in the text input when I load the form page, and no suggestions are displayed when input is entered.
Here's what I've done (in modules/custom/hr_misc.module):
function hr_misc_form_alter(&$form, &$form_state, $form_id)
{
if($form_id == "property_node_form"){
$form['field_property_region']['#autocomplete_path'] = 'hr_misc/autocomplete/property_regions';
dpm($form['field_property_region']);
}
}
When I load the form (at /node/add/property), the dpm() debug output is displayed and includes the #autocomplete_path value as expected.
function hr_misc_menu() {
$items['hr_misc/autocomplete/property_regions/%'] = array(
'page callback' => '_hr_misc_autocomplete_property_regions',
'access callback' => TRUE,
'page arguments' => array(3),
'weight' => 1,
'type' => MENU_CALLBACK,
);
return $items;
}
Most of the examples I've seen don't include the wildcard or passing page_arguments. One did, and it makes sense to me that it should, so I've included it. I've tried both approaches. If I visit
http://mysite/hr_misc/autocomplete/property_regions/Lond
I see the JSON output:
{"London":"London"}
The JSON is produced by the following function:
function _hr_misc_autocomplete_property_regions($string)
{
$query = new EntityFieldQuery();
$entities = $query->entityCondition('entity_type', 'node')
->propertyCondition('type', 'property')
->fieldCondition('field_property_region', 'value', '%'. $string.'%', 'LIKE');
$result = $query->execute();
$nodes = $result['node'];
// Get all fields attached to a given node type
$fields = field_info_instances('node', 'property');
// Get id of body field
$field_id = $fields['field_property_region']['field_id'];
// Attach a field of selected id only to get value for it
field_attach_load('node', $nodes, FIELD_LOAD_CURRENT, array('field_id' => $field_id));
$items = array();
foreach ($nodes AS $nid => $node) {
$region = $node->field_property_region[LANGUAGE_NONE][0]['value'];
$items[$region] = $region;
}
print drupal_json_output($items);
exit();
}
I'm at a loss. There are loads of articles on adding autocomplete fields, and they all seem to say that this is all you need. Do I need to add something else? The "author" field of my node autocompletes just fine, and I'm not seeing any errors (JS or PHP).
I have a requirement where i need to show webform in ctool modal. When user submit the webform it should show the confirmation message in the same ctool modal. This functionality is being used in my project at several places just the webform to show in ctool modal is changing. I googled it and found this http://drupal.org/node/1196150. The code i found here helped me now i am able to display the webform in ctool modal and form working very well but the confirmation message is not showing in ctool modal. After submission it show the same form in modal rather then confirmation message. This code attache the custom submit handler with form in hook_form_alter and in this submit handler it set the sessoin variable. On this session variable it make decision whether to show form or confirmation message.
here is code
function webform_modal_menu() {
$items['service-page'] = array(
'title' => 'Service page',
'access callback' => TRUE,
'page callback' => 'webform_modal_page',
);
$items['modal/%ctools_js/service-webform'] = array(
'title' => t("service webform"),
'page callback' => 'webform_modal_content',
'file' => 'webform_modal.inc',
'page arguments' => array(1),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
function webform_modal_page() {
ctools_include('ajax'); // Module include the dependence it needs for ajax.
ctools_include('modal');
ctools_modal_add_js();
$output = ctools_modal_text_button(t('Click Here'), 'modal/nojs/service-webform', t('Pop me up'));
$output .= '<div id="modal-message"> </div>';
ctools_include('plugins');
return $output;
}
function webform_modal_content($js = NULL) {
define('WEBFORM_NID', 14);
$webform_nid = WEBFORM_NID; // Your webform $nid
return ctools_ajax_modal_webform($js, $webform_nid);
}
function webform_modal_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'webform_client_form_14') {
$form['#submit'][2] = '_custom_webform_submit';
}
function _custom_webform_submit($form, &$form_state) {
$webform_nid = $form['#node']->nid;
lists_session('webform_client_form_'.$webform_nid, $value = 'submitted');
}
function ctools_ajax_modal_webform($js = NULL, $webform_nid = NULL) {
$node = node_load($webform_nid); // Load webform node
$submission = (object) array(); // empty object required by webform
// React without the modal
if (!$js) {
// Webform requires more parameters than standard forms
return drupal_get_form('webform_client_form_'.$webform_nid, $node, $submission);
}
// React with the modal
// Add modal components
ctools_include('modal');
ctools_include('ajax');
$form_state = array(
'title' => $node->title,
'ajax' => TRUE,
);
// Emulate ctools_modal_form_wrapper() form modal.inc because webform can not be triggered trough drupal_build_form
// If it can, I'd be glad to understand how
$form_state += array(
're_render' => TRUE,
'no_redirect' => !empty($form_state['ajax']),
);
//drupal_get_form('webform_client_form_'.$webform_nid, $node, $submission);
$args = array($node, $submission);
$form_state['build_info']['args'] = $args;
$output = ctools_modal_form_wrapper('webform_client_form_'.$webform_nid, $form_state);
if ( isset($_SESSION['lists']['webform_client_form_'.$webform_nid]) && $_SESSION['lists']['webform_client_form_'.$webform_nid] == 'submitted') {
unset($_SESSION['lists']['webform_client_form_'.$webform_nid]);
$confirm_message = array("#markup" => '<div> Your request has been submitted</div>');
$confirmation['#markup'] =
'<div class="popups-confirmation-wrapper">'.
drupal_render($confirm_message).
'</div>';
$output = array(); // Recreate output
// Oerwrite the form output if it was successful.
$output[] = ctools_modal_command_display('Confirmation', $confirmation);
}
// Render output in modal window
print ajax_render($output);
exit;
}
function lists_session($key, $value = NULL) {
static $storage;
if ($value) {
$storage[$key] = $value ;
$_SESSION['lists'][$key] = $value ; // I put 'lists' in case some other module uses 'type' in $_SESSION
}
else if (empty($storage[$key]) && isset($_SESSION['lists'][$key])) {
$storage[$key] = $_SESSION['lists'][$key];
}
return $storage[$key];
}
When cache is cleared i directly access modal/nojs/service-webform menu item it show the form and hook_form_alter is called. i confirmed this printing some message in form_alter after directly accessing modal/nojs/service-webform now if i access this url using 'Click here' button which i created on serice-page then it also show that form alter is called and print the message on console(because i am using chromephp for logging.). But if cache is cleared and i click on 'Click here' then form is shown in modal but form_alter is not called because it doesn't print any message in console. After clicking on that button if i access modal/nojs/service-webform directly then also form_alter is not being called. So problem is that custom submit handler is not being called. i confirmed this printing $form variable in form alter. This may make more clear http://drupal.org/node/1196150#comment-6458176 Is there any cache issue or i am making some mistake?
I created a small module for altering forms called "form_mods". The form I'm altering is the "user_profile_form". I added a category for extra fields called "Profile".
I created a select field in the Drupal admin called "profile_state" and I'm altering it in my module to have a key => value list of states and It's working for me when logged in as an admin but an anonymous user that's trying to register sees an empty states select field. Is there a permissions issue here? I tried to add 'access' => user_access('access content') to the field but that didn't work. Here is my code:
function form_mods_form_alter($form, $form_state, $form_id) {
switch ($form_id) {
## user_profile_form ###################################################################################
case 'user_profile_form': // This is our form ID.
//echo '###'.$form['_category']['#value'].'###';
if($form['_category']['#value'] == 'Profile'){
// Modify the states dropdown list
$states = load_states_list();
$form['Profile']['profile_state'] = array(
'#type' => 'select',
'#title' => t('State'),
'#options' => $states,
'#required' => TRUE,
'#default_value' => isset($form['Profile']['profile_state']['#default_value']) ? $form['Profile']['profile_state']['#default_value'] : '',
'#element_validate' => array('profile_state_validate')
);
}
####################################################################################
break;
}
}
function load_states_list() {
$states = array('' => '-- Select a state'); // add a default empty value
$results = db_query("SELECT * FROM {states_list} ORDER BY name ASC");
while ($state = db_fetch_array($results)) {
$states[$state['code']] = $state['name'];
}
return $states;
}
Thanks
First of all, are you sure you're function ever gets run? Having the function named the way you do, I don't think it is. If the module name is "form_mods", your function should be called
function form_mods_form_alter
Or...since you're only modifying the user_profile_form form, you could use the function name
function form_mods_user_profile_form_alter
Now, the reason it isn't working is because you don't have the & in front of the $form in the parameter list. The & basically passes the variable as reference, and so any changes you make to the $form variable will be saved and passed back to the calling function. So, your function should look like
function form_mods_form_alter(&$form, &$form_state, $form_id) {
OR
function form_mods_user_profile_form_alter(&$form, &$form_state) {
Reference: http://api.drupal.org/api/drupal/developer--hooks--core.php/function/hook_form_alter/6
Thank you mikesir87 for the reference link. I figured out my problem.
There are 2 different forms that are using those fields I created. They have different form id's. I have to look for "user_profile_form" and "user_register" form id's.
Here is my new code that works:
function form_mods_form_alter($form, $form_state, $form_id) {
if( (($form_id == 'user_profile_form') && ($form['_category']['#value'] == 'Profile')) || ($form_id == 'user_register') ){
// Modify the states dropdown list
$states = form_mods_load_states_list();
$form['Profile']['profile_state'] = array(
'#type' => 'select',
'#title' => t('State'),
'#options' => $states,
'#required' => TRUE,
'#default_value' => isset($form['Profile']['profile_state']['#default_value']) ? $form['Profile']['profile_state']['#default_value'] : ''
);
}
}
thanks
I am using hook form_alter and setting the $form['#redirect'] = 'dir1/dir2/mypage'. But the form refuses to go there.
Form seems to work otherwise, but keeps sending back to the original form instead of the redirect.
The form I am altering is from the root user module.
mymodule_form_alter( ){
... code...
$form['account']['pass'] = array(
'#type' => 'password_confirm',
'#size' => 25, '#description' => t(''),
'#required' => TRUE
);
unset($form['Birthdate']['profile_birthdate']);
unset($form['Birthdate']);
unset($form['#action']);
$form['#validate'] = array('_mymodule_school_registration_validate');
$form['#submit'] = array( '_mymodule_school_registration_submit');
$form['#redirect']= "dir1/dir2/mypage";
}
Please help trying to meet an overdue dead line!! : (
Thanks in advance.
Your hook_form_alter() implementation is not correct:
Without parameters, you're aren't modifying anything, so none of your changes get registered,
$form['#submit'] and $form['#validate'] are already arrays with content, so you should not be resetting them with array(),
unsetting $form['#action'] causes the form to do nothing when submitted,
setting $form['#redirect'] in hook_form_alter() will get overridden by other handlers, and
your hook_form_alter() implementation would affect (and break) every form.
More info: Forms API Reference
Instead, try the following:
function mymodule_form_alter(&$form, $form_state, $form_id) {
if ($form_id === 'form_id_goes_here') {
// Code here
$form['account']['pass'] = array(
'#type' => 'password_confirm',
'#size' => 25,
'#description' => t(''),
'#required' => TRUE
);
unset($form['Birthdate']['profile_birthdate']);
unset($form['Birthdate']);
$form['#validate'][] = '_mymodule_school_registration_validate';
$form['#submit'][] = '_mymodule_school_registration_submit';
}
}
function _mymodule_school_registration_submit($form, &$form_state) {
// Code here
$form_state['redirect'] = 'dir1/dir2/mypage';
}
Try
$form_state['redirect'] = "dir1/dir2/mypage";
If you've only got one handler for your submit you could easily redirect with
function _mymodule_school_registration_submit(..args...) {
...
drupal_goto('somewhere');
}
I think the functionality is the same.
http://api.drupal.org/api/function/drupal_goto/6
I tend to avoid redirects so once you have met your deadline I would refactor your code. You can usually get away with out redirecting.