count(variable_get('macro_submissions', array())), '!link' => l('End session', 'admin/build/macro/export/session/end') ); drupal_set_message(t('[Active macros: %d | !link]', $args)); } } /** * Implementation of hook_menu(). */ function macro_menu() { $items = array(); $items['admin/build/macro'] = array( 'title' => 'Macro engine', 'description' => 'Configure the Drupal macro engine. Export recorded macros or import previously recorded macros.', 'page callback' => 'drupal_get_form', 'page arguments' => array('macro_admin_settings'), 'access callback' => 'user_access', 'access arguments' => array('administer macro settings'), ); $items['admin/build/macro/export'] = array( 'title' => 'Export', 'page callback' => 'drupal_get_form', 'page arguments' => array('macro_export_macro'), 'access arguments' => array('macro access'), 'type' => MENU_LOCAL_TASK, ); $items['admin/build/macro/import'] = array( 'title' => 'Import', 'page callback' => 'drupal_get_form', 'page arguments' => array('macro_import_macro'), 'access arguments' => array('macro access'), 'type' => MENU_LOCAL_TASK, ); $items['admin/build/macro/settings'] = array( 'title' => 'Configure', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/build/macro/export/session/end'] = array( 'page callback' => 'macro_end_macro_session', 'access arguments' => array('macro access'), 'type' => MENU_CALLBACK, ); return $items; } /** * Implementation of hook_form_alter(). */ function macro_form_alter(&$form, $form_state, $form_id) { switch ($form_id) { case 'macro_export_macro': /** TODO: eventually add file saving with foldering options. $form['file-save'] = array('#type' => 'fieldset', '#title' => 'Save To File', '#description' => ''); $form['file-save']['filename'] = array('#type' => 'textfield', '#title' => 'Filename'); $form['file-save]['save'] = array('#type' => 'submit', '#value' => 'Save', '#submit' => array('macro_export_file_save_submit')); */ break; // Forms to specifically ignore for macro. case 'macro_admin_settings': case 'macro_import_macro': case 'macro_export_macro': break; default: // Add import / export buttons to each form for simplified macro saving. if (user_access('macro access') && variable_get('macro_display_actions', FALSE)) { $form['macro-actions'] = array('#type' => 'fieldset', '#title' => t('Macro actions'), '#weight' => 5000, '#collapsible' => TRUE, '#collapsed' => TRUE); $form['macro-actions']['import-data'] = array('#type' => 'submit', '#name' => 'import', '#value' => t('Import'), '#submit' => array('macro_import_action_submit')); $form['macro-actions']['export-data'] = array('#type' => 'submit', '#name' => 'export', '#value' => t('Export'), '#submit' => array('macro_export_action_submit')); if (!variable_get('macro_enabled', FALSE)) { $form['macro-actions']['export-session-data'] = array('#type' => 'submit', '#value' => t('Start session'), '#submit' => array('macro_export_session_action_submit')); } } // Add the record callback on submit. if ($form_id != 'macro_import_macro' && variable_get('macro_enabled', FALSE)) { $form['#submit'][] = 'macro_record_macro'; } // Clear the current sessions. if (variable_get('macro_delete', FALSE)) { variable_set('macro_submissions', array()); variable_set('macro_delete', FALSE); } break; } } /** * Form submit handler to redirect to the import form. */ function macro_import_action_submit($form, &$form_state) { drupal_goto('admin/build/macro/import', drupal_get_destination()); } /** * Form callback to handle macro export functionality. */ function macro_export_action_submit($form, &$form_state) { // Start a fresh session. variable_set('macro_submissions', array()); // Record the single macro. macro_record_macro($form, $form_state); // Send straight to the export form. drupal_goto('admin/build/macro/export'); } /** * Form callback to handle macro export session functionality. */ function macro_export_session_action_submit($form, &$form_state) { // Start recording submissions and clear the saved submissions for a fresh session. variable_set('macro_enabled', TRUE); variable_set('macro_submissions', array()); } /** * A form submission handler, that stores the form submissions into the variables table */ function macro_record_macro($form, &$form_state) { $macros = variable_get('macro_submissions', array()); // Remove the $form_state as it will be rebuilt on import. array_shift($form['#parameters']); // TODO: Why is it when the record method is called through the $form['#submit'] when // the action buttons are not displayed do these exception values not show up in the 'values' ? $exceptions = array('export', 'export-data', 'export-session-data', 'import-data', 'form_id', 'submit', 'reset', 'form_build_id', 'form_token', 'delete'); // Remove the unneeded values that this module implements. foreach ($exceptions as $exception) { unset($form_state['values'][$exception]); } $macro = array( 'form_id' => $form['form_id']['#value'], 'path' => $_GET['q'], 'parameters' => $form['#parameters'], 'values' => $form_state['values'], ); // Support for multistep. if ($form_state['storage']) { $macro['storage'] = $form_state['storage']; } $macros[] = $macro; variable_set('macro_submissions', $macros); return $macro; } /** * This recursively runs thru an object and converts it into an array. * This is to be called for form entries as we do not want varexport to treat any element * as an object. If varexport sees an object, it will output stdClass::__set_state, which is * not defined and we cannot define it either. So we recursively cast all objects to arrays. */ function _macro_recursively_convert_objects_to_arrays($entity) { $converted = array(); foreach (((array) $entity) as $key => $value) { if (is_array($value) || is_object($value)) { $converted[$key] = _macro_recursively_convert_objects_to_arrays($value); } else { $converted[$key] = $value; } } return($converted); } /** * A form callback that displays the macro exported. * * The output of this callback should be saved to the profiles/$profile/macros.inc file, to be * automatically played back upon completed install. * @return a textarea containing the recorded macros */ function macro_export_macro() { $form['code'] = array( '#type' => 'textarea', '#title' => 'macros exported', '#default_value' => macro_get_macro(), '#rows' => 20, ); return $form; } /** * The output of this callback should be saved to the profiles/$profile/macros.inc file, to be * automatically played back upon completed install. * @return a code representation of the recorded macro. */ function macro_get_macro() { $subs = variable_get('macro_submissions', array()); $string = ''; foreach ($subs as $key => $form) { $string .= "\$macro[$key]['form_id'] = " . var_export($form['form_id'], TRUE) . ";\n"; $string .= "\$macro[$key]['path'] = " . var_export($form['path'], TRUE) . ";\n"; $string .= "\$macro[$key]['values'] = " . var_export(_macro_recursively_convert_objects_to_arrays((array) $form['values']), TRUE) . ";\n"; // Add multistep support. if ($form['storage']) { $string .= "\$macro[$key]['storage'] = " . var_export(_macro_recursively_convert_objects_to_arrays((array) $form['storage']), TRUE) . ";\n"; } // the form parameters are being used here. array_shift($form['parameters']); $string .= "\$macro[$key]['parameters'] = " . var_export(serialize($form['parameters']), TRUE) . ";\n\n"; } return $string; } /** * A form callback that displays the macro import form. * * @return a form for importing a previously recorded macro */ function macro_import_macro() { $form['macro'] = array( '#type' => 'textarea', '#title' => 'macro to import', '#rows' => 20, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('play macro'), ); return $form; } /** * Implementation of macro_import_macro hook_submit function. * * Plays back the submitted macro. */ function macro_import_macro_submit($form, &$form_state) { include_once './includes/install.inc'; eval($form_state['values']['macro']); drupal_execute_macro($macro); } /** * Menu callback for the macro settings form. */ function macro_admin_settings() { $form['settings_general'] = array( '#type' => 'fieldset', '#title' => t('Macro settings'), '#collapsible' => TRUE, ); $form['settings_general']['macro_enabled'] = array( '#type' => 'checkbox', '#title' => t('Enable macro recording'), '#default_value' => variable_get('macro_enabled', FALSE), '#description' => t('Set whether the macro engine will record form submissions.'), ); $form['settings_general']['macro_display_actions'] = array( '#type' => 'checkbox', '#title' => 'Display Actions', '#description' => 'Add "import / export" buttons at the bottom of each form that is displayed.', '#default_value' => variable_get('macro_display_actions', FALSE), ); $form['settings_general']['macro_delete'] = array( '#type' => 'checkbox', '#title' => t('Delete recorded macro'), '#default_value' => variable_get('macro_delete', FALSE), '#description' => t('Set whether to clear previously recorded macro.'), ); return system_settings_form($form); } /** * End a macro session from a page callback with a redirect to the export form. */ function macro_end_macro_session() { variable_set('macro_enabled', FALSE); drupal_goto('admin/build/macro/export'); } /** * Attempts to programmatically submit all the forms that have been specified in the $macros collection. * * @param array * - a list of macros to execute * * @return array * - a list of results based on the macros provided. */ function drupal_execute_macro($macro) { foreach ($macro as $key => $data) { $item = menu_get_item($data['path']); if ($item && !empty($item['file'])) { include_once $item['file']; } } $results = array(); foreach ($macro as $key => $data) { $param = unserialize($data['parameters']); $form_values = array('values' => $data['values']); // Support for multistep. if ($data['storage']) { $form_values['storage'] = $data['storage']; } $args = array($data['form_id'], $form_values); $args = array_merge($args, $param); $results[] = call_user_func_array('drupal_execute', $args); if (form_get_errors()) { drupal_set_message(t("An error has occured with macro #%macro_number , form_id %form_id. Please check the errors displayed for more details.", array('%macro_number' => $key, '%form_id' => $data['form_id']))); } } return $results; }