$form) { $status = $form['source']; $delete = t('delete'); $edit = l(t('edit'), 'admin/build/forms/edit/' . $form_id); $export = l(t('export FAPI code'), 'admin/build/forms/exportfapi/' . $form_id); $exportable = l(t('export form'), 'admin/build/forms/export/' . $form_id); // Determine modified status if (($form['file'] || $form['database']) && $form['fbcache']) { $status = t('database'); $delete = t('revert'); } elseif ($form['file'] && $form['database']) { $status = t('modified'); $delete = t('revert'); } elseif ($form['file']) { $delete = ''; } elseif ($form['fbcache']) { $status = t('unsaved'); } // Link delete/revert button $delete = (!empty($delete)) ? l($delete, "admin/build/forms/delete/$form_id") : ''; // Link $form_id $form_path = db_result(db_query("SELECT path FROM {form_manager_settings} WHERE form_id='%s'", $form_id)); $form_id = (!empty($form_path)) ? l($form_id, $form_path) : l($form_id, "form/$form_id"); // Create the actual table rows. $rows[] = array( $form_id, $status, $edit . '   ' . $export . '   ' . $exportable . '   ' . $delete, ); } $bottom_links = l(t('Create a new form'), 'admin/build/forms/add') . '    ' . l(t('Import a form'), 'admin/build/forms/import'); $rows[] = array( array('data' => $bottom_links, 'colspan' => 3), ); // Spit out the themed table for output. $output = theme('table', $headers, $rows); return $output; } /** * Helper function to list the forms of all available forms. When the same form exists in multiple * locations (file, db, etc), the following precedence is used to return the form once: * - file * - Form Manager table (database) * - Form Builder cache (temporary storage) * * @return * An associative array containing all the forms. * - Key: form_id * - name: form_id * - source: the source (database or file) */ function _form_manager_list_forms() { // Get list of forms in files $file_forms = _form_manager_list_forms_by_type('file'); // Get list of forms in form manager table $db_forms = _form_manager_list_forms_by_type('database'); // Get list of forms in form builder cache $fb_forms = _form_manager_list_forms_by_type('fbcache'); // Shuffle the sources together. Put db forms after file forms, so that they'll take precedence. $forms = array_merge($file_forms, $db_forms, $fb_forms); foreach ($forms as $form_id => $form) { $forms[$form_id]['file'] = $file_forms[$form_id]['file']; $forms[$form_id]['database'] = $db_forms[$form_id]['database']; $forms[$form_id]['fbcache'] = $fb_forms[$form_id]['fbcache']; } return $forms; } /** * Helper function to list the forms of a given source of a given type (file, database, etc). * * @param $type * The source type of the form. * - file: The forms stored in code in the forms directory with the filename of form_id.inc * - database: The forms stored in the database. These may be unique or may override forms with the same form_id stored in a file. Forms stored in the database will take precedence. * - fbcache: The forms stored in Form Builder's cache. Generally, these are forms that haven't been saved yet. These will take prcedence over all other forms. * * @return * An associative array containing all the forms of the given source type. * - Key: form_id * - name: form_id * - source: the source (form builder cache, database, or file) */ function _form_manager_list_forms_by_type($type = 'file') { $forms = array(); switch($type) { case 'file': $forms_path = drupal_get_path('module', 'form_manager') . '/forms'; $mask = '.*\.inc$'; $files = file_scan_directory($forms_path, $mask); foreach ($files as $file) { $forms[$file->name] = array( 'name' => $file->name, 'source' => 'file', $type => TRUE, ); } break; case 'database': $results = db_query("SELECT form_id FROM {form_manager_forms}"); while ($data = db_fetch_array($results)) { $forms[$data['form_id']] = array( 'name' => $data['form_id'], 'source' => 'database', $type => TRUE, ); } break; case 'fbcache': $results = db_query("SELECT form_id FROM {form_builder_cache} WHERE sid='%s'", session_id()); while ($data = db_fetch_array($results)) { $forms[$data['form_id']] = array( 'name' => $data['form_id'], 'source' => 'file builder cache', $type => TRUE, ); } break; } return $forms; } /** * form function for exporting an entire FAPI array. */ function form_manager_export_form($form, $form_id) { module_load_include('inc', 'form_builder', 'includes/form_builder.api'); module_load_include('inc', 'form_builder', 'includes/form_builder.cache'); module_load_include('inc', 'form_manager', 'includes/form_manager.pages'); $form = array(); $form_state = array(); $current = array(); $current = form_builder_cache_load('form_manager', $form_id); if (empty($current)) { form_manager_get_form($current, $form_state, $form_id); } $form['form_manager'] = array( '#type' => 'textarea', '#title' => t('Export code'), '#default_value' => form_manager_export($current), '#attributes' => array('readonly' => 'readonly', 'style' => 'font-family: monospace;'), '#rows' => 20, ); return $form; } /** * Menu callback for exporting an entire FAPI array. * * @param $form_id * The form_id of the form to retreive. * * @return * HTML output; the content of the page. */ function form_manager_export_form_page($form_id) { $links = array(); $links[] = array( 'title' => t('list forms'), 'href' => 'admin/build/forms', 'attributes' => '', 'query' => '', 'fragment' => '', ); $links[] = array( 'title' => t('edit'), 'href' => 'admin/build/forms/edit/' . $form_id, 'attributes' => '', 'query' => '', 'fragment' => '', ); $output = ''; $output .= theme('links', $links, array('class' => 'tabs secondary')); $output .= drupal_get_form('form_manager_export_form', $form_id); return $output; } function form_manager_export($form) { $output = ''; $output .= form_manager_export_recurse($form); //$output .= 'return $form;'; return $output; } /** * Recursive function for pretty-printing of FAPI arrays. */ function form_manager_export_recurse($form, $parents = array()) { $output = ''; form_manager_reset_element_keys($form); // Sort this level of the array according to weight. uasort($form, 'element_sort'); // Print out this parent element and it's properties. $properties = element_properties($form); $omit = array('#form_builder', '#key', '#form_manager_form_id', '#element_name'); if (count($properties)) { $output .= form_manager_export_variable_name($parents, $form) . " = array(\n"; foreach (element_properties($form) as $property) { if (!in_array($property, $omit)) { if (is_array($form[$property])) { $output .= " '". $property . "' => array(\n"; foreach ($form[$property] as $key => $value) { $output .= " '" . $key . "' => '". str_replace("'", "\'", $value) ."',\n"; } $output .= " ),\n"; } else { $output .= " '". $property . "' => '" . str_replace("'", "\'", $form[$property]) ."',\n"; } } } $output .= ");\n"; } else { //$output .= form_manager_export_variable_name($parents) . " = array();\n"; } foreach (element_children($form) as $key) { $parents[] = $key; $output .= form_manager_export_recurse($form[$key], $parents); array_pop($parents); } return $output; } function form_manager_export_variable_name($parents, $form = array()) { $output = '$form'; foreach ($parents as $parent) { $output .= "['". $parent ."']"; } return $output; } /** * Look for element_name and change the Form Builder default key name to this value. * * This really just makes a copy of the sub-array with the new key name, and unsets * the original sub-array. It also unsets the element_name element just to make sure * this doesn't act multiple times on the same element. */ function form_manager_reset_element_keys(&$form) { foreach ($form as $key => $element) { if (!empty($element['#element_name']) && is_array($element) && $element['#element_name'] != $key) { $element_name = $element['#element_name']; unset($element['#element_name']); $form[$element_name] = $element; unset($form[$key]); } } } /** * Main form building interface. Can be used as a menu callback. * * @param $form_type * The type of form being edited. Usually the name of the providing module. * @param $form_id * The unique identifier for the form being edited with the type. */ function form_manager_edit($form_type, $form_id) { module_load_include('inc', 'form_builder', 'includes/form_builder.api'); module_load_include('inc', 'form_builder', 'includes/form_builder.cache'); module_load_include('inc', 'form_builder', 'includes/form_builder.admin'); // Set the current form type (used for display of the sidebar block). form_builder_active_form($form_type, $form_id); // Is this a noop? $form = _form_manager_load_form($form_type, $form_id); $output = ''; $output .= drupal_get_form('form_builder_preview', $form, $form_type, $form_id); $output .= drupal_get_form('form_builder_positions', $form, $form_type, $form_id); $output .= drupal_get_form('form_manager_edit_actions'); return $output; } function _form_manager_load_form($form_type, $form_id) { // Load the current state of the form, or create a new cache if needed. $form = form_builder_cache_load($form_type, $form_id); if (!$form) { $form = form_builder_load_form($form_type, $form_id); form_builder_cache_save($form_type, $form_id, $form); } return $form; } /** * Form function to generate some options below the Form Builder set of forms to: * - Save -- Copies the Form Builder cache into the Form Manager table (after making some slight alterations) * - Export -- Generates a FAPI-appropriate array to copy * - Cancel -- Cancel the form changes and go back to the main admin page */ function form_manager_edit_actions() { $form = array(); $form['formid'] = array( '#type' => 'hidden', '#value' => check_plain(arg(4)), ); // Buttons $form['save'] = array( '#type' => 'submit', '#value' => t('Save'), ); $form['export'] = array( '#type' => 'submit', '#value' => t('Export'), ); $form['cancel'] = array( '#type' => 'submit', '#value' => t('Cancel'), ); return $form; } /** * Implementation of hook_submit to handle the form_manager_edit_actions form. */ function form_manager_edit_actions_submit(&$form, &$form_values) { $values = $form_values['values']; $op = $form_values['values']['op']; switch($op) { case 'Cancel': // TODO: Use a confirm form to confirm and delete the form_builder_cache cached form drupal_goto('admin/build/forms'); break; case 'Save': // TODO: Use a confirm form to confirm and delete the form_builder_cache cached form _form_manager_edit_actions_submit_save($values); break; case 'Export': // TODO: Use a confirm form to confirm and delete the form_builder_cache cached form drupal_goto('admin/build/forms/export/' . $values['formid']); break; } } /** * Helper function to save the form sent to form_manager_edit_actions_submit() */ function _form_manager_edit_actions_submit_save($values) { $session = session_id(); // Copy the form_builder_cache'ed form to the form_manager_forms table (updating if necesary) $fid = db_result(db_query("SELECT fid FROM {form_manager_forms} WHERE form_id='%s'", $values['formid'])); $form_cache = db_fetch_object(db_query("SELECT * FROM {form_builder_cache} WHERE sid='%s' AND form_id='%s'", $session, $values['formid'])); // Rename the element keys if they have been explicitely defined in the Form Builder edit page. $form = unserialize($form_cache->data); form_manager_reset_element_keys($form); foreach (element_children($form) as $key) { if (is_array($form[$key])) { form_manager_reset_element_keys($form[$key]); } } $form_cache->data = serialize($form); // We don't care about session ID since the form will be saved permanently and not cached by session. Chuck it. unset($form_cache->sid); // Update the timestamp on the form. $form_cache->updated = time(); // We need to know if this is a new form or if we're updating an existing form so that drupal_write_record // can do the appropriate thing to save it. $update = array(); if ($fid) { $form_cache->fid = $fid; $update[] = 'fid'; } drupal_write_record('form_manager_forms', $form_cache, $update); // Remove the form_builder_cache form (all for this session ID only) db_query("DELETE FROM {form_builder_cache} WHERE sid='%s' AND form_id='%s'", $session, $values['formid']); // redirect to admin page drupal_goto('admin/build/forms'); } /** * confirm_delete function * Confirm that the admin wants to delete the given form */ function form_manager_confirm_delete(&$form_state) { $form_id = check_plain(arg(4)); $form_data = array( 'form_id' => $form_id, 'database' => db_result(db_query("SELECT fid FROM {form_manager_forms} WHERE form_id='%s'", $form_id)), 'fbcache' => db_result(db_query("SELECT form_id FROM {form_builder_cache} WHERE sid='%s' AND form_id='%s'", session_id(), $form_id)), ); if (!empty($form_data['database']) || !empty($form_data['fbcache'])) { $form_state['storage']['form_data'] = $form_data; $form = array(); return confirm_form( $form, t('Are you sure you want to delete the form %form_id?', array('%form_id' => $form_id)), 'admin/build/forms', t('You are about to delete the delete the form %form_id. This action cannot be undone.', array('%form_id' => $form_id)), t('Delete') ); } else { drupal_set_message(t('That form does not exist in the database.'), $type = 'error', FALSE); drupal_goto('admin/build/forms'); } } /** * Form submit handler for the confirm delete form. * Handles deletion of the specified form from the database. */ function form_manager_confirm_delete_submit($form, &$form_state) { $form_data = $form_state['storage']['form_data']; if (!empty($form_data['database'])) { db_query("DELETE FROM {form_manager_forms} WHERE form_id='%s'", $form_data['form_id']); } if (!empty($form_data['fbcache'])) { db_query("DELETE FROM {form_builder_cache} WHERE sid='%s' AND form_id='%s'", session_id(), $form_data['form_id']); } drupal_set_message(t('The form %formid has been deleted from the database.', array('%formid' => $form_data['form_id'])), $type = 'status', FALSE); drupal_goto('admin/build/forms'); } /** * Form function to allow admin users to create a new form from scratch. */ function form_manager_add() { $form = array(); // use 'formid' instead of 'form_id' in order to not confuse it with *this* form's form_id $form['formid'] = array( '#type' => 'textfield', '#title' => t('Form ID'), '#description' => t('This is the internal form_id of the form that will be used to uniquely identify it. Only letters, numbers, and underscores are allowed.'), '#required' => TRUE, ); $form['path'] = array( '#type' => 'textfield', '#title' => t('Path'), '#description' => t('The path to the page that will display the form. ex. form/form_id'), '#required' => TRUE, ); $form['published'] = array( '#type' => 'checkbox', '#title' => t('Published?'), '#description' => t('Whether or not the form is published. If it is not published, then only users with the administer form_manager forms will be able to view it. NOTE: This is not currently used. Future versions will allow you to enable/disable forms, but this functionality does not yet exist.'), '#default_value' => TRUE, ); $form['multistep'] = array( '#type' => 'checkbox', '#title' => t('Multi-Step?'), '#description' => t('Whether or not the form is multi-step. Top-level fieldsets will be used to determine "pages" of form elements. NOTE: this is not currently implemented; all forms with fieldsets are currently assumed to be multi-step forms. This will change in future versions, and this checkbox is here to provide future compatability.'), '#default_value' => TRUE, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Create form'), ); return $form; } /** * @todo Ensure that the form_id submitted doesn't exist already. * - as a file-based form * - in the database or in Form Builder cache * - as a hook or form from another module */ function form_manager_add_validate($form, &$form_state) { // TODO: this function } /** * Submit handler for add form function */ function form_manager_add_submit($form, &$form_state) { // Save settings in the form_manager_settings table $form_data = array( 'form_id' => $form_state['values']['formid'], 'path' => $form_state['values']['path'], 'status' => $form_state['values']['published'], 'multistep' => $form_state['values']['multistep'], 'update' => time(), ); // TODO: Save the data in the form_manager_settings table, table needs to be added to hook_schema //$form_data = (object) $form_data; drupal_write_record('form_manager_settings', $form_data); // Make sure the new form's path will be available // TODO: I don't like rebuilding the menu_router table for the simple addition of a new callback function path. // Find a better way to do this, like inserting it as a single row in {menu_router}. menu_router_build(); // Send the user to the Form Builder edit page drupal_goto('admin/build/forms/edit/' . $form_data['form_id']); } function form_manager_admin_exportable($formid = FALSE) { $output = ''; ctools_include('export'); $result = ctools_export_load_object('form_manager_forms', 'conditions', array('form_id' => $formid)); drupal_set_title(check_plain($result->description)); $code = ctools_export_object('form_manager_forms', array_shift($result), ''); $lines = substr_count($code, "\n"); return drupal_get_form('ctools_export_form', $code, 'Form Manager form'); } function form_manager_admin_import() { $form['formid'] = array( '#type' => 'textfield', '#title' => t('Form name'), '#description' => t('Enter the name of the new form. This is optional and is not necessary if you do not wish to rename the object.'), ); $form['object'] = array( '#type' => 'textarea', '#title' => t('Paste form code here'), '#rows' => 15, ); $form['submit'] = array( '#value' => t('Import'), '#type' => 'submit', ); return $form; } /** * Make sure that an import actually provides a handler. */ function form_manager_admin_import_validate(&$form, &$form_state) { // First, run the PHP and turn the input code into an object. ob_start(); eval($form_state['values']['object']); ob_end_clean(); // The object should appear as $formmanager. This was the "identifier" set in the export section of the schema. if (empty($formmanager)) { $errors = ob_contents(); if (empty($errors)) { $errors = t('No formmanager object found.'); } form_error($form['object'], t('Unable to get a formmanager from the import. Errors reported: @errors', array('@errors' => $errors))); } $formmanager->data = str_replace("\r\n", ' ', $formmanager->data); // make sure we're not importing an existing form if (!empty($form_state['values']['formid'])) { $formmanager->form_id = $form_state['values']['formid']; } if (form_manager_formid_exists($formmanager->form_id)) { $errors = t('That form_id @formid already exists.', array('@formid' => $formmanager->form_id)); form_error($form['object'], t('Unable to get a formmanager object from the import. Errors reported: @errors', array('@errors' => $errors))); } $form_state['obj'] = $formmanager; } /** * Save the imported object. */ function form_manager_admin_import_submit(&$form, &$form_state) { $formmanager = $form_state['obj']; if (!empty($form_state['values']['name'])) { $formmanager->form_id = $form_state['values']['name']; } form_manager_admin_import_save($formmanager); $form_state['redirect'] = 'admin/build/forms/edit/' . $formmanager->form_id; } function form_manager_admin_import_save($formmanager) { $update = array(); if (!empty($formmanager->name)) { if (form_manager_formid_exists($formmanager->form_id)) { $update[] = 'fid'; } } drupal_write_record('form_manager_forms', $formmanager, $update); } function form_manager_formid_exists($formid) { $fid = db_result(db_query("SELECT fid FROM {form_manager_forms} WHERE form_id='%s'", $formid)); if (!empty($fid)) { return 'fid'; } return FALSE; }