updated core to 7.58 (right after the site was hacked)

This commit is contained in:
2018-04-20 23:48:40 +02:00
parent 18f4aba146
commit 9344a61b61
711 changed files with 99690 additions and 480 deletions

View File

@ -0,0 +1,17 @@
/*
* @file
* CSS for ajax_example.
*
* See @link ajax_example_dependent_dropdown_degrades @endlink for
* details on what this file does. It is not used in any other example.
*/
/* Hides the next button when not degrading to non-javascript browser */
html.js .next-button {
display: none;
}
/* Makes the next/choose button align to the right of the select control */
.form-item-dropdown-first, .form-item-question-type-select {
display: inline-block;
}

View File

@ -0,0 +1,12 @@
name = AJAX Example
description = An example module showing how to use Drupal AJAX forms
package = Example modules
core = 7.x
files[] = ajax_example.test
; Information added by Drupal.org packaging script on 2017-01-10
version = "7.x-1.x-dev"
core = "7.x"
project = "examples"
datestamp = "1484076787"

View File

@ -0,0 +1,56 @@
<?php
/**
* @file
* AJAX Examples install file schema for ajax_example_form_node_form_alter()
*/
/**
* Implements hook_schema().
*/
function ajax_example_schema() {
$schema['ajax_example_node_form_alter'] = array(
'description' => 'Stores example settings for nodes.',
'fields' => array(
'nid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The {node}.nid to store settings.',
),
'example_1' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Node Form Example 1 checkbox',
),
'example_2' => array(
'type' => 'varchar',
'length' => 256,
'not null' => FALSE,
'default' => '',
'description' => 'Node Form Example 2 textfield',
),
),
'primary key' => array('nid'),
'foreign keys' => array(
'dnv_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
),
);
return $schema;
}
/**
* Add the new ajax_example_node_form_alter table.
*/
function ajax_example_update_7100() {
if (!db_table_exists('ajax_example_node_form_alter')) {
$schema = ajax_example_schema();
db_create_table('ajax_example_node_form_alter', $schema['ajax_example_node_form_alter']);
return st('Created table ajax_example_node_form_alter');
}
}

View File

@ -0,0 +1,29 @@
/*
* @file
* JavaScript for ajax_example.
*
* See @link ajax_example_dependent_dropdown_degrades @endlink for
* details on what this file does. It is not used in any other example.
*/
(function($) {
// Re-enable form elements that are disabled for non-ajax situations.
Drupal.behaviors.enableFormItemsForAjaxForms = {
attach: function() {
// If ajax is enabled.
if (Drupal.ajax) {
$('.enabled-for-ajax').removeAttr('disabled');
}
// Below is only for the demo case of showing with js turned off.
// It overrides the behavior of the CSS that would normally turn off
// the 'ok' button when JS is enabled. Here, for demonstration purposes,
// we have AJAX disabled but JS turned on, so use this to simulate.
if (!Drupal.ajax) {
$('html.js .next-button').show();
}
}
};
})(jQuery);

View File

