You are here

arrange_fields.module in Arrange Fields 6

Same filename and directory in other branches
  1. 7 arrange_fields.module

File

arrange_fields.module
View source
<?php

// $Id$

/**
 * Implementation of hook_menu().
 *
 * @return array
 */
function arrange_fields_menu() {
  $items = array();
  $items["arrange-fields/%"] = array(
    "title" => "Arrange fields - Form",
    "page callback" => "arrange_fields_display_form",
    "page arguments" => array(
      1,
    ),
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_CALLBACK,
  );
  $items["arrange-fields/webform/%"] = array(
    "title" => "Arrange fields - Webform",
    "page callback" => "arrange_fields_display_webform",
    "page arguments" => array(
      2,
    ),
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_CALLBACK,
  );
  $items["arrange-fields/other/%"] = array(
    "title" => "Arrange fields - Other Forms",
    "page callback" => "arrange_fields_display_otherform",
    "page arguments" => array(
      2,
    ),
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_CALLBACK,
  );
  $items["admin/content/types/arrange-fields"] = array(
    "title" => "Arrange form fields",
    "page callback" => "arrange_fields_display_main",
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_LOCAL_TASK,
    "weight" => 5,
  );

  // Admin settings menu...
  $items["admin/settings/arrange-fields"] = array(
    "title" => "Arrange fields",
    "description" => "Arrange fields and components on your forms.",
    "page callback" => "arrange_fields_display_main",
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_NORMAL_ITEM,
  );
  $items["admin/settings/arrange-fields/forms"] = array(
    "title" => "Forms",
    "type" => MENU_DEFAULT_LOCAL_TASK,
    "weight" => 1,
  );
  $items["admin/settings/arrange-fields/settings"] = array(
    "title" => "Settings",
    "page callback" => "drupal_get_form",
    "page arguments" => array(
      "arrange_fields_settings_form",
    ),
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_LOCAL_TASK,
    "weight" => 2,
  );
  $items["admin/settings/arrange-fields/export"] = array(
    "title" => "Export",
    "page callback" => "drupal_get_form",
    "page arguments" => array(
      "arrange_fields_export_form",
    ),
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_LOCAL_TASK,
    "weight" => 3,
  );
  $items["admin/settings/arrange-fields/import"] = array(
    "title" => "Import",
    "page callback" => "drupal_get_form",
    "page arguments" => array(
      "arrange_fields_import_form",
    ),
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_LOCAL_TASK,
    "weight" => 4,
  );

  // CCK/Content type menu......
  // Get a list of content types and create menu hooks for them.
  // first, let's make sure that CCK is installed (by checking for the existence
  // of the content_types() function).
  if (function_exists("content_types")) {
    foreach (node_get_types() as $type) {
      $type_name = $type->type;
      $content_type = content_types($type_name);
      $type_url_str = $content_type['url_str'];
      $items["admin/content/node-type/{$type_url_str}/arrange-fields"] = array(
        "title" => "Arrange fields",
        "page callback" => "arrange_fields_display_form",
        "page arguments" => array(
          $type_name,
        ),
        "access arguments" => array(
          "administer arrange fields",
        ),
        "type" => MENU_LOCAL_TASK,
        "weight" => 3,
      );
    }
  }

  // If webform has been installed, create the menu hooks so it ties in
  // nicely with webform.
  if ($GLOBALS["arrange_fields_webform_installed"]) {

    // I will include menu hooks for both major versions of webform I wish
    // to support.  Having both here shouldn't harm anything.
    // This is for webform 2.9
    $items["node/%webform_menu/edit/arrange-fields"] = array(
      "title" => "Arrange fields",
      "page callback" => "arrange_fields_display_webform",
      "page arguments" => array(
        1,
      ),
      "access arguments" => array(
        "administer arrange fields",
      ),
      "type" => MENU_LOCAL_TASK,
      "weight" => 3,
    );

    // This is for webform 3.x.
    $items["node/%webform_menu/webform/arrange-fields"] = array(
      "title" => "Arrange fields",
      "page callback" => "arrange_fields_display_webform",
      "page arguments" => array(
        1,
      ),
      "access arguments" => array(
        "administer arrange fields",
      ),
      "type" => MENU_LOCAL_TASK,
      "weight" => 3,
    );
  }

  // Menu hooks for the two popups used with CCK and webform fields...
  $items["arrange-fields/popup-edit-field"] = array(
    "title" => "Arrange fields - Form",
    "page callback" => "arrange_fields_popup_edit_field",
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_CALLBACK,
  );
  $items["arrange-fields/popup-close-window"] = array(
    "title" => "Arrange fields - Form",
    "page callback" => "arrange_fields_popup_close_window",
    "access arguments" => array(
      "administer arrange fields",
    ),
    "type" => MENU_CALLBACK,
  );
  return $items;
}
function arrange_fields_perm() {
  return array(
    "administer arrange fields",
  );
}

/**
 * This function returns a form which we will use to configure
 * the arrange_fields module.  It is called
 * from menu item:  admin/settings/arrange-fields/settings
 *
 * @return array
 */
function arrange_fields_settings_form() {
  $form = array();
  $form["arrange_fields_grid_width"] = array(
    "#type" => "select",
    "#title" => t("Grid width"),
    "#options" => array(
      1 => "1px",
      5 => "5px",
      10 => "10px (default)",
      15 => "15px",
      20 => "20px",
    ),
    "#default_value" => variable_get("arrange_fields_grid_width", 10),
    "#description" => t("This setting determines the spacing between elements on the grid (when arranging fields).\n                         For more free-form movement, set to 1px.  More rigid, boxy movement, set to 20px.\n                         If you are unsure what to set, leave this at 10px."),
  );
  $form["arrange_fields_other_form_ids"] = array(
    "#type" => "textarea",
    "#title" => t("Additional form_id's"),
    "#default_value" => variable_get("arrange_fields_other_form_ids", ""),
    "#description" => t("Enter any additional form_id's you would like to be\n                      able to arrange, <strong>one per line</strong>.  \n                      Be aware that\n                      this module (Arrange Fields) may not be able to\n                      arrange forms with complex structures.  Also, form_ids that\n                      are longer than 100 characters are allowed, but may not work \n                      correctly if the first 100 characters are identical between two\n                      or more ids.\n                      <br>\n                      After you enter complete form_id's here and hit Save,\n                      you will be able to arrange them by clicking on them\n                      at the bottom of the ") . l("Arrange Fields admin settings page", "admin/settings/arrange-fields") . ".\n                      <br>\n                      " . t("Example:") . " <br>\n                      &nbsp;&nbsp;<strong>user_register</strong>\n                      <br>\n                      &nbsp;&nbsp;<strong>my_custom_form_id_1</strong>\n                      ",
  );
  return system_settings_form($form);
}

