'Chaos Tools AJAX Demo',
'page callback' => 'ctools_ajax_sample_page',
'access callback' => TRUE,
'type' => MENU_NORMAL_ITEM,
);
$items['ctools_ajax_sample/%ctools_js/hello'] = array(
'title' => 'Hello World',
'page callback' => 'ctools_ajax_sample_hello',
'page arguments' => array(1),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['ctools_ajax_sample/%ctools_js/tablenix/%'] = array(
'title' => 'Hello World',
'page callback' => 'ctools_ajax_sample_tablenix',
'page arguments' => array(1, 3),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['ctools_ajax_sample/%ctools_js/login'] = array(
'title' => 'Login',
'page callback' => 'ctools_ajax_sample_login',
'page arguments' => array(1),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['ctools_ajax_sample/%ctools_js/animal'] = array(
'title' => 'Animal',
'page callback' => 'ctools_ajax_sample_animal',
'page arguments' => array(1),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['ctools_ajax_sample/%ctools_js/login/%'] = array(
'title' => 'Post-Login Action',
'page callback' => 'ctools_ajax_sample_login_success',
'page arguments' => array(1, 3),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implementation of hook_theme()
*
* Render some basic output for this module.
*/
function ctools_ajax_sample_theme() {
return array(
// Sample theme functions.
'ctools_ajax_sample_container' => array(
'arguments' => array('content' => NULL),
),
);
}
// ---------------------------------------------------------------------------
// Page callbacks
/**
* Page callback to display links and render a container for AJAX stuff.
*/
function ctools_ajax_sample_page() {
global $user;
// Include the CTools tools that we need.
ctools_include('ajax');
ctools_include('modal');
// Add CTools' javascript to the page.
ctools_modal_add_js();
// Create our own javascript that will be used to theme a modal.
$sample_style = array(
'ctools-sample-style' => array(
'modalSize' => array(
'type' => 'fixed',
'width' => 500,
'height' => 300,
'addWidth' => 20,
'addHeight' => 15,
),
'modalOptions' => array(
'opacity' => .5,
'background-color' => '#000',
),
'animation' => 'fadeIn',
'modalTheme' => 'CToolsSampleModal',
'throbber' => theme('image', ctools_image_path('ajax-loader.gif', 'ctools_ajax_sample'), t('Loading...'), t('Loading')),
),
);
drupal_add_js($sample_style, 'setting');
// Since we have our js, css and images in well-known named directories,
// CTools makes it easy for us to just use them without worrying about
// using drupal_get_path() and all that ugliness.
ctools_add_js('ctools-ajax-sample', 'ctools_ajax_sample');
ctools_add_css('ctools-ajax-sample', 'ctools_ajax_sample');
// Create a list of clickable links.
$links = array();
// Only show login links to the anonymous user.
if ($user->uid == 0) {
$links[] = ctools_modal_text_button(t('Modal Login (default style)'), 'ctools_ajax_sample/nojs/login', t('Login via modal'));
// The extra class points to the info in ctools-sample-style which we added to the settings.
$links[] = ctools_modal_text_button(t('Modal Login (custom style)'), 'ctools_ajax_sample/nojs/login', t('Login via modal'), 'ctools-modal-ctools-sample-style');
}
// Three ways to do our animal picking wizard.
$links[] = l(t('Wizard (no modal)'), 'ctools_ajax_sample/nojs/animal');
$links[] = ctools_modal_text_button(t('Wizard (default modal)'), 'ctools_ajax_sample/nojs/animal', t('Pick an animal'));
$links[] = ctools_modal_text_button(t('Wizard (custom modal)'), 'ctools_ajax_sample/nojs/animal', t('Pick an animal'), 'ctools-modal-ctools-sample-style');
$links[] = ctools_ajax_text_button(t('Hello world!'), "ctools_ajax_sample/nojs/hello", t('Replace text with "hello world"'));
$output = theme('item_list', $links, t('Actions'));
// This container will have data AJAXed into it.
$output .= theme('ctools_ajax_sample_container', '
' . t('Sample Content') . '
');
// Create a table that we can have data removed from via AJAX.
$header = array(t('Row'), t('Content'), t('Actions'));
$rows = array();
for($i = 1; $i < 11; $i++) {
$rows[] = array(
'class' => 'ajax-sample-row-'. $i,
'data' => array(
$i,
md5($i),
ctools_ajax_text_button("remove", "ctools_ajax_sample/nojs/tablenix/$i", t('Delete this row')),
),
);
}
$output .= theme('table', $header, $rows, array('class' => 'ajax-sample-table'));
return $output;
}
/**
* Returns a "take it all over" hello world style request.
*/
function ctools_ajax_sample_hello($js = NULL) {
$output = '' . t('Hello World') . '
';
if ($js) {
ctools_include('ajax');
$commands = array();
$commands[] = ctools_ajax_command_html('#ctools-sample', $output);
ctools_ajax_render($commands); // this function exits.
}
else {
return $output;;
}
}
/**
* Nix a row from a table and restripe.
*/
function ctools_ajax_sample_tablenix($js, $row) {
if (!$js) {
// We don't support degrading this from js because we're not
// using the server to remember the state of the table.
return MENU_ACCESS_DENIED;
}
ctools_include('ajax');
$commands = array();
$commands[] = ctools_ajax_command_remove("tr.ajax-sample-row-$row");
$commands[] = ctools_ajax_command_restripe("table.ajax-sample-table");
ctools_ajax_render($commands);
}
/**
* A modal login callback.
*/
function ctools_ajax_sample_login($js = NULL) {
// Fall back if $js is not set.
if (!$js) {
return drupal_get_form('user_login');
}
ctools_include('modal');
ctools_include('ajax');
$form_state = array(
'title' => t('Login'),
'ajax' => TRUE,
);
$output = ctools_modal_form_wrapper('user_login', $form_state);
if (empty($output)) {
// empty $output signifies success, so we'll use it as our $commands
// array.
$output = array();
$inplace = ctools_ajax_text_button(t('remain here'), 'ctools_ajax_sample/nojs/login/inplace', t('Go to your account'));
$account = ctools_ajax_text_button(t('your account'), 'ctools_ajax_sample/nojs/login/user', t('Go to your account'));
$output[] = ctools_modal_command_display(t('Login Success'), 'Login successful. You can now choose whether to '. $inplace .', or go to '. $account.'.
');
}
ctools_ajax_render($output);
}
/**
* Post-login processor: should we go to the user account or stay in place?
*/
function ctools_ajax_sample_login_success($js, $action) {
if (!$js) {
// we should never be here out of ajax context
return MENU_NOT_FOUND;
}
ctools_include('ajax');
$commands = array();
if ($action == 'inplace') {
// stay here
$commands[] = ctools_ajax_command_reload();
}
else {
// bounce bounce
$commands[] = ctools_ajax_command_redirect('user');
}
ctools_ajax_render($commands);
}
/**
* A modal login callback.
*/
function ctools_ajax_sample_animal($js = NULL, $step = NULL) {
if ($js) {
ctools_include('modal');
ctools_include('ajax');
}
$form_info = array(
'id' => 'animals',
'path' => "ctools_ajax_sample/" . ($js ? 'ajax' : 'nojs') . "/animal/%step",
'show trail' => TRUE,
'show back' => TRUE,
'show cancel' => TRUE,
'show return' => FALSE,
'next callback' => 'ctools_ajax_sample_wizard_next',
'finish callback' => 'ctools_ajax_sample_wizard_finish',
'cancel callback' => 'ctools_ajax_sample_wizard_cancel',
// this controls order, as well as form labels
'order' => array(
'start' => t('Choose animal'),
),
// here we map a step to a form id.
'forms' => array(
// e.g. this for the step at wombat/create
'start' => array(
'form id' => 'ctools_ajax_sample_start'
),
),
);
// We're not using any real storage here, so we're going to set our
// object_id to 1. When using wizard forms, id management turns
// out to be one of the hardest parts. Editing an object with an id
// is easy, but new objects don't usually have ids until somewhere
// in creation.
//
// We skip all this here by just using an id of 1.
$object_id = 1;
if (empty($step)) {
// We reset the form when $step is NULL because that means they have
// for whatever reason started over.
ctools_ajax_sample_cache_clear($object_id);
$step = 'start';
}
// This automatically gets defaults if there wasn't anything saved.
$object = ctools_ajax_sample_cache_get($object_id);
$animals = ctools_ajax_sample_animals();
// Make sure we can't somehow accidentally go to an invalid animal.
if (empty($animals[$object->type])) {
$object->type = 'unknown';
}
// Now that we have our object, dynamically add the animal's form.
if ($object->type == 'unknown') {
// If they haven't selected a type, add a form that doesn't exist yet.
$form_info['order']['unknown'] = t('Configure animal');
$form_info['forms']['unknown'] = array('form id' => 'nothing');
}
else {
// Add the selected animal to the order so that it shows up properly in the trail.
$form_info['order'][$object->type] = $animals[$object->type]['config title'];
}
// Make sure all animals forms are represented so that the next stuff can
// work correctly:
foreach ($animals as $id => $animal) {
$form_info['forms'][$id] = array('form id' => $animals[$id]['form']);
}
$form_state = array(
'ajax' => $js,
// Put our object and ID into the form state cache so we can easily find
// it.
'object_id' => $object_id,
'object' => &$object,
);
// Send this all off to our form. This is like drupal_get_form only wizardy.
ctools_include('wizard');
$output = ctools_wizard_multistep_form($form_info, $step, $form_state);
if ($output === FALSE || !empty($form_state['complete'])) {
// This creates a string based upon the animal and its setting using
// function indirection.
$animal = $animals[$object->type]['output']($object);
}
// If $output is FALSE, there was no actual form.
if ($js) {
// If javascript is active, we have to use a render array.
$commands = array();
if ($output === FALSE || !empty($form_state['complete'])) {
// Dismiss the modal.
$commands[] = ctools_ajax_command_html('#ctools-sample', $animal);
$commands[] = ctools_modal_command_dismiss();
}
else if (!empty($form_state['cancel'])) {
// If cancelling, return to the activity.
$commands[] = ctools_modal_command_dismiss();
}
else {
$commands = ctools_modal_form_render($form_state, $output);
}
ctools_ajax_render($commands);
}
else {
if ($output === FALSE || !empty($form_state['complete'])) {
return $animal;
}
else if (!empty($form_state['cancel'])) {
drupal_goto('ctools_ajax_sample');
}
else {
return $output;
}
}
}
// ---------------------------------------------------------------------------
// Themes
/**
* Theme function for main rendered output.
*/
function theme_ctools_ajax_sample_container($content) {
$output = '';
$output .= $content;
$output .= '
';
return $output;
}
// ---------------------------------------------------------------------------
// Stuff needed for our little wizard.
/**
* Get a list of our animals and associated forms.
*
* What we're doing is making it easy to add more animals in just one place,
* which is often how it will work in the real world. If using CTools, what
* you would probably really have, here, is a set of plugins for each animal.
*/
function ctools_ajax_sample_animals() {
return array(
'sheep' => array(
'title' => t('Sheep'),
'config title' => t('Configure sheep'),
'form' => 'ctools_ajax_sample_configure_sheep',
'output' => 'ctools_ajax_sample_show_sheep',
),
'lizard' => array(
'title' => t('Lizard'),
'config title' => t('Configure lizard'),
'form' => 'ctools_ajax_sample_configure_lizard',
'output' => 'ctools_ajax_sample_show_lizard',
),
'raptor' => array(
'title' => t('Raptor'),
'config title' => t('Configure raptor'),
'form' => 'ctools_ajax_sample_configure_raptor',
'output' => 'ctools_ajax_sample_show_raptor',
),
);
}
// ---------------------------------------------------------------------------
// Wizard caching helpers.
/**
* Store our little cache so that we can retain data from form to form.
*/
function ctools_ajax_sample_cache_set($id, $object) {
ctools_include('object-cache');
ctools_object_cache_set('ctools_ajax_sample', $id, $object);
}
/**
* Get the current object from the cache, or default.
*/
function ctools_ajax_sample_cache_get($id) {
ctools_include('object-cache');
$object = ctools_object_cache_get('ctools_ajax_sample', $id);
if (!$object) {
// Create a default object.
$object = new stdClass;
$object->type = 'unknown';
$object->name = '';
}
return $object;
}
/**
* Clear the wizard cache.
*/
function ctools_ajax_sample_cache_clear($id) {
ctools_include('object-cache');
ctools_object_cache_clear('ctools_ajax_sample', $id);
}
// ---------------------------------------------------------------------------
// Wizard in-between helpers; what to do between or after forms.
/**
* Handle the 'next' click on the add/edit pane form wizard.
*
* All we need to do is store the updated pane in the cache.
*/
function ctools_ajax_sample_wizard_next(&$form_state) {
ctools_ajax_sample_cache_set($form_state['object_id'], $form_state['object']);
}
/**
* Handle the 'finish' click on teh add/edit pane form wizard.
*
* All we need to do is set a flag so the return can handle adding
* the pane.
*/
function ctools_ajax_sample_wizard_finish(&$form_state) {
$form_state['complete'] = TRUE;
}
/**
* Handle the 'cancel' click on the add/edit pane form wizard.
*/
function ctools_ajax_sample_wizard_cancel(&$form_state) {
$form_state['cancel'] = TRUE;
}
// ---------------------------------------------------------------------------
// Wizard forms for our simple info collection wizard.
/**
* Wizard start form. Choose an animal.
*/
function ctools_ajax_sample_start(&$form, &$form_state) {
$form_state['title'] = t('Choose animal');
$animals = ctools_ajax_sample_animals();
foreach ($animals as $id => $animal) {
$options[$id] = $animal['title'];
}
$form['type'] = array(
'#title' => t('Choose your animal'),
'#type' => 'radios',
'#options' => $options,
'#default_value' => $form_state['object']->type,
'#required' => TRUE,
);
}
/**
* They have selected a sheep. Set it.
*/
function ctools_ajax_sample_start_submit(&$form, &$form_state) {
$form_state['object']->type = $form_state['values']['type'];
// Override where to go next based on the animal selected.
$form_state['clicked_button']['#next'] = $form_state['values']['type'];
}
/**
* Wizard form to configure your sheep.
*/
function ctools_ajax_sample_configure_sheep(&$form, &$form_state) {
$form_state['title'] = t('Configure sheep');
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name your sheep'),
'#default_value' => $form_state['object']->name,
'#required' => TRUE,
);
$form['sheep'] = array(
'#title' => t('What kind of sheep'),
'#type' => 'radios',
'#options' => array(
t('Wensleydale') => t('Wensleydale'),
t('Merino') => t('Merino'),
t('Corriedale') => t('Coriedale'),
),
'#default_value' => !empty($form_state['object']->sheep) ? $form_state['object']->sheep : '',
'#required' => TRUE,
);
}
/**
* Submit the sheep and store the values from the form.
*/
function ctools_ajax_sample_configure_sheep_submit(&$form, &$form_state) {
$form_state['object']->name = $form_state['values']['name'];
$form_state['object']->sheep = $form_state['values']['sheep'];
}
/**
* Provide some output for our sheep.
*/
function ctools_ajax_sample_show_sheep($object) {
return t('You have a @type sheep named "@name".', array(
'@type' => $object->sheep,
'@name' => $object->name,
));
}
/**
* Wizard form to configure your lizard.
*/
function ctools_ajax_sample_configure_lizard(&$form, &$form_state) {
$form_state['title'] = t('Configure lizard');
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name your lizard'),
'#default_value' => $form_state['object']->name,
'#required' => TRUE,
);
$form['lizard'] = array(
'#title' => t('Venomous'),
'#type' => 'checkbox',
'#default_value' => !empty($form_state['object']->lizard),
);
}
/**
* Submit the lizard and store the values from the form.
*/
function ctools_ajax_sample_configure_lizard_submit(&$form, &$form_state) {
$form_state['object']->name = $form_state['values']['name'];
$form_state['object']->lizard = $form_state['values']['lizard'];
}
/**
* Provide some output for our raptor.
*/
function ctools_ajax_sample_show_lizard($object) {
return t('You have a @type lizard named "@name".', array(
'@type' => empty($object->lizard) ? t('non-venomous') : t('venomous'),
'@name' => $object->name,
));
}
/**
* Wizard form to configure your raptor.
*/
function ctools_ajax_sample_configure_raptor(&$form, &$form_state) {
$form_state['title'] = t('Configure raptor');
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name your raptor'),
'#default_value' => $form_state['object']->name,
'#required' => TRUE,
);
$form['raptor'] = array(
'#title' => t('What kind of raptor'),
'#type' => 'radios',
'#options' => array(
t('Eagle') => t('Eagle'),
t('Hawk') => t('Hawk'),
t('Owl') => t('Owl'),
t('Buzzard') => t('Buzzard'),
),
'#default_value' => !empty($form_state['object']->raptor) ? $form_state['object']->raptor : '',
'#required' => TRUE,
);
$form['domesticated'] = array(
'#title' => t('Domesticated'),
'#type' => 'checkbox',
'#default_value' => !empty($form_state['object']->domesticated),
);
}
/**
* Submit the raptor and store the values from the form.
*/
function ctools_ajax_sample_configure_raptor_submit(&$form, &$form_state) {
$form_state['object']->name = $form_state['values']['name'];
$form_state['object']->raptor = $form_state['values']['raptor'];
$form_state['object']->domesticated = $form_state['values']['domesticated'];
}
/**
* Provide some output for our raptor.
*/
function ctools_ajax_sample_show_raptor($object) {
return t('You have a @type @raptor named "@name".', array(
'@type' => empty($object->domesticated) ? t('wild') : t('domesticated'),
'@raptor' => $object->raptor,
'@name' => $object->name,
));
}