@ -0,0 +1,693 @@
<?php
/**
* @file
* AJAX Examples module file with basic examples.
*/
/**
* @defgroup ajax_example Example: AJAX
* @ingroup examples
* @{
* These examples show basic AJAX concepts.
*
* General documentation is available at
* @link ajax AJAX Framework documentation @endlink and at the
* @link http://drupal.org/node/752056 AJAX Forms handbook page @endlink.
*
* The several examples here demonstrate basic AJAX usage.
*/
// The Node Form Alter example needs to be in another file.
module_load_include('inc', 'ajax_example', 'ajax_example_node_form_alter');
/**
* Implements hook_menu().
*
* Sets up calls to drupal_get_form() for all our example cases.
*
* @see menu_example.module
* @see menu_example_menu()
*/
function ajax_example_menu() {
$items = array();
$items['examples/ajax_example'] = array(
'title' => 'AJAX Example',
'page callback' => 'ajax_example_intro',
'access callback' => TRUE,
'expanded' => TRUE,
);
// Change the description of a form element.
$items['examples/ajax_example/simplest'] = array(
'title' => 'Simplest AJAX Example',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_simplest'),
'access callback' => TRUE,
'weight' => 0,
);
// Generate a changing number of checkboxes.
$items['examples/ajax_example/autocheckboxes'] = array(
'title' => 'Generate checkboxes',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_autocheckboxes'),
'access callback' => TRUE,
'weight' => 1,
);
// Generate different textfields based on form state.
$items['examples/ajax_example/autotextfields'] = array(
'title' => 'Generate textfields',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_autotextfields'),
'access callback' => TRUE,
'weight' => 2,
);
// Submit a form without a page reload.
$items['examples/ajax_example/submit_driven_ajax'] = array(
'title' => 'Submit-driven AJAX',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_submit_driven_ajax'),
'access callback' => TRUE,
'weight' => 3,
);
// Repopulate a dropdown based on form state.
$items['examples/ajax_example/dependent_dropdown'] = array(
'title' => 'Dependent dropdown',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_dependent_dropdown'),
'access callback' => TRUE,
'weight' => 4,
);
// Repopulate a dropdown, but this time with graceful degredation.
// See ajax_example_graceful_degradation.inc.
$items['examples/ajax_example/dependent_dropdown_degrades'] = array(
'title' => 'Dependent dropdown (with graceful degradation)',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_dependent_dropdown_degrades'),
'access callback' => TRUE,
'weight' => 5,
'file' => 'ajax_example_graceful_degradation.inc',
);
// The above example as it appears to users with no javascript.
$items['examples/ajax_example/dependent_dropdown_degrades_no_js'] = array(
'title' => 'Dependent dropdown with javascript off',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_dependent_dropdown_degrades', TRUE),
'access callback' => TRUE,
'file' => 'ajax_example_graceful_degradation.inc',
'weight' => 5,
);
// Populate a form section based on input in another element.
$items['examples/ajax_example/dynamic_sections'] = array(
'title' => 'Dynamic Sections (with graceful degradation)',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_dynamic_sections'),
'access callback' => TRUE,
'weight' => 6,
'file' => 'ajax_example_graceful_degradation.inc',
);
// The above example as it appears to users with no javascript.
$items['examples/ajax_example/dynamic_sections_no_js'] = array(
'title' => 'Dynamic Sections w/JS turned off',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_dynamic_sections', TRUE),
'access callback' => TRUE,
'weight' => 6,
'file' => 'ajax_example_graceful_degradation.inc',
);
// A classic multi-step wizard, but with no page reloads.
// See ajax_example_graceful_degradation.inc.
$items['examples/ajax_example/wizard'] = array(
'title' => 'Wizard (with graceful degradation)',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_wizard'),
'access callback' => TRUE,
'file' => 'ajax_example_graceful_degradation.inc',
'weight' => 7,
);
// The above example as it appears to users with no javascript.
$items['examples/ajax_example/wizard_no_js'] = array(
'title' => 'Wizard w/JS turned off',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_wizard', TRUE),
'access callback' => TRUE,
'file' => 'ajax_example_graceful_degradation.inc',
'weight' => 7,
);
// Add-more button that creates additional form elements.
// See ajax_example_graceful_degradation.inc.
$items['examples/ajax_example/add_more'] = array(
'title' => 'Add-more button (with graceful degradation)',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_add_more'),
'access callback' => TRUE,
'file' => 'ajax_example_graceful_degradation.inc',
'weight' => 8,
);
// The above example as it appears to users with no javascript.
$items['examples/ajax_example/add_more_no_js'] = array(
'title' => 'Add-more button w/JS turned off',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_add_more', TRUE),
'access callback' => TRUE,
'file' => 'ajax_example_graceful_degradation.inc',
'weight' => 8,
);
// Use the AJAX framework outside the context of a form using the use-ajax
// class. See ajax_example_misc.inc.
$items['examples/ajax_example/ajax_link'] = array(
'title' => 'Ajax Link ("use-ajax" class)',
'page callback' => 'ajax_example_render_link',
'access callback' => TRUE,
'file' => 'ajax_example_misc.inc',
'weight' => 9,
);
// Use the AJAX framework outside the context of a form using a renderable
// array of type link with the #ajax property. See ajax_example_misc.inc.
$items['examples/ajax_example/ajax_link_renderable'] = array(
'title' => 'Ajax Link (Renderable Array)',
'page callback' => 'ajax_example_render_link_ra',
'access callback' => TRUE,
'file' => 'ajax_example_misc.inc',
'weight' => 9,
);
// A menu callback is required when using ajax outside of the Form API.
$items['ajax_link_callback'] = array(
'page callback' => 'ajax_link_response',
'access callback' => 'user_access',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'ajax_example_misc.inc',
);
// Use AJAX framework commands outside of the #ajax form property.
// See ajax_example_advanced.inc.
$items['examples/ajax_example/advanced_commands'] = array(
'title' => 'AJAX framework commands',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_advanced_commands'),
'access callback' => TRUE,
'file' => 'ajax_example_advanced.inc',
'weight' => 100,
);
// Autocomplete examples.
$items['examples/ajax_example/simple_autocomplete'] = array(
'title' => 'Autocomplete (simple)',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_simple_autocomplete'),
'access arguments' => array('access user profiles'),
'file' => 'ajax_example_autocomplete.inc',
'weight' => 10,
);
$items['examples/ajax_example/simple_user_autocomplete_callback'] = array(
'page callback' => 'ajax_example_simple_user_autocomplete_callback',
'file' => 'ajax_example_autocomplete.inc',
'type' => MENU_CALLBACK,
'access arguments' => array('access user profiles'),
);
$items['examples/ajax_example/node_autocomplete'] = array(
'title' => 'Autocomplete (node with nid)',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_unique_autocomplete'),
'access arguments' => array('access content'),
'file' => 'ajax_example_autocomplete.inc',
'weight' => 11,
);
$items['examples/ajax_example/unique_node_autocomplete_callback'] = array(
'page callback' => 'ajax_example_unique_node_autocomplete_callback',
'file' => 'ajax_example_autocomplete.inc',
'type' => MENU_CALLBACK,
'access arguments' => array('access content'),
);
$items['examples/ajax_example/node_by_author'] = array(
'title' => 'Autocomplete (node limited by author)',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_node_by_author_autocomplete'),
'access callback' => TRUE,
'file' => 'ajax_example_autocomplete.inc',
'weight' => 12,
);
$items['examples/ajax_example/node_by_author_autocomplete'] = array(
'page callback' => 'ajax_example_node_by_author_node_autocomplete_callback',
'file' => 'ajax_example_autocomplete.inc',
'type' => MENU_CALLBACK,
'access arguments' => array('access content'),
);
// This is the landing page for the progress bar example. It uses
// drupal_get_form() in order to build the form.
$items['examples/ajax_example/progressbar'] = array(
'title' => 'Progress bar example',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_example_progressbar_form'),
'access arguments' => array('access content'),
'file' => 'ajax_example_progressbar.inc',
);
// This is the callback route for the AJAX-based progress bar.
$items['examples/ajax_example/progressbar/progress/%'] = array(
'title' => 'Progress bar progress',
'page callback' => 'ajax_example_progressbar_progress',
'page arguments' => array(4),
'type' => MENU_CALLBACK,
'access arguments' => array('access content'),
'file' => 'ajax_example_progressbar.inc',
);
return $items;
}
/**
* A basic introduction page for the ajax_example module.
*/
function ajax_example_intro() {
$markup = t('The AJAX example module provides many examples of AJAX including forms, links, and AJAX commands.');
$list[] = l(t('Simplest AJAX Example'), 'examples/ajax_example/simplest');
$list[] = l(t('Generate checkboxes'), 'examples/ajax_example/autocheckboxes');
$list[] = l(t('Generate textfields'), 'examples/ajax_example/autotextfields');
$list[] = l(t('Submit-driven AJAX'), 'examples/ajax_example/submit_driven_ajax');
$list[] = l(t('Dependent dropdown'), 'examples/ajax_example/dependent_dropdown');
$list[] = l(t('Dependent dropdown (with graceful degradation)'), 'examples/ajax_example/dependent_dropdown_degrades');
$list[] = l(t('Dynamic Sections w/JS turned off'), 'examples/ajax_example/dependent_dropdown_degrades_no_js');
$list[] = l(t('Wizard (with graceful degradation)'), 'examples/ajax_example/wizard');
$list[] = l(t('Wizard w/JS turned off'), 'examples/ajax_example/wizard_no_js');
$list[] = l(t('Add-more button (with graceful degradation)'), 'examples/ajax_example/add_more');
$list[] = l(t('Add-more button w/JS turned off'), 'examples/ajax_example/add_more_no_js');
$list[] = l(t('Ajax Link ("use-ajax" class)'), 'examples/ajax_example/ajax_link');
$list[] = l(t('Ajax Link (Renderable Array)'), 'examples/ajax_example/ajax_link_renderable');
$list[] = l(t('AJAX framework commands'), 'examples/ajax_example/advanced_commands');
$list[] = l(t('Autocomplete (simple)'), 'examples/ajax_example/simple_autocomplete');
$list[] = l(t('Autocomplete (node with nid)'), 'examples/ajax_example/node_autocomplete');
$list[] = l(t('Autocomplete (node limited by author)'), 'examples/ajax_example/node_by_author');
$variables['items'] = $list;
$variables['type'] = 'ul';
$markup .= theme('item_list', $variables);
return $markup;
}
/**
* Basic AJAX callback example.
*
* Simple form whose ajax-enabled 'changethis' member causes a text change
* in the description of the 'replace_textfield' member.
*
* See @link http://drupal.org/node/262422 Form API Tutorial @endlink
*/
function ajax_example_simplest($form, &$form_state) {
$form = array();
$form['changethis'] = array(
'#title' => t("Choose something and explain why"),
'#type' => 'select',
'#options' => array(
'one' => 'one',
'two' => 'two',
'three' => 'three',
),
'#ajax' => array(
// #ajax has two required keys: callback and wrapper.
// 'callback' is a function that will be called when this element changes.
'callback' => 'ajax_example_simplest_callback',
// 'wrapper' is the HTML id of the page element that will be replaced.
'wrapper' => 'replace_textfield_div',
// There are also several optional keys - see ajax_example_autocheckboxes
// below for details on 'method', 'effect' and 'speed' and
// ajax_example_dependent_dropdown for 'event'.
),
);
// This entire form element will be replaced whenever 'changethis' is updated.
$form['replace_textfield'] = array(
'#type' => 'textfield',
'#title' => t("Why"),
// The prefix/suffix provide the div that we're replacing, named by
// #ajax['wrapper'] above.
'#prefix' => '<div id="replace_textfield_div">',
'#suffix' => '</div>',
);
// An AJAX request calls the form builder function for every change.
// We can change how we build the form based on $form_state.
if (!empty($form_state['values']['changethis'])) {
$form['replace_textfield']['#description'] = t("Say why you chose '@value'", array('@value' => $form_state['values']['changethis']));
}
return $form;
}
/**
* Callback for ajax_example_simplest.
*
* On an ajax submit, the form builder function is called again, then the $form
* and $form_state are passed to this callback function so it can select which
* portion of the form to send on to the client.
*
* @return array
* Renderable array (the textfield element)
*/
function ajax_example_simplest_callback($form, $form_state) {
// The form has already been submitted and updated. We can return the replaced
// item as it is.
return $form['replace_textfield'];
}
/**
* Form manipulation through AJAX.
*
* AJAX-enabled select element causes replacement of a set of checkboxes
* based on the selection.
*/
function ajax_example_autocheckboxes($form, &$form_state) {
// Since the form builder is called after every AJAX request, we rebuild
// the form based on $form_state.
$num_checkboxes = !empty($form_state['values']['howmany_select']) ? $form_state['values']['howmany_select'] : 1;
$form['howmany_select'] = array(
'#title' => t('How many checkboxes do you want?'),
'#type' => 'select',
'#options' => array(1 => 1, 2 => 2, 3 => 3, 4 => 4),
'#default_value' => $num_checkboxes,
'#ajax' => array(
'callback' => 'ajax_example_autocheckboxes_callback',
'wrapper' => 'checkboxes-div',
// 'method' defaults to replaceWith, but valid values also include
// append, prepend, before and after.
// 'method' => 'replaceWith',
// 'effect' defaults to none. Other valid values are 'fade' and 'slide'.
// See ajax_example_autotextfields for an example of 'fade'.
'effect' => 'slide',
// 'speed' defaults to 'slow'. You can also use 'fast'
// or a number of milliseconds for the animation to last.
// 'speed' => 'slow',
// Don't show any throbber...
'progress' => array('type' => 'none'),
),
);
$form['checkboxes_fieldset'] = array(
'#title' => t("Generated Checkboxes"),
// The prefix/suffix provide the div that we're replacing, named by
// #ajax['wrapper'] above.
'#prefix' => '<div id="checkboxes-div">',
'#suffix' => '</div>',
'#type' => 'fieldset',
'#description' => t('This is where we get automatically generated checkboxes'),
);
for ($i = 1; $i <= $num_checkboxes; $i++) {
$form['checkboxes_fieldset']["checkbox$i"] = array(
'#type' => 'checkbox',
'#title' => "Checkbox $i",
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Callback for autocheckboxes.
*
* Callback element needs only select the portion of the form to be updated.
* Since #ajax['callback'] return can be HTML or a renderable array (or an
* array of commands), we can just return a piece of the form.
* See @link ajax_example_advanced.inc AJAX Advanced Commands for more details
* on AJAX framework commands.
*
* @return array
* Renderable array (the checkboxes fieldset)
*/
function ajax_example_autocheckboxes_callback($form, $form_state) {
return $form['checkboxes_fieldset'];
}
/**
* Show/hide textfields based on AJAX-enabled checkbox clicks.
*/
function ajax_example_autotextfields($form, &$form_state) {
$form['ask_first_name'] = array(
'#type' => 'checkbox',
'#title' => t('Ask me my first name'),
'#ajax' => array(
'callback' => 'ajax_example_autotextfields_callback',
'wrapper' => 'textfields',
'effect' => 'fade',
),
);
$form['ask_last_name'] = array(
'#type' => 'checkbox',
'#title' => t('Ask me my last name'),
'#ajax' => array(
'callback' => 'ajax_example_autotextfields_callback',
'wrapper' => 'textfields',
'effect' => 'fade',
),
);
$form['textfields'] = array(
'#title' => t("Generated text fields for first and last name"),
'#prefix' => '<div id="textfields">',
'#suffix' => '</div>',
'#type' => 'fieldset',
'#description' => t('This is where we put automatically generated textfields'),
);
// Since checkboxes return TRUE or FALSE, we have to check that
// $form_state has been filled as well as what it contains.
if (!empty($form_state['values']['ask_first_name']) && $form_state['values']['ask_first_name']) {
$form['textfields']['first_name'] = array(
'#type' => 'textfield',
'#title' => t('First Name'),
);
}
if (!empty($form_state['values']['ask_last_name']) && $form_state['values']['ask_last_name']) {
$form['textfields']['last_name'] = array(
'#type' => 'textfield',
'#title' => t('Last Name'),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Click Me'),
);
return $form;
}
/**
* Callback for autotextfields.
*
* Selects the piece of the form we want to use as replacement text and returns
* it as a form (renderable array).
*
* @return array
* Renderable array (the textfields element)
*/
function ajax_example_autotextfields_callback($form, $form_state) {
return $form['textfields'];
}
/**
* A very basic form which with an AJAX-enabled submit.
*
* On submit, the markup in the #markup element is updated.
*/
function ajax_example_submit_driven_ajax($form, &$form_state) {
$form['box'] = array(
'#type' => 'markup',
'#prefix' => '<div id="box">',
'#suffix' => '</div>',
'#markup' => '<h1>Initial markup for box</h1>',
);
$form['submit'] = array(
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_example_submit_driven_callback',
'wrapper' => 'box',
),
'#value' => t('Submit'),
);
return $form;
}
/**
* Callback for submit_driven example.
*
* Select the 'box' element, change the markup in it, and return it as a
* renderable array.
*
* @return array
* Renderable array (the box element)
*/
function ajax_example_submit_driven_callback($form, $form_state) {
// In most cases, it is recommended that you put this logic in form generation
// rather than the callback. Submit driven forms are an exception, because
// you may not want to return the form at all.
$element = $form['box'];
$element['#markup'] = "Clicked submit ({$form_state['values']['op']}): " . date('c');
return $element;
}
/**
* AJAX-based dropdown example form.
*
* A form with a dropdown whose options are dependent on a
* choice made in a previous dropdown.
*
* On changing the first dropdown, the options in the second
* are updated.
*/
function ajax_example_dependent_dropdown($form, &$form_state) {
// Get the list of options to populate the first dropdown.
$options_first = _ajax_example_get_first_dropdown_options();
// If we have a value for the first dropdown from $form_state['values'] we use
// this both as the default value for the first dropdown and also as a
// parameter to pass to the function that retrieves the options for the
// second dropdown.
$selected = isset($form_state['values']['dropdown_first']) ? $form_state['values']['dropdown_first'] : key($options_first);
$form['dropdown_first'] = array(
'#type' => 'select',
'#title' => 'Instrument Type',
'#options' => $options_first,
'#default_value' => $selected,
// Bind an ajax callback to the change event (which is the default for the
// select form type) of the first dropdown. It will replace the second
// dropdown when rebuilt.
'#ajax' => array(
// When 'event' occurs, Drupal will perform an ajax request in the
// background. Usually the default value is sufficient (eg. change for
// select elements), but valid values include any jQuery event,
// most notably 'mousedown', 'blur', and 'submit'.
// 'event' => 'change',
'callback' => 'ajax_example_dependent_dropdown_callback',
'wrapper' => 'dropdown-second-replace',
),
);
$form['dropdown_second'] = array(
'#type' => 'select',
'#title' => $options_first[$selected] . ' ' . t('Instruments'),
// The entire enclosing div created here gets replaced when dropdown_first
// is changed.
'#prefix' => '<div id="dropdown-second-replace">',
'#suffix' => '</div>',
// When the form is rebuilt during ajax processing, the $selected variable
// will now have the new value and so the options will change.
'#options' => _ajax_example_get_second_dropdown_options($selected),
'#default_value' => isset($form_state['values']['dropdown_second']) ? $form_state['values']['dropdown_second'] : '',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Selects just the second dropdown to be returned for re-rendering.
*
* Since the controlling logic for populating the form is in the form builder
* function, all we do here is select the element and return it to be updated.
*
* @return array
* Renderable array (the second dropdown)
*/
function ajax_example_dependent_dropdown_callback($form, $form_state) {
return $form['dropdown_second'];
}
/**
* Helper function to populate the first dropdown.
*
* This would normally be pulling data from the database.
*
* @return array
* Dropdown options.
*/
function _ajax_example_get_first_dropdown_options() {
// drupal_map_assoc() just makes an array('String' => 'String'...).
return drupal_map_assoc(
array(
t('String'),
t('Woodwind'),
t('Brass'),
t('Percussion'),
)
);
}
/**
* Helper function to populate the second dropdown.
*
* This would normally be pulling data from the database.
*
* @param string $key
* This will determine which set of options is returned.
*
* @return array
* Dropdown options
*/
function _ajax_example_get_second_dropdown_options($key = '') {
$options = array(
t('String') => drupal_map_assoc(
array(
t('Violin'),
t('Viola'),
t('Cello'),
t('Double Bass'),
)
),
t('Woodwind') => drupal_map_assoc(
array(
t('Flute'),
t('Clarinet'),
t('Oboe'),
t('Bassoon'),
)
),
t('Brass') => drupal_map_assoc(
array(
t('Trumpet'),
t('Trombone'),
t('French Horn'),
t('Euphonium'),
)
),
t('Percussion') => drupal_map_assoc(
array(
t('Bass Drum'),
t('Timpani'),
t('Snare Drum'),
t('Tambourine'),
)
),
);
if (isset($options[$key])) {
return $options[$key];
}
else {
return array();
}
}
/**
* @} End of "defgroup ajax_example".
*/

View File

@ -0,0 +1,75 @@
<?php
/**
* @file
* Test ajax example module.
*/
/**
* Functional tests for AJAX Example module.
*
* @ingroup ajax_example
*/
class AjaxExampleTestCase extends DrupalWebTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Ajax example',
'description' => 'Checks behavior of the Ajax Example',
'group' => 'Examples',
);
}
/**
* Enable module.
*/
public function setUp() {
parent::setUp('ajax_example');
}
/**
* Check the non-JS version of the "Dynamic Sections" example.
*/
public function testDynamicSectionsNoJs() {
// The path to the example form.
$path = 'examples/ajax_example/dynamic_sections_no_js';
// Confirmation text for right and wrong answers.
$wrong = t('Wrong answer. Try again. (Hint: The right answer is "George Washington".)');
$right = t('You got the right answer: George Washington');
// For each question style, choose some parameters.
$params = array(
t('Multiple Choice') => array(
'value' => t('Abraham Lincoln'),
'answer' => t('Abraham Lincoln'),
'response' => $wrong,
),
t('True/False') => array(
'value' => t('George Washington'),
'answer' => t('George Washington'),
'response' => $right,
),
t('Fill-in-the-blanks') => array(
'value' => NULL,
'answer' => t('George Washington'),
'response' => $right,
),
);
foreach ($params as $style => $q_and_a) {
// Submit the initial form.
$edit = array('question_type_select' => $style);
$this->drupalPost($path, $edit, t('Choose'));
$this->assertResponse(200, format_string('Question style "@style" selected.', array('@style' => $style)));
// For convenience, make variables out of the entries in $QandA.
extract($q_and_a);
// Check for the expected input field.
$this->assertFieldByName('question', $value);
// Now, submit the dynamically generated form.
$edit = array('question' => $answer);
$this->drupalPost(NULL, $edit, t('Submit your answer'));
$this->assertRaw($response, 'Dynamic form has been submitted.');
}
}
}

View File

@ -0,0 +1,400 @@
<?php
/**
* @file
* AJAX Commands examples.
*
* This demonstrates each of the
* new AJAX commands. This is consolidated into a dense page because
* it's advanced material and because it would spread itself all over creation
* otherwise.
*/
/**
* Form to display the AJAX Commands.
*/
function ajax_example_advanced_commands($form, &$form_state) {
$form = array();
$form['intro'] = array(
'#type' => 'markup',
'#markup' => t("<div>Demonstrates how AJAX commands can be used.</div>"),
);
// Shows the 'after' command with a callback generating commands.
$form['after_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("This shows the Ajax 'after' command. Click to put something below the div that says 'Something can be inserted after this'"),
);
$form['after_command_example_fieldset']['after_command_example'] = array(
'#value' => t("AJAX 'After': Click to put something after the div"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_after_callback',
),
'#suffix' => "<div id='after_div'>Something can be inserted after this</div>
<div id='after_status'>'After' Command Status: Unknown</div>",
);
// Shows the 'alert' command.
$form['alert_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("Demonstrates the AJAX 'alert' command. Click the button."),
);
$form['alert_command_example_fieldset']['alert_command_example'] = array(
'#value' => t("AJAX 'Alert': Click to alert"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_alert_callback',
),
);
// Shows the 'append' command.
$form['append_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("This shows the Ajax 'append' command. Click to put something below the div that says 'Something can be inserted after this'"),
);
$form['append_command_example_fieldset']['append_command_example'] = array(
'#value' => t("AJAX 'Append': Click to append something"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_append_callback',
),
'#suffix' => "<div id='append_div'>Something can be appended inside this div... </div>
<div id='append_status'>'After' Command Status: Unknown</div>",
);
// Shows the 'before' command.
$form['before_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("This shows the Ajax 'before' command."),
);
$form['before_command_example_fieldset']['before_command_example'] = array(
'#value' => t("AJAX 'before': Click to put something before the div"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_before_callback',
),
'#suffix' => "<div id='before_div'>Something can be inserted before this</div>
<div id='before_status'>'before' Command Status: Unknown</div>",
);
// Shows the 'changed' command.
$form['changed_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("Demonstrates the AJAX 'changed' command. If region is 'changed', it is marked with CSS. This example also puts an asterisk by changed content."),
);
$form['changed_command_example_fieldset']['changed_command_example'] = array(
'#title' => t("AJAX changed: If checked, div is marked as changed."),
'#type' => 'checkbox',
'#default_value' => FALSE,
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_changed_callback',
),
'#suffix' => "<div id='changed_div'> <div id='changed_div_mark_this'>This div can be marked as changed or not.</div></div>
<div id='changed_status'>Status: Unknown</div>",
);
// Shows the AJAX 'css' command.
$form['css_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("Demonstrates the AJAX 'css' command."),
);
$form['css_command_example_fieldset']['css_command_example'] = array(
'#title' => t("AJAX CSS: Choose the color you'd like the '#box' div to be."),
'#type' => 'select',
// '#default_value' => 'green',
'#options' => array('green' => 'green', 'blue' => 'blue'),
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_css_callback',
),
'#suffix' => "<div id='css_div' style='height: 50px; width: 50px; border: 1px solid black'> box</div>
<div id='css_status'>Status: Unknown</div>",
);
// Shows the AJAX 'data' command. But there is no use of this information,
// as this would require a javascript client to use the data.
$form['data_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("Demonstrates the AJAX 'data' command."),
);
$form['data_command_example_fieldset']['data_command_example'] = array(
'#title' => t("AJAX data: Set a key/value pair on a selector."),
'#type' => 'textfield',
'#default_value' => 'color=green',
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_data_callback',
),
'#suffix' => "<div id='data_div'>This div should have key='time'/value='a time string' attached.</div>
<div id='data_status'>Status: Unknown</div>",
);
// Shows the AJAX 'html' command.
$form['html_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("Demonstrates the AJAX 'html' command."),
);
$form['html_command_example_fieldset']['html_command_example'] = array(
'#title' => t("AJAX html: Replace the HTML in a selector."),
'#type' => 'textfield',
'#default_value' => 'new value',
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_html_callback',
),
'#suffix' => "<div id='html_div'>Original contents</div>
<div id='html_status'>Status: Unknown</div>",
);
// Shows the AJAX 'prepend' command.
$form['prepend_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("This shows the AJAX 'prepend' command. Click to put something below the div that says 'Something can be inserted after this'"),
);
$form['prepend_command_example_fieldset']['prepend_command_example'] = array(
'#value' => t("AJAX 'prepend': Click to prepend something"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_prepend_callback',
),
'#suffix' => "<div id='prepend_div'>Something can be prepended to this div... </div>
<div id='prepend_status'>'After' Command Status: Unknown</div>",
);
// Shows the AJAX 'remove' command.
$form['remove_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("Shows the Ajax 'remove' command."),
);
$form['remove_command_example_fieldset']['remove_command_example'] = array(
'#title' => t("AJAX 'remove': Check to remove text. Uncheck to add it back."),
'#type' => 'checkbox',
'#default_value' => FALSE,
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_remove_callback',
),
'#suffix' => "<div id='remove_div'><div id='remove_text'>text to be removed</div></div>
<div id='remove_status'>'After' Command Status: Unknown</div>",
);
// Show off the AJAX 'restripe' command. Also shows classic AJAX replacement
// on the "how many rows" processing.
$form['restripe_command_example_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t("Demonstrates the Ajax 'restripe' command."),
);
$form['restripe_command_example_fieldset']['restripe_num_rows'] = array(
'#type' => 'select',
'#default_value' => !empty($form_state['values']['restripe_num_rows']) ? $form_state['values']['restripe_num_rows'] : 1,
'#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_restripe_num_rows',
'method' => 'replace',
'wrapper' => 'restripe_table',
),
);
$form['restripe_command_example_fieldset']['restripe_restripe'] = array(
'#type' => 'submit',
'#value' => t("Restripe the table"),
'#ajax' => array(
'callback' => 'ajax_example_advanced_commands_restripe_callback',
),
'#suffix' => "<div id='restripe_div'>
<table id='restripe_table' style='border: 1px solid black' >
<tr id='table-first'><td>first row</td></tr>
</table>
</div>
<div id='restripe_status'>'Restripe' Command Status: Unknown</div>",
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Callback for 'after'.
*
* @see ajax_command_after()
*/
function ajax_example_advanced_commands_after_callback($form, $form_state) {
$selector = '#after_div';
$commands = array();
$commands[] = ajax_command_after($selector, "New 'after'...");
$commands[] = ajax_command_replace("#after_status", "<div id='after_status'>Updated after_command_example " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'alert'.
*
* @see ajax_command_alert()
*/
function ajax_example_advanced_commands_alert_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_alert("Alert requested at " . date('r'));
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'append'.
*
* @see ajax_command_append()
*/
function ajax_example_advanced_commands_append_callback($form, $form_state) {
$selector = '#append_div';
$commands = array();
$commands[] = ajax_command_append($selector, "Stuff...");
$commands[] = ajax_command_replace("#append_status", "<div id='append_status'>Updated append_command_example " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'before'.
*
* @see ajax_command_before()
*/
function ajax_example_advanced_commands_before_callback($form, $form_state) {
$selector = '#before_div';
$commands = array();
$commands[] = ajax_command_before($selector, "New 'before'...");
$commands[] = ajax_command_replace("#before_status", "<div id='before_status'>Updated before_command_example " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'changed'.
*
* @see ajax_command_changed()
*/
function ajax_example_advanced_commands_changed_callback($form, $form_state) {
$checkbox_value = $form_state['values']['changed_command_example'];
$checkbox_value_string = $checkbox_value ? "TRUE" : "FALSE";
$commands = array();
if ($checkbox_value) {
$commands[] = ajax_command_changed('#changed_div', '#changed_div_mark_this');
}
else {
$commands[] = ajax_command_replace('#changed_div', "<div id='changed_div'> <div id='changed_div_mark_this'>This div can be marked as changed or not.</div></div>");
}
$commands[] = ajax_command_replace("#changed_status", "<div id='changed_status'>Updated changed_command_example to $checkbox_value_string: " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'css'.
*
* @see ajax_command_css()
*/
function ajax_example_advanced_commands_css_callback($form, $form_state) {
$selector = '#css_div';
$color = $form_state['values']['css_command_example'];
$commands = array();
$commands[] = ajax_command_css($selector, array('background-color' => $color));
$commands[] = ajax_command_replace("#css_status", "<div id='css_status'>Updated css_command_example to '{$color}' " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'data'.
*
* @see ajax_command_data()
*/
function ajax_example_advanced_commands_data_callback($form, $form_state) {
$selector = '#data_div';
$text = $form_state['values']['data_command_example'];
list($key, $value) = preg_split('/=/', $text);
$commands = array();
$commands[] = ajax_command_data($selector, $key, $value);
$commands[] = ajax_command_replace("#data_status", "<div id='data_status'>Updated data_command_example with key=$key, value=$value; " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'html'.
*
* @see ajax_command_html()
*/
function ajax_example_advanced_commands_html_callback($form, $form_state) {
$text = $form_state['values']['html_command_example'];
$commands = array();
$commands[] = ajax_command_html('#html_div', $text);
$commands[] = ajax_command_replace("#html_status", "<div id='html_status'>Updated html_command_example with text=$text; " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'prepend'.
*
* @see ajax_command_prepend()
*/
function ajax_example_advanced_commands_prepend_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_prepend('#prepend_div', "Prepended Stuff...");
$commands[] = ajax_command_replace("#prepend_status", "<div id='prepend_status'>Updated prepend_command_example " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'remove'.
*
* @see ajax_command_remove()
*/
function ajax_example_advanced_commands_remove_callback($form, $form_state) {
$commands = array();
$should_remove = $form_state['values']['remove_command_example'];
$should_remove_string = $should_remove ? 'TRUE' : 'FALSE';
if ($should_remove) {
$commands[] = ajax_command_remove('#remove_text');
}
else {
$commands[] = ajax_command_html('#remove_div', "<div id='remove_text'>text to be removed</div>");
}
$commands[] = ajax_command_replace("#remove_status", "<div id='remove_status'>Updated remove_command_example (value={$should_remove_string} " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Callback for 'restripe'.
*
* Rebuilds the table with the selected number of rows.
*/
function ajax_example_advanced_commands_restripe_num_rows($form, $form_state) {
$num_rows = $form_state['values']['restripe_num_rows'];
$output = "<table id='restripe_table' style='border: 1px solid black'>";
for ($i = 1; $i <= $num_rows; $i++) {
$output .= "<tr><td>Row $i</td></tr>";
}
$output .= "</table>";
return $output;
}
/**
* Callback for 'restripe'.
*
* @see ajax_command_restripe()
*/
function ajax_example_advanced_commands_restripe_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_restripe('#restripe_table');
$commands[] = ajax_command_replace("#restripe_status", "<div id='restripe_status'>Restriped table " . date('r') . "</div>");
return array('#type' => 'ajax', '#commands' => $commands);
}

View File

@ -0,0 +1,458 @@
<?php
/**
* @file
* ajax_example_autocomplete.inc
*
* Demonstrates usage of the Form API's autocomplete features.
*
* This file provides three examples in increasing complexity:
* - A simple username autocomplete (usernames are unique, so little effort is
* required)
* - A node title autocomplete (titles are not unique, so we have to find the
* nid and stash it in the field)
* - A username autocomplete that updates a node title autocomplete with a
* changed #autocomplete_path so that the #autocomplete_path can have
* context (the username to use in the search).
*/
/**
* A simple autocomplete form which just looks up usernames in the user table.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form.
*
* @return array
* Form array.
*/
function ajax_example_simple_autocomplete($form, &$form_state) {
$form['info'] = array(
'#markup' => '<div>' . t("This example does a simplest possible autocomplete by username. You'll need a few users on your system for it to make sense.") . '</div>',
);
$form['user'] = array(
'#type' => 'textfield',
'#title' => t('Choose a user (or a people, depending on your usage preference)'),
// The autocomplete path is provided in hook_menu in ajax_example.module.
'#autocomplete_path' => 'examples/ajax_example/simple_user_autocomplete_callback',
);
return $form;
}
/**
* This is just a copy of user_autocomplete().
*
* It works simply by searching usernames (and of course in Drupal usernames
* are unique, so can be used for identifying a record.)
*
* The returned $matches array has
* * key: string which will be displayed once the autocomplete is selected
* * value: the value which will is displayed in the autocomplete pulldown.
*
* In the simplest cases (see user_autocomplete()) these are the same, and
* nothing needs to be done. However, more more complicated autocompletes
* require more work. Here we demonstrate the difference by displaying the UID
* along with the username in the dropdown.
*
* In the end, though, we'll be doing something with the value that ends up in
* the textfield, so it needs to uniquely identify the record we want to access.
* This is demonstrated in ajax_example_unique_autocomplete().
*
* @param string $string
* The string that will be searched.
*/
function ajax_example_simple_user_autocomplete_callback($string = "") {
$matches = array();
if ($string) {
$result = db_select('users')
->fields('users', array('name', 'uid'))
->condition('name', db_like($string) . '%', 'LIKE')
->range(0, 10)
->execute();
foreach ($result as $user) {
// In the simplest case (see user_autocomplete), the key and the value are
// the same. Here we'll display the uid along with the username in the
// dropdown.
$matches[$user->name] = check_plain($user->name) . " (uid=$user->uid)";
}
}
drupal_json_output($matches);
}
/**
* An autocomplete form to look up nodes by title.
*
* An autocomplete form which looks up nodes by title in the node table,
* but must keep track of the nid, because titles are certainly not guaranteed
* to be unique.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form state.
*
* * @return array
* Form array.
*/
function ajax_example_unique_autocomplete($form, &$form_state) {
$form['info'] = array(
'#markup' => '<div>' . t("This example does a node autocomplete by title. The difference between this and a username autocomplete is that the node title may not be unique, so we have to use the nid for uniqueness, placing it in a parseable location in the textfield.") . '</div>',
);
$form['node'] = array(
'#type' => 'textfield',
'#title' => t('Choose a node by title'),
// The autocomplete path is provided in hook_menu in ajax_example.module.
'#autocomplete_path' => 'examples/ajax_example/unique_node_autocomplete_callback',
);
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Node title validation handler.
*
* Validate handler to convert our string like "Some node title [3325]" into a
* nid.
*
* In case the user did not actually use the autocomplete or have a valid string
* there, we'll try to look up a result anyway giving it our best guess.
*
* Since the user chose a unique node, we must now use the same one in our
* submit handler, which means we need to look in the string for the nid.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form state.
*/
function ajax_example_unique_autocomplete_validate($form, &$form_state) {
$title = $form_state['values']['node'];
$matches = array();
// This preg_match() looks for the last pattern like [33334] and if found
// extracts the numeric portion.
$result = preg_match('/\[([0-9]+)\]$/', $title, $matches);
if ($result > 0) {
// If $result is nonzero, we found a match and can use it as the index into
// $matches.
$nid = $matches[$result];
// Verify that it's a valid nid.
$node = node_load($nid);
if (empty($node)) {
form_error($form['node'], t('Sorry, no node with nid %nid can be found', array('%nid' => $nid)));
return;
}
}
// BUT: Not everybody will have javascript turned on, or they might hit ESC
// and not use the autocomplete values offered. In that case, we can attempt
// to come up with a useful value. This is not absolutely necessary, and we
// *could* just emit a form_error() as below.
else {
$nid = db_select('node')
->fields('node', array('nid'))
->condition('title', db_like($title) . '%', 'LIKE')
->range(0, 1)
->execute()
->fetchField();
}
// Now, if we somehow found a nid, assign it to the node. If we failed, emit
// an error.
if (!empty($nid)) {
$form_state['values']['node'] = $nid;
}
else {
form_error($form['node'], t('Sorry, no node starting with %title can be found', array('%title' => $title)));
}
}
/**
* Submit handler for node lookup unique autocomplete example.
*
* Here the nid has already been placed in $form_state['values']['node'] by the
* validation handler.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form state.
*/
function ajax_example_unique_autocomplete_submit($form, &$form_state) {
$node = node_load($form_state['values']['node']);
drupal_set_message(t('You found node %nid with title %title', array('%nid' => $node->nid, '%title' => $node->title)));
}
/**
* Autocomplete callback for nodes by title.
*
* Searches for a node by title, but then identifies it by nid, so the actual
* returned value can be used later by the form.
*
* The returned $matches array has
* - key: The title, with the identifying nid in brackets, like "Some node
* title [3325]"
* - value: the title which will is displayed in the autocomplete pulldown.
*
* Note that we must use a key style that can be parsed successfully and
* unambiguously. For example, if we might have node titles that could have
* [3325] in them, then we'd have to use a more restrictive token.
*
* @param string $string
* The string that will be searched.
*/
function ajax_example_unique_node_autocomplete_callback($string = "") {
$matches = array();
if ($string) {
$result = db_select('node')
->fields('node', array('nid', 'title'))
->condition('title', db_like($string) . '%', 'LIKE')
->range(0, 10)
->execute();
foreach ($result as $node) {
$matches[$node->title . " [$node->nid]"] = check_plain($node->title);
}
}
drupal_json_output($matches);
}
/**
* Search by title and author.
*
* In this example, we'll look up nodes by title, but we want only nodes that
* have been authored by a particular user. That means that we'll have to make
* an autocomplete function which takes a username as an argument, and use
* #ajax to change the #autocomplete_path based on the selected user.
*
* Although the implementation of the validate handler may look complex, it's
* just ambitious. The idea here is:
* 1. Autcomplete to get a valid username.
* 2. Use #ajax to update the node element with a #autocomplete_callback that
* gives the context for the username.
* 3. Do an autcomplete on the node field that is limited by the username.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form state.
*
* @return array
* Form API array.
*/
function ajax_example_node_by_author_autocomplete($form, &$form_state) {
$form['intro'] = array(
'#markup' => '<div>' . t("This example uses a user autocomplete to dynamically change a node title autocomplete using #ajax.
This is a way to get past the fact that we have no other way to provide context to the autocomplete function.
It won't work very well unless you have a few users who have created some content that you can search for.") . '</div>',
);
$form['author'] = array(
'#type' => 'textfield',
'#title' => t('Choose the username that authored nodes you are interested in'),
// Since we just need simple user lookup, we can use the simplest function
// of them all, user_autocomplete().
'#autocomplete_path' => 'user/autocomplete',
'#ajax' => array(
'callback' => 'ajax_example_node_by_author_ajax_callback',
'wrapper' => 'autocomplete-by-node-ajax-replace',
),
);
// This form element with autocomplete will be replaced by #ajax whenever the
// author changes, allowing the search to be limited by user.
$form['node'] = array(
'#type' => 'textfield',
'#title' => t('Choose a node by title'),
'#prefix' => '<div id="autocomplete-by-node-ajax-replace">',
'#suffix' => '</div>',
'#disabled' => TRUE,
);
// When the author changes in the author field, we'll change the
// autocomplete_path to match.
if (!empty($form_state['values']['author'])) {
$author = user_load_by_name($form_state['values']['author']);
if (!empty($author)) {
$autocomplete_path = 'examples/ajax_example/node_by_author_autocomplete/' . $author->uid;
$form['node']['#autocomplete_path'] = $autocomplete_path;
$form['node']['#title'] = t('Choose a node title authored by %author', array('%author' => $author->name));
$form['node']['#disabled'] = FALSE;
}
}
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* AJAX callback for author form element.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form state.
*
* @return array
* Form API array.
*/
function ajax_example_node_by_author_ajax_callback($form, $form_state) {
return $form['node'];
}
/**
* Validate handler to convert our title string into a nid.
*
* In case the user did not actually use the autocomplete or have a valid string
* there, we'll try to look up a result anyway giving it our best guess.
*
* Since the user chose a unique node, we must now use the same one in our
* submit handler, which means we need to look in the string for the nid.
*
* This handler looks complex because it's ambitious (and tries to punt and
* find a node if they've entered a valid username and part of a title), but
* you *could* just do a form_error() if nothing were found, forcing people to
* use the autocomplete to look up the relevant items.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form state.
*
* @return array
* Form API array.
*/
function ajax_example_node_by_author_autocomplete_validate($form, &$form_state) {
$title = $form_state['values']['node'];
$author = $form_state['values']['author'];
$matches = array();
// We must have a valid user.
$account = user_load_by_name($author);
if (empty($account)) {
form_error($form['author'], t('You must choose a valid author username'));
return;
}
// This preg_match() looks for the last pattern like [33334] and if found
// extracts the numeric portion.
$result = preg_match('/\[([0-9]+)\]$/', $title, $matches);
if ($result > 0) {
// If $result is nonzero, we found a match and can use it as the index into
// $matches.
$nid = $matches[$result];
// Verify that it's a valid nid.
$node = node_load($nid);
if (empty($node)) {
form_error($form['node'], t('Sorry, no node with nid %nid can be found', array('%nid' => $nid)));
return;
}
}
// BUT: Not everybody will have javascript turned on, or they might hit ESC
// and not use the autocomplete values offered. In that case, we can attempt
// to come up with a useful value. This is not absolutely necessary, and we
// *could* just emit a form_error() as below. Here we'll find the *first*
// matching title and assume that is adequate.
else {
$nid = db_select('node')
->fields('node', array('nid'))
->condition('uid', $account->uid)
->condition('title', db_like($title) . '%', 'LIKE')
->range(0, 1)
->execute()
->fetchField();
}
// Now, if we somehow found a nid, assign it to the node. If we failed, emit
// an error.
if (!empty($nid)) {
$form_state['values']['node'] = $nid;
}
else {
form_error($form['node'], t('Sorry, no node starting with %title can be found', array('%title' => $title)));
}
}
/**
* Submit handler for node lookup unique autocomplete example.
*
* Here the nid has already been placed in $form_state['values']['node'] by the
* validation handler.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form state.
*
* @return array
* Form API array.
*/
function ajax_example_node_by_author_autocomplete_submit($form, &$form_state) {
$node = node_load($form_state['values']['node']);
$account = user_load($node->uid);
drupal_set_message(t('You found node %nid with title !title_link, authored by !user_link',
array(
'%nid' => $node->nid,
'!title_link' => l($node->title, 'node/' . $node->nid),
'!user_link' => theme('username', array('account' => $account)),
)
));
}
/**
* Autocomplete callback for nodes by title but limited by author.
*
* Searches for a node by title given the passed-in author username.
*
* The returned $matches array has
* - key: The title, with the identifying nid in brackets, like "Some node
* title [3325]"
* - value: the title which will is displayed in the autocomplete pulldown.
*
* Note that we must use a key style that can be parsed successfully and
* unambiguously. For example, if we might have node titles that could have
* [3325] in them, then we'd have to use a more restrictive token.
*
* @param int $author_uid
* The author username to limit the search.
* @param string $string
* The string that will be searched.
*/
function ajax_example_node_by_author_node_autocomplete_callback($author_uid, $string = "") {
$matches = array();
if ($author_uid > 0 && trim($string)) {
$result = db_select('node')
->fields('node', array('nid', 'title'))
->condition('uid', $author_uid)
->condition('title', db_like($string) . '%', 'LIKE')
->range(0, 10)
->execute();
foreach ($result as $node) {
$matches[$node->title . " [$node->nid]"] = check_plain($node->title);
}
}
drupal_json_output($matches);
}

View File

@ -0,0 +1,668 @@
<?php
/**
* @file
* Demonstrations of AJAX with graceful degradation.
*/
/**
* @defgroup ajax_degradation_example Example: AJAX Graceful Degradation
* @ingroup examples
* @{
* These examples show AJAX with graceful degradation when Javascript is not
* available.
*
* In each of these the key idea is that the form is rebuilt different ways
* depending on form input. In order to accomplish that, the formbuilder
* function is in charge of almost all logic.
*/
/**
* Dropdown form based on previous choices.
*
* A form with a dropdown whose options are dependent on a choice made in a
* previous dropdown.
*
* On changing the first dropdown, the options in the second
* are updated. Gracefully degrades if no javascript.
*
* A bit of CSS and javascript is required. The CSS hides the "add more" button
* if javascript is not enabled. The Javascript snippet is really only used
* to enable us to present the form in degraded mode without forcing the user
* to turn off Javascript. Both of these are loaded by using the
* #attached FAPI property, so it is a good example of how to use that.
*
* The extra argument $no_js_use is here only to allow presentation of this
* form as if Javascript were not enabled. ajax_example_menu() provides two
* ways to call this form, one normal ($no_js_use = FALSE) and one simulating
* Javascript disabled ($no_js_use = TRUE).
*/
function ajax_example_dependent_dropdown_degrades($form, &$form_state, $no_js_use = FALSE) {
// Get the list of options to populate the first dropdown.
$options_first = _ajax_example_get_first_dropdown_options();
// If we have a value for the first dropdown from $form_state['values'] we use
// this both as the default value for the first dropdown and also as a
// parameter to pass to the function that retrieves the options for the
// second dropdown.
$selected = isset($form_state['values']['dropdown_first']) ? $form_state['values']['dropdown_first'] : key($options_first);
// Attach the CSS and JS we need to show this with and without javascript.
// Without javascript we need an extra "Choose" button, and this is
// hidden when we have javascript enabled.
$form['#attached']['css'] = array(
drupal_get_path('module', 'ajax_example') . '/ajax_example.css',
);
$form['#attached']['js'] = array(
drupal_get_path('module', 'ajax_example') . '/ajax_example.js',
);
$form['dropdown_first_fieldset'] = array(
'#type' => 'fieldset',
);
$form['dropdown_first_fieldset']['dropdown_first'] = array(
'#type' => 'select',
'#title' => 'Instrument Type',
'#options' => $options_first,
'#attributes' => array('class' => array('enabled-for-ajax')),
// The '#ajax' property allows us to bind a callback to the server whenever
// this form element changes. See ajax_example_autocheckboxes and
// ajax_example_dependent_dropdown in ajax_example.module for more details.
'#ajax' => array(
'callback' => 'ajax_example_dependent_dropdown_degrades_first_callback',
'wrapper' => 'dropdown-second-replace',
),
);
// This simply allows us to demonstrate no-javascript use without
// actually turning off javascript in the browser. Removing the #ajax
// element turns off AJAX behaviors on that element and as a result
// ajax.js doesn't get loaded. This is for demonstration purposes only.
if ($no_js_use) {
unset($form['dropdown_first_fieldset']['dropdown_first']['#ajax']);
}
// Since we don't know if the user has js or not, we always need to output
// this element, then hide it with with css if javascript is enabled.
$form['dropdown_first_fieldset']['continue_to_second'] = array(
'#type' => 'submit',
'#value' => t('Choose'),
'#attributes' => array('class' => array('next-button')),
);
$form['dropdown_second_fieldset'] = array(
'#type' => 'fieldset',
);
$form['dropdown_second_fieldset']['dropdown_second'] = array(
'#type' => 'select',
'#title' => $options_first[$selected] . ' ' . t('Instruments'),
'#prefix' => '<div id="dropdown-second-replace">',
'#suffix' => '</div>',
'#attributes' => array('class' => array('enabled-for-ajax')),
// When the form is rebuilt during processing (either AJAX or multistep),
// the $selected variable will now have the new value and so the options
// will change.
'#options' => _ajax_example_get_second_dropdown_options($selected),
);
$form['dropdown_second_fieldset']['submit'] = array(
'#type' => 'submit',
'#value' => t('OK'),
// This class allows attached js file to override the disabled attribute,
// since it's not necessary in ajax-enabled form.
'#attributes' => array('class' => array('enabled-for-ajax')),
);
// Disable dropdown_second if a selection has not been made on dropdown_first.
if (empty($form_state['values']['dropdown_first'])) {
$form['dropdown_second_fieldset']['dropdown_second']['#disabled'] = TRUE;
$form['dropdown_second_fieldset']['dropdown_second']['#description'] = t('You must make your choice on the first dropdown before changing this second one.');
$form['dropdown_second_fieldset']['submit']['#disabled'] = TRUE;
}
return $form;
}
/**
* Submit function for ajax_example_dependent_dropdown_degrades().
*/
function ajax_example_dependent_dropdown_degrades_submit($form, &$form_state) {
// Now handle the case of the next, previous, and submit buttons.
// only submit will result in actual submission, all others rebuild.
switch ($form_state['triggering_element']['#value']) {
case t('OK'):
// Submit: We're done.
drupal_set_message(t('Your values have been submitted. dropdown_first=@first, dropdown_second=@second', array('@first' => $form_state['values']['dropdown_first'], '@second' => $form_state['values']['dropdown_second'])));
return;
}
// 'Choose' or anything else will cause rebuild of the form and present
// it again.
$form_state['rebuild'] = TRUE;
}
/**
* Selects just the second dropdown to be returned for re-rendering.
*
* @return array
* Renderable array (the second dropdown).
*/
function ajax_example_dependent_dropdown_degrades_first_callback($form, $form_state) {
return $form['dropdown_second_fieldset']['dropdown_second'];
}
/**
* Dynamically-enabled form with graceful no-JS degradation.
*
* Example of a form with portions dynamically enabled or disabled, but
* with graceful degradation in the case of no javascript.
*
* The idea here is that certain parts of the form don't need to be displayed
* unless a given option is selected, but then they should be displayed and
* configured.
*
* The third $no_js_use argument is strictly for demonstrating operation
* without javascript, without making the user/developer turn off javascript.
*/
function ajax_example_dynamic_sections($form, &$form_state, $no_js_use = FALSE) {
// Attach the CSS and JS we need to show this with and without javascript.
// Without javascript we need an extra "Choose" button, and this is
// hidden when we have javascript enabled.
$form['#attached']['css'] = array(
drupal_get_path('module', 'ajax_example') . '/ajax_example.css',
);
$form['#attached']['js'] = array(
drupal_get_path('module', 'ajax_example') . '/ajax_example.js',
);
$form['description'] = array(
'#type' => 'markup',
'#markup' => '<div>' . t('This example demonstrates a form which dynamically creates various sections based on the configuration in the form.
It deliberately allows graceful degradation to a non-javascript environment.
In a non-javascript environment, the "Choose" button next to the select control
is displayed; in a javascript environment it is hidden by the module CSS.
<br/><br/>The basic idea here is that the form is built up based on
the selection in the question_type_select field, and it is built the same
whether we are in a javascript/AJAX environment or not.
<br/><br/>
Try the <a href="!ajax_link">AJAX version</a> and the <a href="!non_ajax_link">simulated-non-AJAX version</a>.
', array('!ajax_link' => url('examples/ajax_example/dynamic_sections'), '!non_ajax_link' => url('examples/ajax_example/dynamic_sections_no_js'))) . '</div>',
);
$form['question_type_select'] = array(
'#type' => 'select',
'#title' => t('Question style'),
'#options' => drupal_map_assoc(
array(
t('Choose question style'),
t('Multiple Choice'),
t('True/False'),
t('Fill-in-the-blanks'),
)
),
'#ajax' => array(
'wrapper' => 'questions-fieldset-wrapper',
'callback' => 'ajax_example_dynamic_sections_select_callback',
),
);
// The CSS for this module hides this next button if JS is enabled.
$form['question_type_submit'] = array(
'#type' => 'submit',
'#value' => t('Choose'),
'#attributes' => array('class' => array('next-button')),
// No need to validate when submitting this.
'#limit_validation_errors' => array(),
'#validate' => array(),
);
// This simply allows us to demonstrate no-javascript use without
// actually turning off javascript in the browser. Removing the #ajax
// element turns off AJAX behaviors on that element and as a result
// ajax.js doesn't get loaded.
if ($no_js_use) {
// Remove the #ajax from the above, so ajax.js won't be loaded.
unset($form['question_type_select']['#ajax']);
}
// This fieldset just serves as a container for the part of the form
// that gets rebuilt.
$form['questions_fieldset'] = array(
'#type' => 'fieldset',
// These provide the wrapper referred to in #ajax['wrapper'] above.
'#prefix' => '<div id="questions-fieldset-wrapper">',
'#suffix' => '</div>',
);
if (!empty($form_state['values']['question_type_select'])) {
$form['questions_fieldset']['question'] = array(
'#markup' => t('Who was the first president of the U.S.?'),
);
$question_type = $form_state['values']['question_type_select'];
switch ($question_type) {
case t('Multiple Choice'):
$form['questions_fieldset']['question'] = array(
'#type' => 'radios',
'#title' => t('Who was the first president of the United States'),
'#options' => drupal_map_assoc(
array(
t('George Bush'),
t('Adam McGuire'),
t('Abraham Lincoln'),
t('George Washington'),
)
),
);
break;
case t('True/False'):
$form['questions_fieldset']['question'] = array(
'#type' => 'radios',
'#title' => t('Was George Washington the first president of the United States?'),
'#options' => array(t('George Washington') => t("True"), 0 => t("False")),
'#description' => t('Click "True" if you think George Washington was the first president of the United States.'),
);
break;
case t('Fill-in-the-blanks'):
$form['questions_fieldset']['question'] = array(
'#type' => 'textfield',
'#title' => t('Who was the first president of the United States'),
'#description' => t('Please type the correct answer to the question.'),
);
break;
}
$form['questions_fieldset']['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit your answer'),
);
}
return $form;
}
/**
* Validation function for ajax_example_dynamic_sections().
*/
function ajax_example_dynamic_sections_validate($form, &$form_state) {
$answer = $form_state['values']['question'];
if ($answer !== t('George Washington')) {
form_set_error('question', t('Wrong answer. Try again. (Hint: The right answer is "George Washington".)'));
}
}
/**
* Submit function for ajax_example_dynamic_sections().
*/
function ajax_example_dynamic_sections_submit($form, &$form_state) {
// This is only executed when a button is pressed, not when the AJAXified
// select is changed.
// Now handle the case of the next, previous, and submit buttons.
// Only submit will result in actual submission, all others rebuild.
switch ($form_state['triggering_element']['#value']) {
case t('Submit your answer'):
// Submit: We're done.
$form_state['rebuild'] = FALSE;
$answer = $form_state['values']['question'];
// Special handling for the checkbox.
if ($answer == 1 && $form['questions_fieldset']['question']['#type'] == 'checkbox') {
$answer = $form['questions_fieldset']['question']['#title'];
}
if ($answer === t('George Washington')) {
drupal_set_message(t('You got the right answer: @answer', array('@answer' => $answer)));
}
else {
drupal_set_message(t('Sorry, your answer (@answer) is wrong', array('@answer' => $answer)));
}
return;
// Any other form element will cause rebuild of the form and present
// it again.
case t('Choose'):
$form_state['values']['question_type_select'] = $form_state['input']['question_type_select'];
// Fall through.
default:
$form_state['rebuild'] = TRUE;
}
}
/**
* Callback for the select element.
*
* This just selects and returns the questions_fieldset.
*/
function ajax_example_dynamic_sections_select_callback($form, $form_state) {
return $form['questions_fieldset'];
}
/**
* Wizard form.
*
* This example is a classic wizard, where a different and sequential form
* is presented on each step of the form.
*
* In the AJAX version, the form is replaced for each wizard section. In the
* multistep version, it causes a new page load.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form.
* @param bool $no_js_use
* Used for this demonstration only. If true means that the form should be
* built using a simulated no-javascript approach (ajax.js will not be
* loaded.)
*
* @return array
* Form array.
*/
function ajax_example_wizard($form, &$form_state, $no_js_use = FALSE) {
// Provide a wrapper around the entire form, since we'll replace the whole
// thing with each submit.
$form['#prefix'] = '<div id="wizard-form-wrapper">';
$form['#suffix'] = '</div>';
// We want to deal with hierarchical form values.
$form['#tree'] = TRUE;
$form['description'] = array(
'#markup' => '<div>' . t('This example is a step-by-step wizard. The <a href="!ajax">AJAX version</a> does it without page reloads; the <a href="!multistep">multistep version</a> is the same code but simulates a non-javascript environment, showing it with page reloads.',
array('!ajax' => url('examples/ajax_example/wizard'), '!multistep' => url('examples/ajax_example/wizard_no_js')))
. '</div>',
);
// $form_state['storage'] has no specific drupal meaning, but it is
// traditional to keep variables for multistep forms there.
$step = empty($form_state['storage']['step']) ? 1 : $form_state['storage']['step'];
$form_state['storage']['step'] = $step;
switch ($step) {
case 1:
$form['step1'] = array(
'#type' => 'fieldset',
'#title' => t('Step 1: Personal details'),
);
$form['step1']['name'] = array(
'#type' => 'textfield',
'#title' => t('Your name'),
'#default_value' => empty($form_state['values']['step1']['name']) ? '' : $form_state['values']['step1']['name'],
'#required' => TRUE,
);
break;
case 2:
$form['step2'] = array(
'#type' => 'fieldset',
'#title' => t('Step 2: Street address info'),
);
$form['step2']['address'] = array(
'#type' => 'textfield',
'#title' => t('Your street address'),
'#default_value' => empty($form_state['values']['step2']['address']) ? '' : $form_state['values']['step2']['address'],
'#required' => TRUE,
);
break;
case 3:
$form['step3'] = array(
'#type' => 'fieldset',
'#title' => t('Step 3: City info'),
);
$form['step3']['city'] = array(
'#type' => 'textfield',
'#title' => t('Your city'),
'#default_value' => empty($form_state['values']['step3']['city']) ? '' : $form_state['values']['step3']['city'],
'#required' => TRUE,
);
break;
}
if ($step == 3) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t("Submit your information"),
);
}
if ($step < 3) {
$form['next'] = array(
'#type' => 'submit',
'#value' => t('Next step'),
'#ajax' => array(
'wrapper' => 'wizard-form-wrapper',
'callback' => 'ajax_example_wizard_callback',
),
);
}
if ($step > 1) {
$form['prev'] = array(
'#type' => 'submit',
'#value' => t("Previous step"),
// Since all info will be discarded, don't validate on 'prev'.
'#limit_validation_errors' => array(),
// #submit is required to use #limit_validation_errors
'#submit' => array('ajax_example_wizard_submit'),
'#ajax' => array(
'wrapper' => 'wizard-form-wrapper',
'callback' => 'ajax_example_wizard_callback',
),
);
}
// This simply allows us to demonstrate no-javascript use without
// actually turning off javascript in the browser. Removing the #ajax
// element turns off AJAX behaviors on that element and as a result
// ajax.js doesn't get loaded.
// For demonstration only! You don't need this.
if ($no_js_use) {
// Remove the #ajax from the above, so ajax.js won't be loaded.
// For demonstration only.
unset($form['next']['#ajax']);
unset($form['prev']['#ajax']);
}
return $form;
}
/**
* Wizard callback function.
*
* @param array $form
* Form API form.
* @param array $form_state
* Form API form.
*
* @return array
* Form array.
*/
function ajax_example_wizard_callback($form, $form_state) {
return $form;
}
/**
* Submit function for ajax_example_wizard.
*
* In AJAX this is only submitted when the final submit button is clicked,
* but in the non-javascript situation, it is submitted with every
* button click.
*/
function ajax_example_wizard_submit($form, &$form_state) {
// Save away the current information.
$current_step = 'step' . $form_state['storage']['step'];
if (!empty($form_state['values'][$current_step])) {
$form_state['storage']['values'][$current_step] = $form_state['values'][$current_step];
}
// Increment or decrement the step as needed. Recover values if they exist.
if ($form_state['triggering_element']['#value'] == t('Next step')) {
$form_state['storage']['step']++;
// If values have already been entered for this step, recover them from
// $form_state['storage'] to pre-populate them.
$step_name = 'step' . $form_state['storage']['step'];
if (!empty($form_state['storage']['values'][$step_name])) {
$form_state['values'][$step_name] = $form_state['storage']['values'][$step_name];
}
}
if ($form_state['triggering_element']['#value'] == t('Previous step')) {
$form_state['storage']['step']--;
// Recover our values from $form_state['storage'] to pre-populate them.
$step_name = 'step' . $form_state['storage']['step'];
$form_state['values'][$step_name] = $form_state['storage']['values'][$step_name];
}
// If they're done, submit.
if ($form_state['triggering_element']['#value'] == t('Submit your information')) {
$value_message = t('Your information has been submitted:') . ' ';
foreach ($form_state['storage']['values'] as $step => $values) {
$value_message .= "$step: ";
foreach ($values as $key => $value) {
$value_message .= "$key=$value, ";
}
}
drupal_set_message($value_message);
$form_state['rebuild'] = FALSE;
return;
}
// Otherwise, we still have work to do.
$form_state['rebuild'] = TRUE;
}
/**
* Form with 'add more' and 'remove' buttons.
*
* This example shows a button to "add more" - add another textfield, and
* the corresponding "remove" button.
*
* It works equivalently with javascript or not, and does the same basic steps
* either way.
*
* The basic idea is that we build the form based on the setting of
* $form_state['num_names']. The custom submit functions for the "add-one"
* and "remove-one" buttons increment and decrement $form_state['num_names']
* and then force a rebuild of the form.
*
* The $no_js_use argument is simply for demonstration: When set, it prevents
* '#ajax' from being set, thus making the example behave as if javascript
* were disabled in the browser.
*/
function ajax_example_add_more($form, &$form_state, $no_js_use = FALSE) {
$form['description'] = array(
'#markup' => '<div>' . t('This example shows an add-more and a remove-last button. The <a href="!ajax">AJAX version</a> does it without page reloads; the <a href="!multistep">non-js version</a> is the same code but simulates a non-javascript environment, showing it with page reloads.',
array('!ajax' => url('examples/ajax_example/add_more'), '!multistep' => url('examples/ajax_example/add_more_no_js')))
. '</div>',
);
// Because we have many fields with the same values, we have to set
// #tree to be able to access them.
$form['#tree'] = TRUE;
$form['names_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('People coming to the picnic'),
// Set up the wrapper so that AJAX will be able to replace the fieldset.
'#prefix' => '<div id="names-fieldset-wrapper">',
'#suffix' => '</div>',
);
// Build the fieldset with the proper number of names. We'll use
// $form_state['num_names'] to determine the number of textfields to build.
if (empty($form_state['num_names'])) {
$form_state['num_names'] = 1;
}
for ($i = 0; $i < $form_state['num_names']; $i++) {
$form['names_fieldset']['name'][$i] = array(
'#type' => 'textfield',
'#title' => t('Name'),
);
}
$form['names_fieldset']['add_name'] = array(
'#type' => 'submit',
'#value' => t('Add one more'),
'#submit' => array('ajax_example_add_more_add_one'),
// See the examples in ajax_example.module for more details on the
// properties of #ajax.
'#ajax' => array(
'callback' => 'ajax_example_add_more_callback',
'wrapper' => 'names-fieldset-wrapper',
),
);
if ($form_state['num_names'] > 1) {
$form['names_fieldset']['remove_name'] = array(
'#type' => 'submit',
'#value' => t('Remove one'),
'#submit' => array('ajax_example_add_more_remove_one'),
'#ajax' => array(
'callback' => 'ajax_example_add_more_callback',
'wrapper' => 'names-fieldset-wrapper',
),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
// This simply allows us to demonstrate no-javascript use without
// actually turning off javascript in the browser. Removing the #ajax
// element turns off AJAX behaviors on that element and as a result
// ajax.js doesn't get loaded.
// For demonstration only! You don't need this.
if ($no_js_use) {
// Remove the #ajax from the above, so ajax.js won't be loaded.
if (!empty($form['names_fieldset']['remove_name']['#ajax'])) {
unset($form['names_fieldset']['remove_name']['#ajax']);
}
unset($form['names_fieldset']['add_name']['#ajax']);
}
return $form;
}
/**
* Callback for both ajax-enabled buttons.
*
* Selects and returns the fieldset with the names in it.
*/
function ajax_example_add_more_callback($form, $form_state) {
return $form['names_fieldset'];
}
/**
* Submit handler for the "add-one-more" button.
*
* Increments the max counter and causes a rebuild.
*/
function ajax_example_add_more_add_one($form, &$form_state) {
$form_state['num_names']++;
$form_state['rebuild'] = TRUE;
}
/**
* Submit handler for the "remove one" button.
*
* Decrements the max counter and causes a form rebuild.
*/
function ajax_example_add_more_remove_one($form, &$form_state) {
if ($form_state['num_names'] > 1) {
$form_state['num_names']--;
}
$form_state['rebuild'] = TRUE;
}
/**
* Final submit handler.
*
* Reports what values were finally set.
*/
function ajax_example_add_more_submit($form, &$form_state) {
$output = t('These people are coming to the picnic: @names',
array(
'@names' => implode(', ', $form_state['values']['names_fieldset']['name']),
)
);
drupal_set_message($output);
}
/**
* @} End of "defgroup ajax_degradation_example".
*/

View File

@ -0,0 +1,116 @@
<?php
/**
* @file
* AJAX Miscellaneous Topics.
*/
/**
* Demonstrates a clickable AJAX-enabled link using the 'use-ajax' class.
*
* Because of the 'use-ajax' class applied here, the link submission is done
* without a page refresh.
*
* When using the AJAX framework outside the context of a form or a renderable
* array of type 'link', you have to include ajax.js explicitly.
*
* @return array
* Form API array.
*
* @ingroup ajax_example
*/
function ajax_example_render_link() {
// drupal_add_library is invoked automatically when a form element has the
// '#ajax' property, but since we are not rendering a form here, we have to
// do it ourselves.
drupal_add_library('system', 'drupal.ajax');
$explanation = t("
The link below has the <i>use-ajax</i> class applied to it, so if
javascript is enabled, ajax.js will try to submit it via an AJAX call instead
of a normal page load. The URL also contains the '/nojs/' magic string, which
is stripped if javascript is enabled, allowing the server code to tell by the
URL whether JS was enabled or not, letting it do different things based on that.");
$output = "<div>" . $explanation . "</div>";
// The use-ajax class is special, so that the link will call without causing
// a page reload. Note the /nojs portion of the path - if javascript is
// enabled, this part will be stripped from the path before it is called.
$link = l(t('Click here'), 'ajax_link_callback/nojs/', array('attributes' => array('class' => array('use-ajax'))));
$output .= "<div id='myDiv'></div><div>$link</div>";
return $output;
}
/**
* AJAX-enabled link in a renderable array.
*
* Demonstrates a clickable AJAX-enabled link using a renderable array with the
* #ajax property.
*
* A link that is constructed as a renderable array can have the #ajax property,
* which ensures that the link submission is done without a page refresh. The
* href of the link is used as the ajax callback, but it degrades gracefully
* without JavaScript because if the 'nojs' portion of the href is not stripped
* out by js, the callback will return content as required for a full page
* reload.
*
* The necessary JavaScript file, ajax.js, will be included on the page
* automatically.
*
* @return array
* Form API array.
*/
function ajax_example_render_link_ra() {
$explanation = "
The link below has been rendered as an element with the #ajax property, so if
javascript is enabled, ajax.js will try to submit it via an AJAX call instead
of a normal page load. The URL also contains the '/nojs/' magic string, which
is stripped if javascript is enabled, allowing the server code to tell by the
URL whether JS was enabled or not, letting it do different things based on that.";
$build['my_div'] = array(
'#markup' => $explanation . '<div id="myDiv"></div>',
);
$build['ajax_link'] = array(
'#type' => 'link',
'#title' => t('Click here'),
// Note the /nojs portion of the href - if javascript is enabled,
// this part will be stripped from the path before it is called.
'#href' => 'ajax_link_callback/nojs/',
'#id' => 'ajax_link',
'#ajax' => array(
'wrapper' => 'myDiv',
'method' => 'html',
),
);
return $build;
}
/**
* Callback for link example.
*
* Takes different logic paths based on whether Javascript was enabled.
* If $type == 'ajax', it tells this function that ajax.js has rewritten
* the URL and thus we are doing an AJAX and can return an array of commands.
*
* @param string $type
* Either 'ajax' or 'nojs. Type is simply the normal URL argument to this URL.
*
* @return string|array
* If $type == 'ajax', returns an array of AJAX Commands.
* Otherwise, just returns the content, which will end up being a page.
*
* @ingroup ajax_example
*/
function ajax_link_response($type = 'ajax') {
if ($type == 'ajax') {
$output = t("This is some content delivered via AJAX");
$commands = array();
// See ajax_example_advanced.inc for more details on the available commands
// and how to use them.
$commands[] = ajax_command_append('#myDiv', $output);
$page = array('#type' => 'ajax', '#commands' => $commands);
ajax_deliver($page);
}
else {
$output = t("This is some content delivered via a page load.");
return $output;
}
}

View File

@ -0,0 +1,149 @@
<?php
/**
* @file
* This example shows how to use AJAX when altering a node form.
*
* It maintains a table parallel to the node table containing attributes
* 'example_1' and 'example_2' which are displayed on the node form.
* 'example_2' is displayed dynamically when example_1 is checked.
*/
/**
* Implements hook_form_FORM_ID_alter().
*
* Adds two fields to the node form, second only appears after first is enabled.
*/
function ajax_example_form_node_form_alter(&$form, &$form_state, $form_id) {
$node = $form['#node'];
$form['ajax_example_1'] = array(
'#type' => 'checkbox',
'#title' => t('AJAX Example 1'),
'#description' => t('Enable to show second field.'),
'#default_value' => $node->ajax_example['example_1'],
'#ajax' => array(
'callback' => 'ajax_example_form_node_callback',
'wrapper' => 'ajax-example-form-node',
'effect' => 'fade',
),
);
$form['container'] = array(
'#prefix' => '<div id="ajax-example-form-node">',
'#suffix' => '</div>',
);
// If the state values exist and 'ajax_example_1' state value is 1 or
// if the state values don't exist and 'example1' variable is 1 then
// display the ajax_example_2 field.
if (!empty($form_state['values']['ajax_example_1']) && $form_state['values']['ajax_example_1'] == 1
|| empty($form_state['values']) && $node->ajax_example['example_1']) {
$form['container']['ajax_example_2'] = array(
'#type' => 'textfield',
'#title' => t('AJAX Example 2'),
'#description' => t('AJAX Example 2'),
'#default_value' => empty($form_state['values']['ajax_example_2']) ? $node->ajax_example['example_2'] : $form_state['values']['ajax_example_2'],
);
}
}
/**
* Returns changed part of the form.
*
* @return array
* Form API array.
*
* @see ajax_example_form_node_form_alter()
*/
function ajax_example_form_node_callback($form, $form_state) {
return $form['container'];
}
/**
* Implements hook_node_submit().
* @see ajax_example_form_node_form_alter()
*/
function ajax_example_node_submit($node, $form, &$form_state) {
$values = $form_state['values'];
// Move the new data into the node object.
$node->ajax_example['example_1'] = $values['ajax_example_1'];
// Depending on the state of ajax_example_1; it may not exist.
$node->ajax_example['example_2'] = isset($values['ajax_example_2']) ? $values['ajax_example_2'] : '';
}
/**
* Implements hook_node_prepare().
*
* @see ajax_example_form_node_form_alter()
*/
function ajax_example_node_prepare($node) {
if (empty($node->ajax_example)) {
// Set default values, since this only runs when adding a new node.
$node->ajax_example['example_1'] = 0;
$node->ajax_example['example_2'] = '';
}
}
/**
* Implements hook_node_load().
*
* @see ajax_example_form_node_form_alter()
*/
function ajax_example_node_load($nodes, $types) {
$result = db_query('SELECT * FROM {ajax_example_node_form_alter} WHERE nid IN(:nids)', array(':nids' => array_keys($nodes)))->fetchAllAssoc('nid');
foreach ($nodes as &$node) {
$node->ajax_example['example_1']
= isset($result[$node->nid]->example_1) ?
$result[$node->nid]->example_1 : 0;
$node->ajax_example['example_2']
= isset($result[$node->nid]->example_2) ?
$result[$node->nid]->example_2 : '';
}
}
/**
* Implements hook_node_insert().
*
* @see ajax_example_form_node_form_alter()
*/
function ajax_example_node_insert($node) {
if (isset($node->ajax_example)) {
db_insert('ajax_example_node_form_alter')
->fields(array(
'nid' => $node->nid,
'example_1' => $node->ajax_example['example_1'],
'example_2' => $node->ajax_example['example_2'],
))
->execute();
}
}
/**
* Implements hook_node_update().
* @see ajax_example_form_node_form_alter()
*/
function ajax_example_node_update($node) {
if (db_select('ajax_example_node_form_alter', 'a')->fields('a')->condition('nid', $node->nid, '=')->execute()->fetchAssoc()) {
db_update('ajax_example_node_form_alter')
->fields(array(
'example_1' => $node->ajax_example['example_1'],
'example_2' => $node->ajax_example['example_2'],
))
->condition('nid', $node->nid)
->execute();
}
else {
// Cleaner than doing it again.
ajax_example_node_insert($node);
}
}
/**
* Implements hook_node_delete().
* @see ajax_example_form_node_form_alter()
*/
function ajax_example_node_delete($node) {
db_delete('ajax_example_node_form_alter')
->condition('nid', $node->nid)
->execute();
}

View File

@ -0,0 +1,116 @@
<?php
/**
* @file
* Progress bar example.
*/
/**
* Implements hook_FORMID().
*
* Build a landing-page form for the progress bar example.
*
* @see https://api.drupal.org/api/drupal/developer%21topics%21forms_api_reference.html/7#ajax_progress
*/
function ajax_example_progressbar_form($form, &$form_state) {
$form_state['time'] = REQUEST_TIME;
// We make a DIV which the progress bar can occupy. You can see this in use
// in ajax_example_progressbar_callback().
$form['status'] = array(
'#markup' => '<div id="progress-status"></div>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#ajax' => array(
// Here we set up our AJAX callback handler.
'callback' => 'ajax_example_progressbar_callback',
// Tell FormAPI about our progress bar.
'progress' => array(
'type' => 'bar',
'message' => t('Execute..'),
// Have the progress bar access this URL path.
'url' => url('examples/ajax_example/progressbar/progress/' . $form_state['time']),
// The time interval for the progress bar to check for updates.
'interval' => 1000,
),
),
);
return $form;
}
/**
* Get the progress bar execution status, as JSON.
*
* This is the menu handler for
* examples/ajax_example/progressbar/progress/$time.
*
* This function is our wholly arbitrary job that we're checking the status for.
* In this case, we're reading a system variable that is being updated by
* ajax_example_progressbar_callback().
*
* We set up the AJAX progress bar to check the status every second, so this
* will execute about once every second.
*
* The progress bar JavaScript accepts two values: message and percentage. We
* set those in an array and in the end convert it JSON for sending back to the
* client-side JavaScript.
*
* @param int $time
* Timestamp.
*
* @see ajax_example_progressbar_callback()
*/
function ajax_example_progressbar_progress($time) {
$progress = array(
'message' => t('Starting execute...'),
'percentage' => -1,
);
$completed_percentage = variable_get('example_progressbar_' . $time, 0);
if ($completed_percentage) {
$progress['message'] = t('Executing...');
$progress['percentage'] = $completed_percentage;
}
drupal_json_output($progress);
}
/**
* Our submit handler.
*
* This handler spends some time changing a variable and sleeping, and then
* finally returns a form element which marks the #progress-status DIV as
* completed.
*
* While this is occurring, ajax_example_progressbar_progress() will be called
* a number of times by the client-sid JavaScript, which will poll the variable
* being set here.
*
* @see ajax_example_progressbar_progress()
*/
function ajax_example_progressbar_callback($form, &$form_state) {
$variable_name = 'example_progressbar_' . $form_state['time'];
$commands = array();
variable_set($variable_name, 10);
sleep(2);
variable_set($variable_name, 40);
sleep(2);
variable_set($variable_name, 70);
sleep(2);
variable_set($variable_name, 90);
sleep(2);
variable_del($variable_name);
$commands[] = ajax_command_html('#progress-status', t('Executed.'));
return array(
'#type' => 'ajax',
'#commands' => $commands,
);
}