/**
 * This function simply provides textareas for the user to copy/paste from
 * which will allow them to export the arrangement of their forms.
 * It does not really need to use the form API, since there is no submission,
 * but I am doing this to make it easier to program.
 *
 */
function arrange_fields_export_form() {
  $form = array();

  // We need to look for every type of form in which we have arrangement data on.
  // The easiest way to do this is to look through the variables table.
  $res = db_query("SELECT * FROM {variable}\n                   WHERE name LIKE 'arrange_fields_position_data_%%' \n                   ORDER BY name ");
  while ($cur = db_fetch_array($res)) {
    $db_name = $cur["name"];

    // Convert it to a plain string.
    if (!($db_value = unserialize($cur["value"]))) {
      continue;
    }

    // Make sure we actually contain arrangement data, and not just an
    // empty string.
    if (trim($db_value) == "") {
      continue;
    }

    // Get the form_id from the name.
    $form_id = str_replace("arrange_fields_position_data_", "", $db_name);

    // If this is a webform, verify that it still exists and has not been
    // deleted.
    if (strstr($form_id, "webform_client_form")) {
      $nid = str_replace("webform_client_form_", "", $form_id);
      if (!($node = node_load($nid))) {
        continue;
      }

      // Otherwise, grab the title so we can append that to the form_id.
      $form_id .= " (Webform: {$node->title})";
    }
    $value = $db_name . "~~!af-name-sep!~~" . $db_value . "~~!af-val-sep!~~";
    $form[$db_name] = array(
      "#title" => $form_id,
      "#type" => "textarea",
      "#default_value" => $value,
    );
  }
  return $form;
}

/**
 * We will use this form to let users paste in definitions for form arrangements
 * which they got from the "export" screen.
 *
 */
function arrange_fields_import_form() {
  $form = array();
  $form["import"] = array(
    "#title" => "Import",
    "#type" => "textarea",
    "#rows" => 15,
    "#description" => t("Use this box to import definitions from an Arrange Fields export.\n                       You may paste in more than one definition at a time."),
  );
  $form["submit"] = array(
    "#type" => "submit",
    "#value" => "Import",
  );
  return $form;
}

/**
 * This function will handle the actual importing of arrange fields definitions.
 *
 */
function arrange_fields_import_form_submit($form, $form_state) {
  $import = trim($form_state["values"]["import"]);
  $definitions = explode("~~!af-val-sep!~~", $import);
  foreach ($definitions as $definition) {
    $temp = explode("~~!af-name-sep!~~", $definition);
    $db_name = trim($temp[0]);
    $db_value = trim($temp[1]);
    if ($db_name == "") {
      continue;
    }

    // Let's use variable_set to import the information.
    variable_set($db_name, $db_value);
    drupal_set_message($db_name . " " . t("definition was imported successfully."));
  }
}

/**
 * This function uses recursion to go through an array (presumably $form).
 * It will return back an array of references to fields which we might
 * want to place wrappers around.
 * 
 * It is meant to be used by arrange_fields_add_draggable_wrappers().
 *
 * @param array $arr
 * @param string $previous_key
 * @return array
 */
function arrange_fields_get_form_fields(&$arr, $previous_key = "") {
  $rtn = array();
  foreach ($arr as $name => &$element) {
    if (is_array($element)) {

      // Is this the buttons element on a CCK form?  If so, we want
      // the entire buttons array, NOT each individual button (this was
      // causing a bug where Diff's View changes and the Delete button
      // were not arrangeable.
      if ($name == "buttons" && $element["#type"] == "" && $element["submit"]["#type"] == "submit") {

        // Okay, we found it (most likely, anyway).
        // Let's add it to our return array.
        $rtn[] = array(
          "name" => $name,
          "element" => &$element,
        );
      }
      elseif (isset($element["#type"]) && $element["#type"] != "hidden" && $element["#type"] != "token" && $element["#type"] != "value") {

        // We found an element!  Save its reference in our return array.
        // If the name is numeric (as is the case with some CCK elements), we
        // actually want to use the previous element.
        if (is_numeric($name) && $previous_key != "") {

          // Let's use the previous element here, which should be $arr.
          $rtn[] = array(
            "name" => $previous_key,
            "element" => &$arr,
          );
        }
        else {

          // Add to our return array.
          $rtn[] = array(
            "name" => $name,
            "element" => &$element,
          );
        }
      }
      else {

        // This is not a field, but possibly another level of nesting
        // in our $form.  So, let's recursively call this function
        // and explore this new level.
        $temp = arrange_fields_get_form_fields($element, $name);

        // It's possible we did indeed find some fields in the nested sub-array
        // we just explored.  If so, let's add it to the end of our on-going
        // return array.
        $rtn = array_merge($rtn, $temp);
      }
    }
  }
  return $rtn;
}

/**
 * In this function, we are going to add div's around elements we wish to make
 * draggable on the page.  The jQuery can then grab onto those divs to make
 * them draggable.
 *
 * @param array $form
 * @param string $form_type
 * @param bool $disable_buttons
 */
