form_builder_webform.module in Form Builder 7
Same filename and directory in other branches
Form Builder integration for the Webform module.
File
modules/webform/form_builder_webform.moduleView source
<?php
/**
* @file
* Form Builder integration for the Webform module.
*/
/**
* Implements hook_menu().
*/
function form_builder_webform_menu() {
$items['node/%webform_menu/webform/form-builder'] = array(
'title' => 'Form builder',
'page callback' => 'form_builder_webform_components_page',
'page arguments' => array(
1,
),
'access callback' => 'node_access',
'access arguments' => array(
'update',
1,
),
'weight' => -1,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
return $items;
}
/**
* Implements hook_menu_alter().
*/
function form_builder_webform_menu_alter(&$items) {
$items['node/%webform_menu/webform']['page callback'] = 'form_builder_webform_components_page';
$items['node/%webform_menu/webform/components']['type'] = MENU_LOCAL_TASK;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Webform uses hook_form_alter() to add it's own submit-handler. So this
* hook is called after webform's webform_form_alter()'
*/
function form_builder_webform_form_node_form_alter(&$form, &$form_state) {
if (!empty($form['webform'])) {
// Add our own submit-handler after webform_form_submit().
$form['actions']['submit']['#submit'][] = 'form_builder_webform_node_form_submit';
}
}
/**
* Submit handler for node forms on webform content-types.
*
* Redirect to the form_builder interface by default.
*/
function form_builder_webform_node_form_submit($form, &$form_state) {
$form_state['redirect'] = 'node/' . $form_state['nid'] . '/webform/form-builder';
}
/**
* Menu callback; display a form builder interface to edit webform components.
*/
function form_builder_webform_components_page($node) {
module_load_include('inc', 'form_builder', 'includes/form_builder.admin');
// Load all components.
$components = webform_components();
foreach ($components as $component_type => $component) {
webform_component_include($component_type);
}
$build = array();
$path = drupal_get_path('module', 'webform');
$build['#attached']['css'][] = $path . '/css/webform.css';
$build['#attached']['css'][] = $path . '/css/webform-admin.css';
$build['#attached']['js'][] = $path . '/js/webform.js';
$build['#attached']['js'][] = $path . '/js/webform-admin.js';
$build['#attached']['js'][] = $path . '/js/select-admin.js';
$build['#attached']['library'][] = array(
'system',
'ui.datepicker',
);
// form_builder_interface() resets the cached form. We want the form-handlers
// of form_builder_webform_save_form() to be called before that.
$form = drupal_get_form('form_builder_webform_save_form', $node->nid);
$build[] = form_builder_interface('webform', $node->nid);
$build[] = $form;
return $build;
}
/**
* Form to save the Form Builder interface.
*
* The actual Form Builder interface is displayed as part of the
* form_builder_webform_components_page() function.
*/
function form_builder_webform_save_form($form, &$form_state, $nid) {
$form['#node'] = node_load($nid);
$form['nid'] = array(
'#type' => 'value',
'#value' => $nid,
);
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['save'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel'),
'#submit' => array(
'form_builder_webform_cancel',
),
);
return $form;
}
/**
* Validate handler; Prepare the webform components for saving.
*/
function form_builder_webform_save_form_validate($form, &$form_state) {
$node = $form['#node'];
$form_obj = FormBuilderLoader::instance()
->fromCache('webform', $node->nid);
$form_obj
->updateNode($node);
}
/**
* Submit handler; save the current Form Builder interface changes.
*/
function form_builder_webform_save_form_submit($form, &$form_state) {
// Save the node itself to update components and allow other modules to
// respond to any changes. The Form Builder cache is intentionally left in
// place so other modules can check it for changes also.
$node = $form['#node'];
node_save($node);
// Remove the cached form_builder form.
FormBuilderLoader::instance()
->fromCache('webform', $node->nid)
->delete();
drupal_set_message(t('Changes to the form have been saved.'));
}
/**
* Submit handler for the "Cancel" button.
*/
function form_builder_webform_cancel(&$form, &$form_state) {
$node = $form['#node'];
FormBuilderLoader::instance()
->fromCache('webform', $node->nid)
->delete();
drupal_set_message(t('Changes to the form have been discarded.'));
}
/**
* Implements hook_form_builder_property_groups().
*/
function form_builder_webform_form_builder_property_groups($form_type) {
$groups = array();
if ($form_type == 'webform') {
$groups['analysis'] = array(
'weight' => 4,
'title' => t('Analysis'),
);
}
return $groups;
}
/**
* Implements hook_form_builder_form_types().
*/
function form_builder_webform_form_builder_form_types() {
$types['webform'] = array(
'class' => 'FormBuilderWebformForm',
'property class' => 'FormBuilderWebformProperty',
'element class' => 'FormBuilderWebformElement',
);
return $types;
}
/**
* Implements hook_form_builder_element_types().
*
* Define the element types within webforms that are editable.
*/
function form_builder_webform_form_builder_element_types($form_type, $form_id) {
if ($form_type != 'webform') {
return;
}
$types = array();
$components = webform_components();
foreach ($components as $type => $component) {
if ($additional_fields = form_builder_webform_component_invoke($type, 'form_builder_types')) {
if ($map = _form_builder_webform_property_map($type)) {
foreach ($additional_fields as $field_name => $field_type) {
foreach ($map['properties'] as $property_name => $property_info) {
$additional_fields[$field_name]['properties'][] = $property_name;
}
}
}
$types = array_merge($types, $additional_fields);
}
}
return $types;
}
/**
* Implements hook_form_builder_properties().
*/
function form_builder_webform_form_builder_properties($form_type) {
$properties = array();
if ($form_type == 'webform') {
$components = webform_components();
foreach ($components as $component_type => $component) {
// Components providing a map to automatically list properties.
if ($map = _form_builder_webform_property_map($component_type)) {
foreach ($map['properties'] as $name => $property_map) {
if (isset($property_map['form_parents'])) {
$property_map['form'] = '_form_builder_webform_mapped_form';
}
$properties[$name] = $property_map;
}
}
// Individual components manually providing properties.
if ($additional_properties = form_builder_webform_component_invoke($component_type, 'form_builder_properties')) {
foreach ($additional_properties as $name => $property) {
$properties += array(
$name => array(),
);
$properties[$name] = array_merge($properties[$name], $property);
}
}
}
}
return $properties;
}
/**
* Implements hook_form_builder_add_element_alter().
*
* Modify a FAPI element before it is added to the form array.
*/
function form_builder_webform_form_builder_add_element_alter(&$element, $form_type, $form_id) {
if ($form_type == 'webform') {
$element['#webform_component']['nid'] = is_numeric($form_id) ? $form_id : NULL;
$element['#webform_component']['is_new'] = TRUE;
$element['#webform_component']['pid'] = 0;
$element['#webform_component']['form_key'] = $element['#key'];
unset($element['#webform_component']['cid']);
}
}
/**
* Implements hook_form_builder_preview_alter().
*
* The most common use of the preview altering is filtering field descriptions
* via filter_xss() or other functions. Webform has its own filtering function
* for this purpose.
*/
function form_builder_webform_form_builder_preview_alter(&$element, $form_type, $form_id) {
if ($form_type == 'webform') {
// Filter all descriptions for all components.
if (isset($element['#description'])) {
$element['#description'] = _webform_filter_descriptions($element['#description']);
}
if (isset($element['#default_value']) && is_string($element['#default_value'])) {
$element['#value'] = _webform_filter_values($element['#default_value'], NULL, NULL, NULL, FALSE);
}
// Let components do any extra filtering if needed.
$type = isset($element['#webform_component']['type']) ? $element['#webform_component']['type'] : $element['#form_builder']['element_type'];
if ($new_element = form_builder_webform_component_invoke($type, 'form_builder_preview_alter', $element)) {
$element = $new_element;
}
// A #title_display property of 0 (as stored by Webform) means no setting.
if (isset($element['#title_display']) && strcmp('0', $element['#title_display']) === 0) {
unset($element['#title_display']);
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* This form_alter is a workaround for an unfortunate interaction that ends up
* duplicating titles and descriptions for radios and checkboxes. By default,
* system.module defines a pre_render function for radios and checkboxes
* (form_pre_render_conditional_form_element). This pre_render adds the
* form_element theme_wrapper if there is a title or a description.
* Unfortunately, webform is already adding webform_element, which is nearly
* a copy of form_element, and if you have both, you get two titles and
* descriptions.
*
* We can't use the normal mechanism for component alters, because that happens
* in a pre_render (so removing the other pre_render isn't going to help).
*/
function form_builder_webform_form_form_builder_preview_alter(&$form, $form_state) {
if ($form['#form_builder']['form_type'] != 'webform') {
return;
}
$form['#attached']['css'][] = drupal_get_path('module', 'webform') . '/css/webform.css';
_form_builder_remove_conditional_form_element_pre_render($form);
}
/**
* Helper function to remove a system.module pre-render function from a form.
*
* @see form_builder_webform_form_form_builder_preview_alter()
*/
function _form_builder_remove_conditional_form_element_pre_render(&$form) {
foreach (element_children($form) as $key) {
$form_element =& $form[$key];
if (isset($form_element['#pre_render'])) {
// Remove system_module's pre_render function, if it exists.
$k = array_search('form_pre_render_conditional_form_element', $form_element['#pre_render']);
if ($k !== FALSE) {
unset($form_element['#pre_render'][$k]);
}
}
// Recurse through the child elements (for example, to handle cases where
// a set of radios or checkboxes with the above pre-render function is
// contained within a fieldset).
_form_builder_remove_conditional_form_element_pre_render($form_element);
}
}
/**
* Invoke a form builder callback for a webform component.
*
* If the webform component implements the callback itself, this function
* returns the result obtained from that. Otherwise, if this module has a
* default implementation of the callback on behalf of the component, the
* result obtained from that is returned.
*
* @param $component_type
* The component type as a string.
* @param $callback
* The callback to execute.
* @param ...
* Any additional parameters required by the $callback.
*/
function form_builder_webform_component_invoke($component_type, $callback) {
$args = func_get_args();
// First try invoking the callback in the webform component itself.
$result = call_user_func_array('webform_component_invoke', $args);
if (isset($result)) {
return $result;
}
// Otherwise look for a default implementation provided by this module.
$component_type = array_shift($args);
$callback = array_shift($args);
$function = '_form_builder_webform_' . $callback . '_' . $component_type;
module_load_include('inc', 'form_builder_webform', 'form_builder_webform.components');
if (function_exists($function)) {
return call_user_func_array($function, $args);
}
}
/**
* Helper function; Retrieve a component's map and merge in generic properties.
*/
function _form_builder_webform_property_map($component_type) {
static $maps;
if (!isset($maps[$component_type])) {
module_load_include('inc', 'webform', 'includes/webform.components');
$map = form_builder_webform_component_invoke($component_type, 'form_builder_map');
$map = $map ? $map : array();
$map += array(
'form_builder_type' => $component_type,
);
if (webform_component_feature($component_type, 'title')) {
$map['properties']['title'] = array(
'storage_parents' => array(
'name',
),
);
}
if (webform_component_feature($component_type, 'title_display')) {
$map['properties']['title_display'] = array(
'form_parents' => array(
'display',
'title_display',
),
'storage_parents' => array(
'extra',
'title_display',
),
);
}
if (webform_component_feature($component_type, 'default_value')) {
$map['properties']['default_value'] = array(
'class' => 'FormBuilderWebformPropertyValue',
'storage_parents' => array(
'value',
),
);
}
if (webform_component_feature($component_type, 'description')) {
$map['properties']['description'] = array(
'storage_parents' => array(
'extra',
'description',
),
);
}
if (webform_component_feature($component_type, 'placeholder')) {
$map['properties']['placeholder'] = array(
'form_parents' => array(
'display',
'placeholder',
),
'storage_parents' => array(
'extra',
'placeholder',
),
);
}
if (webform_component_feature($component_type, 'private')) {
$map['properties']['webform_private'] = array(
'form_parents' => array(
'display',
'private',
),
'storage_parents' => array(
'extra',
'private',
),
);
}
if (webform_component_feature($component_type, 'css_classes')) {
$map['properties']['css_classes'] = array(
'form_parents' => array(
'display',
'css_classes',
),
'storage_parents' => array(
'extra',
'css_classes',
),
);
}
if (webform_component_feature($component_type, 'wrapper_classes')) {
$map['properties']['wrapper_classes'] = array(
'form_parents' => array(
'display',
'wrapper_classes',
),
'storage_parents' => array(
'extra',
'wrapper_classes',
),
);
}
if (webform_component_feature($component_type, 'required')) {
$map['properties']['required'] = array(
'class' => 'FormBuilderWebformPropertyRequired',
);
}
// All components support the key property.
$map['properties']['key'] = array(
'storage_parents' => array(
'form_key',
),
);
drupal_alter('form_builder_webform_property_map', $map, $component_type);
$maps[$component_type] = $map;
}
return $maps[$component_type];
}