function arrange_fields_add_draggable_wrappers(&$form, $form_type, $disable_buttons = FALSE) {

  // Figure out just what are the fields for this form.
  if ($form_type == "webform") {

    // If we are dealing with a webform, then we need to make sure
    // we get every element in $form["submitted"], as well as the submit
    // buttons.  I am doing it this way because webform sometimes represents
    // its fields in a way that the arrange_fields_get_form_fields cannot
    // find.
    $fields = array();
    $fields[] = array(
      "name" => "submit",
      "element" => &$form["submit"],
    );
    $fields[] = array(
      "name" => "next",
      "element" => &$form["next"],
    );
    $fields[] = array(
      "name" => "previous",
      "element" => &$form["previous"],
    );
    foreach ($form["submitted"] as $name => &$element) {
      if (is_array($element)) {
        $fields[] = array(
          "name" => $name,
          "element" => &$element,
        );
      }
    }

    // If the user is using webform 3.x, there may also be an "actions"
    // array element, which contains our buttons.
    if (is_array($form["actions"])) {
      foreach ($form["actions"] as $name => &$element) {
        if (is_array($element)) {
          $fields[] = array(
            "name" => $name,
            "element" => &$element,
          );
        }
      }
    }

    // We may need the webform's node later, so let's load it now.
    if (isset($form["details"]["nid"]["#value"])) {
      $webform_nid = $form["details"]["nid"]["#value"];
      $webform_node = node_load($webform_nid);
    }

    // If using the captcha module, add that element as well.
    if (is_array($form["captcha"])) {
      $fields[] = array(
        "name" => "captcha",
        "element" => &$form["captcha"],
      );
    }

    // If using the mollom captcha module, add that element as well
    if (isset($form["mollom"]) && is_array($form["mollom"])) {
      $fields[] = array(
        "name" => "mollom",
        "element" => &$form["mollom"],
      );
    }
  }
  else {

    // Not using webform, so we can use the function "get_form_fields()".
    $fields = arrange_fields_get_form_fields($form);
  }

  // Make each field draggable by adding wrappers to it.
  foreach ($fields as $field_arr) {
    $field_name = $field_arr["name"];
    $element =& $field_arr["element"];

    // We need to make sure that field_name does not contain trouble characters, like spaces.
    // The Profile module lets you create fieldsets with spaces in it, for example.
    $field_name = preg_replace("/[^a-zA-Z0-9]/", "-", $field_name);
    if ($element["#arrange_fields_added_wrappers"] == TRUE) {

      // We have already added wrappers to this element.  This can possibly
      // happen when we are working on Webforms, based on the way the logic works,
      // we can have the same elements in there twice.
      continue;
    }

    // Make sure we are not still looking at a hidden or otherwise unwanted
    // field which might be present because of the way I am doing Webform fields.
    if ($element["#type"] == "hidden" || $element["#type"] == "token" || $element["#type"] == "value") {

      // Just skip to the next one.
      continue;
    }
    $field_type = $element["#type"];
    $edit_link = "";
    $edit_link = "<div class='arrange-fields-control-handle'>";

    // If this is a CCK field, give it an option to edit in a popup.
    if ($GLOBALS["arrange_fields_editing_type"] == $form_type && $form_type != "" && substr($field_name, 0, 6) == "field_") {
      $edit_link .= "<a href='javascript: arrangeFieldsPopupEditField(\"{$form_type}\", \"{$field_name}\");'>cck</a>";
    }

    // If this is a webform field, give it an option to edit in a popup.
    if ($form_type == "webform" && is_array($webform_node->webform["components"])) {

      // We need to figure out the webform_component (cid) of this field.
      // We will do this by scanning the webform["components"] array for
      // this field_name
      $cid = FALSE;
      foreach ($webform_node->webform["components"] as $tcid => $c_arr) {
        if ($c_arr["form_key"] == $field_name) {
          $cid = $tcid;
          break;
        }
      }
      if ($cid) {
        $webform_field_id = $webform_nid . "_" . $cid;
        $edit_link .= "<a href='javascript: arrangeFieldsPopupEditField(\"{$form_type}\", \"{$webform_field_id}\");'>wf</a>";
      }
    }
    $edit_link .= "<span class='arrange-fields-handle-region'> &nbsp; &nbsp; </span>\n                    <a href='javascript: arrangeFieldsDialogConfigureField(\"{$field_name}\",\"{$field_type}\");' class='arrange-fields-config-link' \n                    title='Configure this field'>&nbsp;</a>";
    $edit_link .= "</div>";
    $css_class = "draggable-form-item";
    $css_id = "edit-{$field_name}-draggable-wrapper";

    // Are we dealing with a fieldset?**
    if ($element["#type"] == "fieldset" && $field_name != "captcha") {

      // Fieldsets get an extra css_class and the ID changes.
      $css_class .= " draggable-form-item-fieldset";
      $css_id = "edit-{$field_name}-fieldset-draggable-wrapper";
    }

    // **The bit about captcha is a hack put in to ensure that Captcha is NOT considered a fieldset.
    // It seems that at different times during form rendering, the #type property
    // for it sometimes has "fieldset" and sometimes does not, possibly depending
    // on other modules installed in the system.  To make it consistent, we are not
    // going to see it as a fieldset, as that is how it appears for most systems.
    // Set the prefix and suffix for this field such that we
    // wrap a div around it which the jquery can lock onto later to
    // make it draggable.
    $element["#prefix"] .= "<div id='{$css_id}' class='{$css_class}'>{$edit_link}";
    $element["#suffix"] .= "</div>";

    // If the field is a button, and we have chosen to disable buttons, then
    // disable this one.
    if ($element["#type"] == "submit" || $element["#type"] == "button") {
      if ($disable_buttons) {
        $element["#attributes"]["disabled"] = "disabled";
        $element["#attributes"]["class"] .= " disabled-button ";
      }
    }

    // This is essentially the same disable buttons code, but for CCK content types,
    // since all those buttons are in a [buttons] element.
    if ($field_name == "buttons" && !isset($element["#type"]) && $disable_buttons) {
      foreach ($element as &$e) {
        if (is_array($e)) {
          $e["#attributes"]["disabled"] = "disabled";
          $e["#attributes"]["class"] .= " disabled-button ";
        }
      }
    }

    // Indicate that we have successfully added all the necessary wrappers
    // to this element.  This is so we don't do it twice.
    $element["#arrange_fields_added_wrappers"] = TRUE;
  }

  // We set this global so later on we can easily tell if we've already performed this opperation.
  $GLOBALS["arrange_fields_added_wrappers_" . $form["form_id"]["#value"]] = TRUE;
}

/**
 * This function, which is meant to be displayed in the popup, will just take
 * CCK's field_edit_form and display it for us.  This is to make it more
 * convienent for the user, so they do not have to go to another page.
 *
 * @return string
 */
function arrange_fields_popup_edit_field() {
  $type_name = $_REQUEST["type_name"];
  $field_name = $_REQUEST["field"];
  if ($type_name != "webform") {

    // Make sure this CCK include has been loaded.
    module_load_include('inc', 'content', 'includes/content.admin');

    // We set this global here, so in our hook_form_alter, we know we need
    // to change the #redirect of the form to go back to our other popup.
    $GLOBALS["arrange_fields_editing_field"] = TRUE;
    $rtn = drupal_get_form('content_field_edit_form', $type_name, $field_name);
  }
  else {

    // This is a webform!
    // First, get the webform component from the $field_name, which looks like:
    // nid_cid.
    $temp = explode("_", $field_name);
    $nid = $temp[0];
    $cid = $temp[1];
    $component = webform_menu_component_load($cid, $nid, "");

    // Also load the webform as a node.
    $node = node_load($nid);

    // We set this global here, so in our hook_form_alter, we know we need
    // to change the #redirect of the form to go back to our other popup.
    $GLOBALS["arrange_fields_editing_field"] = TRUE;

    // Now, attempt to display the form.
    $rtn = drupal_get_form('webform_component_edit_form', $node, $component, FALSE);
  }
  return $rtn;
}

/**
 * After the popup_edit_field has been submitted, the user comes to this page,
 * which simply instructs them to click a button which will make the opener page
 * save itself.  This is necessary, or the changes the user made in the popup
 * will not be reflected on the page!
 *
 * @return string
 */
function arrange_fields_popup_close_window() {
  jquery_ui_add(array(
    'ui.draggable',
    'ui.dialog',
    'ui.resizable',
  ));
  drupal_add_js(drupal_get_path("module", "arrange_fields") . "/js/arrange_fields.js");
  $rtn .= "<div style='text-align: center;'>\n            <input type='button' value='" . t("Close and Save/Reload Main Window") . "'\n            onClick='arrangeFieldsClosePopup()'>\n           </div>";
  return $rtn;
}

/**
 * Implementation of hook_form_alter().
 *
 */
function arrange_fields_form_alter(&$form, &$form_state, $form_id) {
  $form_type = $GLOBALS["arrange_fields_editing_type"];
  if ($GLOBALS["arrange_fields_editing"] == $form_id) {

    // meaning, we are arranging the fields on the current form_id...
    // Let's unset the various elements which will just get in the
    // way of the fields.
    unset($form["#submit"]);
    $form["#attributes"]["class"] = "arrange-fields-container";
    if (!$GLOBALS["arrange_fields_added_wrappers_" . $form["form_id"]["#value"]]) {
      arrange_fields_add_draggable_wrappers($form, $form_type, TRUE);
    }
  }

  // Do we have position data for the content type (form) currently being displayed?
  // If we do, then let's add styles to the page which contain the position data.
  if ($position_data = variable_get("arrange_fields_position_data_{$form_id}", FALSE)) {
    if (strpos($form_id, "webform_client_form") === 0) {
      $form_type = "webform";
    }

    // If this is a webform, and we are looking at submission data, then we do not want to try to
    // arrange any fields.  Doing so (at least at the moment) does not look right.
    if ($form_type == "webform" && is_array($form["submission_info"]) && $form["submission_info"]["#type"] == "fieldset") {
      return;
    }
    drupal_add_css(drupal_get_path("module", "arrange_fields") . "/css/arrange_fields.css");
    drupal_add_js(drupal_get_path("module", "arrange_fields") . "/js/arrange_fields_node_edit.js");
    $fid = $form["#id"];

    // The form's CSS id.  We will use it later to target just this one form.
    $form["#attributes"]["class"] .= " arrange-fields-container ";
    $form["#attributes"]["class"] .= " arrange-fields-container-{$fid} ";
    if (!$GLOBALS["arrange_fields_added_wrappers_" . $form["form_id"]["#value"]]) {
      arrange_fields_add_draggable_wrappers($form, $form_type);
    }

    // Let's go through and assign the positions.
    $jsConfigArray = array();
    $jsMarkupArray = array();
    $css_markup = $markup_elements = "";
    $lines = explode(";", $position_data);
    foreach ($lines as $line) {
      if (trim($line) == "") {
        continue;
      }

      // skip blanks
      $temp = explode(",", $line);
      $wrapper_id = trim($temp[0]);
      $pos_top = trim($temp[1]);
      $pos_left = trim($temp[2]);
      $element_type = trim($temp[3]);
      $width = trim($temp[4]);
      $height = trim($temp[5]);
      $wrapper_width = trim($temp[6]);
      $wrapper_height = trim($temp[7]);
      $label_display = trim($temp[8]);
      $label_vertical_align = trim($temp[9]);
      $text_label_display = $label_display;
      if ($label_display == "inline-block") {
        $text_label_display = "inline";
      }
      if ($wrapper_id == "~~maxBottom~~") {

        // This is actually the height of the container.  Let's set that,
        // then continue.
        $css_markup .= " .arrange-fields-container-{$fid} {\n                      height: {$pos_top};\n                        }";
        continue;
      }

      // Was this actually a markup element which the user added?
      // If so, add
      if ($temp[6] == "~~markup_element~~") {
        $markup_width = $temp[7];
        $markup_height = $temp[8];
        $safe_markup_body = trim($temp[9]);
        $markup_body = arrange_fields_unconvert_unsafe_chars(trim($temp[9]));
        $safe_wrapper_style = trim($temp[10]);
        $wrapper_style = arrange_fields_unconvert_unsafe_chars(trim($temp[10]));
        $z_index = trim($temp[11]);
        $markup_elements .= "\n        <div class='draggable-form-item arrange-fields-draggable-markup' \n              id='{$wrapper_id}'>\n          <div class='arrange-fields-control-handle arrange-fields-control-handle-markup'><span class='arrange-fields-handle-region'> &nbsp; &nbsp; </span>\n            <a href='javascript: arrangeFieldsDialogEditMarkup(\"{$wrapper_id}\");' class='arrange-fields-config-markup-link' title='Configure this markup'>&nbsp;</a>\n          </div>\n          <div class='arrange-fields-markup-body form-item' \n              id='{$wrapper_id}_body'>{$markup_body}</div>\n        </div>";
        $css_markup .= "\n          #{$fid} #{$wrapper_id} {\n            top: {$pos_top};\n            left: {$pos_left};\n            width: {$markup_width};\n            height: {$markup_height};\n            z-index: {$z_index};\n            {$wrapper_style}\n          }\n        ";

        // Add this bit of markup to our jsMarkupArray, so it can be
        // added as a setting later.
        $jsMarkupArray[$wrapper_id]["markupBody"] = $safe_markup_body;
        $jsMarkupArray[$wrapper_id]["wrapperStyle"] = $safe_wrapper_style;
        $jsMarkupArray[$wrapper_id]["zIndex"] = $z_index;
        continue;
      }
      $jsConfigArray[$wrapper_id]["wrapperWidth"] = $wrapper_width;
      $jsConfigArray[$wrapper_id]["wrapperHeight"] = $wrapper_height;
      $jsConfigArray[$wrapper_id]["labelDisplay"] = $label_display;
      $jsConfigArray[$wrapper_id]["labelVerticalAlign"] = $label_vertical_align;

      // Add to our CSS markup value...
      $css_markup .= "\n            \n        #{$fid} #{$wrapper_id}{        \n          top: {$pos_top};\n          left: {$pos_left}; \n        }\n      ";

      // We do not want to try to resize any input fields if this
      // is the buttons wrapper.  Otherwise it will mess up our buttons.
      // This usually happens when you have CAPTCHA installed.
      if ($width != "0px" && $element_type != "") {
        $css_markup .= "        \n          #{$fid} #{$wrapper_id} {$element_type}.form-text, \n          #{$fid} #{$wrapper_id} {$element_type}.form-textarea {\n            width: {$width};\n            ";

        // If this is an input field (not a textarea) we do not need to specify the height.
        // This is hopefully a fix to a bug where, for some users, their textfields get shrunk down
        // to only 1px.
        if ($element_type != "input") {
          $css_markup .= "\n              height: {$height};        \n            ";
        }
        $css_markup .= "    }        ";
      }

      // Handle any configurations which were set in the configure dialog.
      if ($wrapper_width != "") {
        $css_markup .= " #{$fid} #{$wrapper_id} { width: {$wrapper_width}; } ";
      }
      if ($wrapper_height != "") {
        $css_markup .= "\n          #{$fid} #{$wrapper_id} { height: {$wrapper_height}; } \n          #{$fid} #{$wrapper_id} fieldset { height: 100%; } \n        ";
      }
      if ($label_display != "") {
        $css_markup .= "\n          #{$fid} #{$wrapper_id} .form-item label { \n            vertical-align: {$label_vertical_align}; \n          }\n          #{$fid} #{$wrapper_id} .form-item > input, \n          #{$fid} #{$wrapper_id} .form-item > label,\n          #{$fid} #{$wrapper_id} .form-item > div,\n          #{$fid} #{$wrapper_id} .form-item > div.form-radios > div,\n          #{$fid} #{$wrapper_id} .form-item > div.form-checkboxes > div\n          {\n            display: {$label_display};\n          }";
        if ($element_type == "input") {

          // Because of IE, we must do something special if the input
          // is a textfield, which is what this is testing for.
          // Basically, the label cannot have a display of "inline-block",
          // it must simply be "inline."  However, the textfield itself still
          // needs to be "inline-block."  Thanks IE!
          $css_markup .= "\n            #{$fid} #{$wrapper_id} .form-item > label\n            {\n              display: {$text_label_display};\n            }\n          ";
        }
        $css_markup .= "  \n          #{$fid} #{$wrapper_id} .form-item div.ui-resizable-handle,\n          #{$fid} #{$wrapper_id} .form-item div.description\n          {\n            display: block;\n          }                          \n                ";
      }
    }
    drupal_add_js(array(
      "arrangeFieldsDialogConfigObj" => $jsConfigArray,
    ), "setting");
    drupal_add_js(array(
      "arrangeFieldsDialogMarkupObj" => $jsMarkupArray,
    ), "setting");

    // Add in the user-specified grid width.
    drupal_add_js(array(
      "arrangeFieldsGridWidth" => variable_get("arrange_fields_grid_width", 10),
    ), "setting");
    if ($GLOBALS["arrange_fields_editing"] != $form_id) {

      // Meaning, we are not currently arranging this form.  The user
      // must actually be putting data into it on the node/edit page.
      // Let's remove the extra
      // styles around the various divs so that it looks more natural.
      $css_markup .= "\n      \n      #{$fid} {\n        border: 0;\n        background: none;\n      }\n      \n      #{$fid} .draggable-form-item {\n        border: 0;\n        background-color: transparent;\n      }\n      \n      ";
    }

    // Now, add in our css markup and markup elements...
    $form["arrange_fields_css_markup_and_elements"] = array(
      "#value" => "<style type='text/css'>{$css_markup}</style>{$markup_elements}",
      "#after_build" => array(
        "arrange_fields_add_form_css_js",
      ),
    );

    // Let's add our modified form back to the cache (needed to get the preview
    // button to work correctly).
    form_set_cache($form["#build_id"], $form, $form_state);
  }

  // The user is trying to configure a field, and we want to show
  // it to them in the custom popup.  When they submit that form, we don't
  // want it to go to CCK's (or Webform's) normal destination,
  // so we set it to our other popup function.
  if (($form_id == "content_field_edit_form" || $form_id == "webform_component_edit_form") && $GLOBALS["arrange_fields_editing_field"] == TRUE) {
    $form["#redirect"] = "arrange-fields/popup-close-window";
  }
}

/**
 * This function simply adds the CSS and JS files we need when on the node/edit page.
 * I have to do it this way (and set this function in an #after_build on the form)
 * in order to make sure this still gets called, even if the form fails validation.
 * 
 * This is not used when actually arranging the fields of a form, but when a user
 * is entering data into the form.  If they forgot a required field (or whatever)
 * and failed validation, we need to make sure these files get reloaded for them,
 * otherwise the field positions will revert back to the default!
 *
 */
function arrange_fields_add_form_css_js($element) {
  drupal_add_css(drupal_get_path("module", "arrange_fields") . "/css/arrange_fields.css");
  drupal_add_js(drupal_get_path("module", "arrange_fields") . "/js/arrange_fields_node_edit.js");
  return $element;
}

/**
 * This is the "main menu" for arrange_fields.  Simply displays a list of
 * content types or webforms the user may arrange the fields of.
 *
 * @return string
 */
function arrange_fields_display_main() {
  $rtn = "";
  drupal_add_css(drupal_get_path("module", "arrange_fields") . "/css/arrange_fields.css");
  $rtn .= t("Select a content type to arrange fields") . "...\n          <ul>";
  foreach (node_get_types() as $type) {

    // To eleminate confusion, let's not display the "webform" content type, if that module
    // is installed.
    if ($GLOBALS["arrange_fields_webform_installed"] && $type->type == "webform") {
      continue;
    }
    $rtn .= "<li>" . l($type->name, "arrange-fields/{$type->type}") . "</li>";
  }
  $rtn .= "</ul>";

  // If webform has been installed, attempt to use any available webforms as well.
  if ($GLOBALS["arrange_fields_webform_installed"]) {
    $rtn .= t("Select a webform to arrange fields") . "...\n          <ul>";
    $is_empty = TRUE;
    $result = db_query("SELECT * FROM {node} WHERE type = 'webform'");
    $nodes = array();
    while ($node = db_fetch_object($result)) {
      $nodes[] = $node;
      $is_empty = FALSE;
    }
    if ($is_empty) {
      $rtn .= "<li>" . t("No webforms have been created yet.") . "</li>";
    }
    else {
      foreach ($nodes as $node_res) {
        $rtn .= "<li>" . l($node_res->title, "arrange-fields/webform/{$node_res->nid}") . "</li>";
      }
    }
    $rtn .= "</ul>";
  }

  // Does the user have any additional form_id's specified they'd like to try
  // to arrange?
  $other_form_ids = trim(variable_get("arrange_fields_other_form_ids", ""));
  if ($other_form_ids != "") {
    $temp = explode("\n", $other_form_ids);
    $rtn .= t("Additional form_id's you specified on the Settings tab...");
    $rtn .= "<ul>";
    foreach ($temp as $form_id) {
      $rtn .= "<li>" . l($form_id, "arrange-fields/other/{$form_id}") . "</li>";
    }
    $rtn .= "</ul>";
    $rtn .= "<div class='arrange-fields-other-form-caveat'>";
    $rtn .= t("Some caveats: Arrange Fields may not work correctly on forms\n              with complex structures.  Also, remember that some forms may\n              appear differently depending on if you are admin, an authorized user,\n              or an anonymous user (like user_register).\n              If you need to arrange a form which only appears for anonymous users,\n              temporarily grant anonymous users the \"administer\n              arrange fields\" permission so you can arrange the form how it\n              needs to look.  Just be sure to take this away when you are done as\n              it introduces a major security risk.");
    $rtn .= "</div>";
  }
  return $rtn;
}

/**
 * hook_init()
 * 
 * The init function is executed at the beginning of every page
 * load.  Here, I'm just trying to see if the webform module
 * has been installed, and if so, I set a global variable I can
 * access later.
 *
 */
function arrange_fields_init() {

  // is webform installed?
  if (function_exists("webform_menu")) {
    $GLOBALS["arrange_fields_webform_installed"] = TRUE;
  }
}

/**
 * This function displays the arrangement form, where the user will be able
 * to actually drag and drop to arrange fields.  In it, we actually load the
 * form which they are arranging, but we will be saving our data to
 * our own form.
 *
 * @param string $form_type
 * @return string
 */
function arrange_fields_display_form($form_type) {
  drupal_set_title("Arrange fields - {$form_type}");
  $rtn = "";
  $form_id = $form_type . "_node_form";
  $position_data = variable_get("arrange_fields_position_data_{$form_id}", FALSE);
  if ($position_data) {

    // Meaning, we have position data already for this form, so it is NOT
    // a brand-new form.  So, we should not pass "true" to the javascript
    // function arrangeFieldsRepositionToGrid.  Let's add a drupal
    // setting so we know that is the case.
    drupal_add_js(array(
      "arrangeFieldsNotNewForm" => TRUE,
    ), "setting");
  }
  arrange_fields_add_arrange_css_js();

  // We want to get the form which will let us save the position
  // information.
  $rtn .= drupal_get_form("arrange_fields_position_form", $form_id, $form_type);
  $rtn .= "<div>" . t("Use this form to drag-and-drop fields into the order which\n          you want them to appear on the node/edit page.") . "</div>\n          <div>" . t("You may resize text fields by dragging the right side\n              of the field.") . "</div>";
  $rtn .= "<input type='button' value='add markup' onClick='arrangeFieldsDialogEditMarkup(\"new\");'>";

  // The form we will be rearranging...
  $GLOBALS["arrange_fields_editing"] = $form_id;
  $GLOBALS["arrange_fields_editing_type"] = $form_type;
  $node_form = new stdClass();
  $node_form->type = $form_type;
  $rtn .= drupal_get_form($form_id, $node_form, $my_form_state);
  $rtn .= "<div>" . t("If you need more room, move a field close to the bottom.  The\n          container will resize, adding more room.") . "</div>";
  $rtn .= arrange_fields_render_dialogs();
  return $rtn;
}

/**
 * Mirror of the javascript function in arrange_fields_dialog.js,
 * this will convert our custom codes for unsafe characters back to
 * what they started off as.
 *
 * @param string $str
 * @return string
 */
function arrange_fields_unconvert_unsafe_chars($str) {
  $str = str_replace("_~!co%~_", ",", $str);
  $str = str_replace("_~!sc%~_", ";", $str);
  $str = str_replace("_~!sq%~_", "'", $str);
  $str = str_replace("_~!dq%~_", '"', $str);
  $str = str_replace("_~!nl%~_", "\n", $str);
  return $str;
}

/**
 * Renders the divs which will be used as the jquery dialogs, displayed using
 * jQuery's .dialog() function.  
 * 
 * One will let the administrator manage
 * details of a field, like width and height of the wrapper.
 * 
 * The other will let users add or edit bits of arbitrary markup on the form.
 *
 */
function arrange_fields_render_dialogs() {
  $rtn = "";
  $rtn .= "<div id='arrange-fields-config-dialog' title='Configure'>\n            <table>\n              <tr>\n                <td width='45%'>Wrapper width:</td><td><input type='input' name='af-dialog-width' size='6'></td>\n              </tr>\n              <tr> \n                <td>Wrapper height:</td><td><input type='input' name='af-dialog-height' size='6'></td>\n              </tr>\n            </table>\n            <div style='font-size: 0.8em; line-height: 1.0em;'>(remember to specify px, %, etc. Leave blank for default.)</div>\n            <br>\n            Label: \n            <label><input type='radio' name='af-dialog-label-display' value='' checked> default (block)</label>\n            <label><input type='radio' name='af-dialog-label-display' value='inline-block' > inline</label>\n           </div>";
  $rtn .= "<div id='arrange-fields-markup-dialog' title='Markup'>\n              <div><b>Body:</b></div>\n              <textarea name='af-markup-body' class='af-markup-body'></textarea>\n              <div style='font-size: 0.8em; line-height: 1.0em;'>\n                You may enter any valid HTML here.</div>\n              \n              <div style='padding-top: 10px;'><b>Wrapper style</b>='\n                 <input name='af-wrapper-style' class='af-wrapper-style'>\n                 '\n                 <div style='font-size: 0.8em; line-height: 1.0em;'>\n                  Enter style definitions. Ex: background-color: black;\n                 </div>\n              </div>\n              \n              <div style='padding-top: 10px;'><b>Z-index</b>:\n                <label><input type='radio' name='af-markup-z-index' value='201' checked> foreground (default)</label>\n                <label><input type='radio' name='af-markup-z-index' value='75' > background</label>\n                                 \n              </div>\n\n              \n              <div class='af-delete-button'>\n                <button type='button' onClick='arrangeFieldsDialogMarkupDelete();'>Delete</button>\n              </div>\n           </div>";
  return $rtn;
}

/**
 * Similar function as arrange_fields_display_form,
 * but this is specifically for webforms (with the webform module). 
 *
 */
function arrange_fields_display_webform($nid) {
  if (is_object($nid)) {

    // We were passed a node by the menu, so sort out
    // what is supposed to be the node, and what the nid.
    $webform_node = $nid;
    $nid = $webform_node->nid;
  }
  else {
    $webform_node = node_load($nid);
  }
  $form_id = "webform_client_form_{$nid}";
  $form_type = "webform";
  drupal_set_title("Arrange fields - webform - {$webform_node->title}");
  $rtn = "";
  $position_data = variable_get("arrange_fields_position_data_{$form_id}", FALSE);
  if ($position_data) {

    // Meaning, we have position data already for this form, so it is NOT
    // a brand-new form.  So, we should not pass "true" to the javascript
    // function arrangeFieldsRepositionToGrid.  Let's add a drupal
    // setting so we know that is the case.
    drupal_add_js(array(
      "arrangeFieldsNotNewForm" => TRUE,
    ), "setting");
  }
  arrange_fields_add_arrange_css_js();

  // We want to get the form which will let us save the position
  // information.
  $rtn .= drupal_get_form("arrange_fields_position_form", $form_id, $form_type);
  $rtn .= "<div>" . t("Use this form to drag-and-drop fields into the order which\n          you want them to appear on the webform entry page.") . "</div>\n          <div>" . t("You may resize text fields by dragging the right side\n              of the field.") . "</div>";
  $rtn .= "<input type='button' value='add markup' onClick='arrangeFieldsDialogEditMarkup(\"new\");'>";

  // The form we will be rearranging...
  $GLOBALS["arrange_fields_editing"] = $form_id;
  $GLOBALS["arrange_fields_editing_type"] = $form_type;
  $rtn .= drupal_get_form($form_id, $webform_node, NULL, TRUE);
  $rtn .= "<div>" . t("If you need more room, move a field close to the bottom, then save positions.  The\n          container will resize, adding more room.") . "</div>";
  $rtn .= arrange_fields_render_dialogs();
  return $rtn;
}

/**
 * Meant to be called by the display_form functions, this will make sure
 * the necessary module inc's are loaded, and the required js and css files
 * are added for the actual arranging of fields.
 *
 */
function arrange_fields_add_arrange_css_js() {
  module_load_include('inc', 'node', 'node.pages');
  jquery_ui_add(array(
    'ui.draggable',
    'ui.dialog',
    'ui.resizable',
  ));
  drupal_add_js(drupal_get_path("module", "arrange_fields") . "/js/arrange_fields_node_edit.js");

  // must be included first.
  drupal_add_js(drupal_get_path("module", "arrange_fields") . "/js/arrange_fields.js");
  drupal_add_js(drupal_get_path("module", "arrange_fields") . "/js/arrange_fields_dialog.js");
  drupal_add_css(drupal_get_path("module", "arrange_fields") . "/css/arrange_fields.css");
}

/**
 * Similar function as arrange_fields_display_form,
 * but this is specifically for other, more generic forms
 * on the system.  For example, user_register, or custom
 * forms which a developer has written.
 *
 */
function arrange_fields_display_otherform($form_id) {
  $form_type = "otherform";
  drupal_set_title("Arrange fields - Other Forms - {$form_id}");
  $rtn = "";
  $position_data = variable_get("arrange_fields_position_data_{$form_id}", FALSE);
  if ($position_data) {

    // Meaning, we have position data already for this form, so it is NOT
    // a brand-new form.  So, we should not pass "true" to the javascript
    // function arrangeFieldsRepositionToGrid.  Let's add a drupal
    // setting so we know that is the case.
    drupal_add_js(array(
      "arrangeFieldsNotNewForm" => TRUE,
    ), "setting");
  }
  arrange_fields_add_arrange_css_js();

  // We want to get the form which will let us save the position
  // information.
  $rtn .= drupal_get_form("arrange_fields_position_form", $form_id, $form_type);
  $rtn .= "<div>" . t("Use this form to drag-and-drop fields into the order which\n          you want them to appear on the form entry page.") . "</div>\n          <div>" . t("You may resize text fields by dragging the right side\n              of the field.") . "</div>";
  $rtn .= "<input type='button' value='add markup' onClick='arrangeFieldsDialogEditMarkup(\"new\");'>";

  // The form we will be rearranging...
  $GLOBALS["arrange_fields_editing"] = $form_id;
  $GLOBALS["arrange_fields_editing_type"] = $form_type;
  $rtn .= drupal_get_form($form_id);
  $rtn .= "<div>" . t("If you need more room, move a field close to the bottom, then save positions.  The\n          container will resize, adding more room.") . "</div>";
  $rtn .= arrange_fields_render_dialogs();
  return $rtn;
}

/**
 * This is the form which we will use to store the position data for the fields.
 *
 * @param array $form_state
 * @param string $form_id
 * @param string $form_type
 * @return array
 */
function arrange_fields_position_form($form_state, $form_id, $form_type) {
  $form = array();
  $form["arrange_fields_form_id"] = array(
    "#type" => "hidden",
    "#value" => $form_id,
  );
  $form["arrange_fields_form_type"] = array(
    "#type" => "hidden",
    "#value" => $form_type,
  );

  // This field ends up being hidden in CSS.  It
  // is where we will store a semi-serialized string of all our
  // position data for all our fields.
  $form["arrange_fields_position_data"] = array(
    "#type" => "textfield",
    "#maxlength" => 999999999999,
  );
  $form["save"] = array(
    "#type" => "submit",
    "#value" => "Save position data",
    "#attributes" => array(
      "onClick" => "return arrangeFieldsSavePositions()",
    ),
  );
  $form["reset"] = array(
    "#type" => "button",
    "#value" => "Reset position data",
    "#attributes" => array(
      "onClick" => "return arrangeFieldsConfirmReset()",
    ),
  );
  return $form;
}

/**
 * The primary purpose of this validator is to find out of the user is trying
 * to reset the form (and restore the form to its orignal state).
 *
 */
function arrange_fields_position_form_validate($form, &$form_state) {
  $form_id = $form_state["values"]["arrange_fields_form_id"];
  if (stristr($form_state["values"]["op"], "reset")) {
    variable_set("arrange_fields_position_data_{$form_id}", "");
    drupal_set_message(t("Position data for this form has been reset.  It will \n                      be displayed to the user without any modifications,\n                      in its original state."));

    // Need to refresh the page in order for the positions to be
    // fully reset.  This next bit finds out what the current URL is,
    // then sends the user there again (refreshing the page)
    $path = isset($_GET['q']) ? $_GET['q'] : '<front>';
    $link = url($path, array(
      'absolute' => TRUE,
    ));
    drupal_goto($link);
  }

  // This bit is required in order to let javascript submit the form!
  // (Which happens after you change CCK settings in the popup, then
  // click the button which says it is going to save and reload your form).
  $form_state["submitted"] = TRUE;
}

/**
 * We will save the position data into a variable using variable_set.
 *
 */
function arrange_fields_position_form_submit($form, $form_state) {
  $form_id = $form_state["values"]["arrange_fields_form_id"];
  $form_type = $form_state["values"]["arrange_fields_form_type"];
  $position_data = $form_state["values"]["arrange_fields_position_data"];
  variable_set("arrange_fields_position_data_{$form_id}", $position_data);
  $demo_link = l(t("See demo of most recent save (loads in new window)"), "node/add/" . str_replace("_", "-", $form_type), array(
    "attributes" => array(
      "target" => "_blank",
    ),
  ));
  if ($form_type == "webform") {
    $nid = str_replace("webform_client_form_", "", $form_id);
    $demo_link = l(t("See demo of most recent save (loads in new window)"), "node/{$nid}", array(
      "attributes" => array(
        "target" => "_blank",
      ),
    ));
  }
  if ($form_type == "otherform") {
    $demo_link = "";
  }
  drupal_set_message(t("Position data saved.") . " " . $demo_link);
}

/**
 * Theme registry alter hook
 * 
 * Register this module's path as a search location for tpl files.
 * We want it to look for the special popup page.tpl files in the module's
 * directory.  Doing this allows us to include template files with the
 * module which can still be overridden by the end user in their site's theme.
 *
 */
function arrange_fields_theme_registry_alter(&$theme_registry) {
  if (is_array($theme_registry['page'])) {

    // store a reference to the first theme path entry (normally this should be 'modules/system')
    $first_path = array_shift($theme_registry['page']['theme paths']);

    // get the path to our module's page template files
    $template_path = drupal_get_path('module', 'arrange_fields');

    // now change the array to have the first entry first, then our template path, and then any other theme paths
    array_unshift($theme_registry['page']['theme paths'], $first_path, $template_path);
  }
}

Functions

Namesort descending Description
arrange_fields_add_arrange_css_js Meant to be called by the display_form functions, this will make sure the necessary module inc's are loaded, and the required js and css files are added for the actual arranging of fields.
arrange_fields_add_draggable_wrappers In this function, we are going to add div's around elements we wish to make draggable on the page. The jQuery can then grab onto those divs to make them draggable.
arrange_fields_add_form_css_js This function simply adds the CSS and JS files we need when on the node/edit page. I have to do it this way (and set this function in an #after_build on the form) in order to make sure this still gets called, even if the form fails validation.
arrange_fields_display_form This function displays the arrangement form, where the user will be able to actually drag and drop to arrange fields. In it, we actually load the form which they are arranging, but we will be saving our data to our own form.
arrange_fields_display_main This is the "main menu" for arrange_fields. Simply displays a list of content types or webforms the user may arrange the fields of.
arrange_fields_display_otherform Similar function as arrange_fields_display_form, but this is specifically for other, more generic forms on the system. For example, user_register, or custom forms which a developer has written.
arrange_fields_display_webform Similar function as arrange_fields_display_form, but this is specifically for webforms (with the webform module).
arrange_fields_export_form This function simply provides textareas for the user to copy/paste from which will allow them to export the arrangement of their forms. It does not really need to use the form API, since there is no submission, but I am doing this to make it easier to…
arrange_fields_form_alter Implementation of hook_form_alter().
arrange_fields_get_form_fields This function uses recursion to go through an array (presumably $form). It will return back an array of references to fields which we might want to place wrappers around.
arrange_fields_import_form We will use this form to let users paste in definitions for form arrangements which they got from the "export" screen.
arrange_fields_import_form_submit This function will handle the actual importing of arrange fields definitions.
arrange_fields_init hook_init()
arrange_fields_menu Implementation of hook_menu().
arrange_fields_perm
arrange_fields_popup_close_window After the popup_edit_field has been submitted, the user comes to this page, which simply instructs them to click a button which will make the opener page save itself. This is necessary, or the changes the user made in the popup will not be reflected…
arrange_fields_popup_edit_field This function, which is meant to be displayed in the popup, will just take CCK's field_edit_form and display it for us. This is to make it more convienent for the user, so they do not have to go to another page.
arrange_fields_position_form This is the form which we will use to store the position data for the fields.
arrange_fields_position_form_submit We will save the position data into a variable using variable_set.
arrange_fields_position_form_validate The primary purpose of this validator is to find out of the user is trying to reset the form (and restore the form to its orignal state).
arrange_fields_render_dialogs Renders the divs which will be used as the jquery dialogs, displayed using jQuery's .dialog() function.
arrange_fields_settings_form This function returns a form which we will use to configure the arrange_fields module. It is called from menu item: admin/settings/arrange-fields/settings
arrange_fields_theme_registry_alter Theme registry alter hook
arrange_fields_unconvert_unsafe_chars Mirror of the javascript function in arrange_fields_dialog.js, this will convert our custom codes for unsafe characters back to what they started off as.