Ejemplo n.º 1
0
/**
 * Called by administrators; updates the content stored on the "Fields" tab in the Edit Form pages.
 *
 * @param integer $form_id the unique form ID
 * @param array $infohash a hash containing the contents of the Edit Form Display tab
 * @return array returns array with indexes:<br/>
 *               [0]: true/false (success / failure)<br/>
 *               [1]: message string<br/>
 */
function ft_update_form_fields_tab($form_id, $infohash)
{
    global $g_table_prefix, $g_root_url, $g_root_dir, $g_debug, $LANG, $g_field_sizes;
    $success = true;
    $message = $LANG["notify_field_changes_saved"];
    $infohash = ft_sanitize($infohash);
    extract(ft_process_hook_calls("start", compact("infohash", "form_id"), array("infohash")), EXTR_OVERWRITE);
    // stores the cleaned-up version of the POST content
    $field_info = array();
    $sortable_id = $infohash["sortable_id"];
    $field_ids = explode(",", $infohash["{$sortable_id}_sortable__rows"]);
    $order = $infohash["sortable_row_offset"];
    $new_sort_groups = explode(",", $infohash["{$sortable_id}_sortable__new_groups"]);
    foreach ($field_ids as $field_id) {
        $is_new_field = preg_match("/^NEW/", $field_id) ? true : false;
        $display_name = isset($infohash["field_{$field_id}_display_name"]) ? $infohash["field_{$field_id}_display_name"] : "";
        $form_field_name = isset($infohash["field_{$field_id}_name"]) ? $infohash["field_{$field_id}_name"] : "";
        $include_on_redirect = isset($infohash["field_{$field_id}_include_on_redirect"]) ? "yes" : "no";
        $field_size = isset($infohash["field_{$field_id}_size"]) ? $infohash["field_{$field_id}_size"] : "";
        $col_name = isset($infohash["col_{$field_id}_name"]) ? $infohash["col_{$field_id}_name"] : "";
        $old_field_size = isset($infohash["old_field_{$field_id}_size"]) ? $infohash["old_field_{$field_id}_size"] : "";
        $old_col_name = isset($infohash["old_col_{$field_id}_name"]) ? $infohash["old_col_{$field_id}_name"] : "";
        $is_system_field = in_array($field_id, $infohash["system_fields"]) ? "yes" : "no";
        // this is only sent for non-system fields
        $field_type_id = isset($infohash["field_{$field_id}_type_id"]) ? $infohash["field_{$field_id}_type_id"] : "";
        // won't be defined for new fields
        $old_field_type_id = isset($infohash["old_field_{$field_id}_type_id"]) ? $infohash["old_field_{$field_id}_type_id"] : "";
        $field_info[] = array("is_new_field" => $is_new_field, "field_id" => $field_id, "display_name" => $display_name, "form_field_name" => $form_field_name, "field_type_id" => $field_type_id, "old_field_type_id" => $old_field_type_id, "include_on_redirect" => $include_on_redirect, "is_system_field" => $is_system_field, "list_order" => $order, "is_new_sort_group" => in_array($field_id, $new_sort_groups) ? "yes" : "no", "col_name" => $col_name, "old_col_name" => $old_col_name, "col_name_changed" => $col_name != $old_col_name ? "yes" : "no", "field_size" => $field_size, "old_field_size" => $old_field_size, "field_size_changed" => $field_size != $old_field_size ? "yes" : "no");
        $order++;
    }
    reset($infohash);
    // delete any extended field settings for those fields whose field type just changed. Two comments:
    //   1. this is compatible with editing the fields in the dialog window. When that happens & the user updates
    //      it, the code updates the old_field_type_id info in the page so this is never called.
    //   2. with the addition of Shared Characteristics, this only deletes fields that aren't mapped between the
    //      two fields types (old and new)
    $changed_fields = array();
    foreach ($field_info as $curr_field_info) {
        if ($curr_field_info["is_new_field"] || $curr_field_info["is_system_field"] == "yes" || $curr_field_info["field_type_id"] == $curr_field_info["old_field_type_id"]) {
            continue;
        }
        $changed_fields[] = $curr_field_info;
    }
    if (!empty($changed_fields)) {
        $field_type_settings_shared_characteristics = ft_get_settings("field_type_settings_shared_characteristics");
        $field_type_map = ft_get_field_type_id_to_identifier();
        $shared_settings = array();
        foreach ($changed_fields as $changed_field_info) {
            $field_id = $changed_field_info["field_id"];
            $shared_settings[] = ft_get_shared_field_setting_info($field_type_map, $field_type_settings_shared_characteristics, $field_id, $changed_field_info["field_type_id"], $changed_field_info["old_field_type_id"]);
            ft_delete_extended_field_settings($field_id);
            ft_delete_field_validation($field_id);
        }
        foreach ($shared_settings as $setting) {
            foreach ($setting as $setting_info) {
                $field_id = $setting_info["field_id"];
                $setting_id = $setting_info["new_setting_id"];
                $setting_value = ft_sanitize($setting_info["setting_value"]);
                mysql_query("\n          INSERT INTO {$g_table_prefix}field_settings (field_id, setting_id, setting_value)\n          VALUES ({$field_id}, {$setting_id}, '{$setting_value}')\n        ");
            }
        }
    }
    // the database column name and size field both affect the form's actual database table structure. If either
    // of those changed, we need to update the database
    $db_col_changes = array();
    $db_col_change_hash = array();
    // added later. Could use refactoring...
    $table_name = "{$g_table_prefix}form_{$form_id}";
    foreach ($field_info as $curr_field_info) {
        if ($curr_field_info["col_name_changed"] == "no" && $curr_field_info["field_size_changed"] == "no") {
            continue;
        }
        if ($curr_field_info["is_new_field"]) {
            continue;
        }
        $field_id = $curr_field_info["field_id"];
        $old_col_name = $curr_field_info["old_col_name"];
        $new_col_name = $curr_field_info["col_name"];
        $new_field_size = $curr_field_info["field_size"];
        $new_field_size_sql = $g_field_sizes[$new_field_size]["sql"];
        list($is_success, $err_message) = _ft_alter_table_column($table_name, $old_col_name, $new_col_name, $new_field_size_sql);
        if ($is_success) {
            $db_col_changes[$field_id] = array("col_name" => $new_col_name, "field_size" => $new_field_size);
        } else {
            // if there have already been successful database column name changes already made,
            // update the database. This helps prevent things getting out of whack
            if (!empty($db_col_changes)) {
                while (list($field_id, $changes) = each($db_col_changes)) {
                    $col_name = $changes["col_name"];
                    $field_size = $changes["field_size"];
                    @mysql_query("\n            UPDATE {$g_table_prefix}form_fields\n            SET    col_name   = '{$col_name}',\n                   field_size = '{$field_size}'\n            WHERE  field_id = {$field_id}\n                      ");
                }
            }
            $message = $LANG["validation_db_not_updated_invalid_input"];
            if ($g_debug) {
                $message .= " \"{$err_message}\"";
            }
            return array(false, $message);
        }
    }
    // now update the fields, and, if need be, the form's database table
    foreach ($field_info as $field) {
        if ($field["is_new_field"]) {
            continue;
        }
        $field_id = $field["field_id"];
        $display_name = $field["display_name"];
        $field_name = $field["form_field_name"];
        $field_type_id = $field["field_type_id"];
        $include_on_redirect = $field["include_on_redirect"];
        $is_system_field = $field["is_system_field"];
        $field_size = $field["field_size"];
        $col_name = $field["col_name"];
        $list_order = $field["list_order"];
        $is_new_sort_group = $field["is_new_sort_group"];
        if ($is_system_field == "yes") {
            $query = "\n        UPDATE {$g_table_prefix}form_fields\n        SET    field_title = '{$display_name}',\n               include_on_redirect = '{$include_on_redirect}',\n               list_order = {$list_order},\n               is_new_sort_group = '{$is_new_sort_group}'\n        WHERE  field_id = {$field_id}\n                  ";
        } else {
            $query = "\n        UPDATE {$g_table_prefix}form_fields\n        SET    field_name = '{$field_name}',\n               field_title = '{$display_name}',\n               field_size = '{$field_size}',\n               col_name = '{$col_name}',\n               field_type_id  = '{$field_type_id}',\n               include_on_redirect = '{$include_on_redirect}',\n               list_order = {$list_order},\n               is_new_sort_group = '{$is_new_sort_group}'\n        WHERE  field_id = {$field_id}\n                  ";
        }
        mysql_query($query) or ft_handle_error("Failed query in <b>" . __FUNCTION__ . "</b>, line " . __LINE__ . ": <i>{$query}</i>", mysql_error());
    }
    // if any of the database column names just changed we need to update any View filters that relied on them
    if (!empty($db_col_changes)) {
        while (list($field_id, $changes) = each($db_col_changes)) {
            ft_update_field_filters($field_id);
        }
    }
    // okay! now add any new fields that the user just added
    $new_fields = array();
    foreach ($field_info as $curr_field) {
        if ($curr_field["is_new_field"]) {
            $new_fields[] = $curr_field;
        }
    }
    if (!empty($new_fields)) {
        list($is_success, $error) = ft_add_form_fields($form_id, $new_fields);
        // if there was a problem adding any of the new fields, inform the user
        if (!$is_success) {
            $success = false;
            $message = $error;
        }
    }
    // Lastly, delete the specified fields. Since some field types (e.g. files) may have additional functionality
    // needed at this stage (e.g. deleting the actual files that had been uploaded via the form). This occurs regardless
    // of whether the add fields step worked or not
    $deleted_field_ids = explode(",", $infohash["{$sortable_id}_sortable__deleted_rows"]);
    extract(ft_process_hook_calls("delete_fields", compact("deleted_field_ids", "infohash", "form_id"), array()), EXTR_OVERWRITE);
    // now actually delete the fields
    ft_delete_form_fields($form_id, $deleted_field_ids);
    extract(ft_process_hook_calls("end", compact("infohash", "field_info", "form_id"), array("success", "message")), EXTR_OVERWRITE);
    return array($success, $message);
}
Ejemplo n.º 2
0
/**
 * Adds/updates all options for a given field. This is called when the user edits fields from the dialog
 * window on the Fields tab. It updates all information about a field: including the custom settings.
 *
 * @param integer $form_id The unique form ID
 * @param integer $field_id The unique field ID
 * @param integer $info a hash containing tab1 and/or tab2 indexes, containing all the latest values for
 *                the field
 * @param array [0] success/fail (boolean), [1] empty string for success, or error message
 */
function ft_update_field($form_id, $field_id, $tab_info)
{
    global $g_table_prefix, $g_field_sizes, $g_debug, $LANG;
    $tab_info = ft_sanitize($tab_info);
    $existing_form_field_info = ft_get_form_field($field_id);
    // TAB 1: this tab contains the standard settings shared by all fields, regardless of type: display text,
    // form field name, field type, pass on, field size, data type and database col name
    $db_col_name_changes = array();
    if (is_array($tab_info["tab1"])) {
        $info = $tab_info["tab1"];
        $display_name = _ft_extract_array_val($info, "edit_field__display_text");
        // bit weird. this field is a checkbox, so if it's not checked it won't be in the request and
        // _ft_extract_array_val returns an empty string
        $include_on_redirect = _ft_extract_array_val($info, "edit_field__pass_on");
        $include_on_redirect = empty($include_on_redirect) ? "no" : "yes";
        if ($existing_form_field_info["is_system_field"] == "yes") {
            $query = "\n        UPDATE {$g_table_prefix}form_fields\n        SET    field_title = '{$display_name}',\n               include_on_redirect = '{$include_on_redirect}'\n        WHERE  field_id = {$field_id}\n      ";
            $result = mysql_query($query);
            if (!$result) {
                return array(false, $LANG["phrase_query_problem"] . $query);
            }
        } else {
            $field_name = _ft_extract_array_val($info, "edit_field__field_name");
            $field_type_id = _ft_extract_array_val($info, "edit_field__field_type");
            $field_size = _ft_extract_array_val($info, "edit_field__field_size");
            $data_type = _ft_extract_array_val($info, "edit_field__data_type");
            $col_name = _ft_extract_array_val($info, "edit_field__db_column");
            $query = mysql_query("\n        UPDATE {$g_table_prefix}form_fields\n        SET    field_name = '{$field_name}',\n               field_type_id = '{$field_type_id}',\n               field_size = '{$field_size}',\n               field_title = '{$display_name}',\n               data_type = '{$data_type}',\n               include_on_redirect = '{$include_on_redirect}',\n               col_name = '{$col_name}'\n        WHERE  field_id = {$field_id}\n          ");
            // if the column name or field size just changed, we need to "physically" update the form's database table
            // If this fails, we rollback both the field TYPE and the field size.
            // BUG The *one* potential issue here is if the user just deleted a field type, then updated a field which - for
            // whatever reason - fails. But this is very much a fringe case
            $old_field_size = $existing_form_field_info["field_size"];
            $old_col_name = $existing_form_field_info["col_name"];
            $old_field_type_id = $existing_form_field_info["field_type_id"];
            if ($old_field_size != $field_size || $old_col_name != $col_name) {
                $new_field_size_sql = $g_field_sizes[$field_size]["sql"];
                $table_name = "{$g_table_prefix}form_{$form_id}";
                list($is_success, $err_message) = _ft_alter_table_column($table_name, $old_col_name, $col_name, $new_field_size_sql);
                if ($is_success) {
                    if ($old_col_name != $col_name) {
                        $db_col_name_changes[] = $field_id;
                    }
                } else {
                    $query = mysql_query("\n            UPDATE {$g_table_prefix}form_fields\n            SET    field_type_id = '{$old_field_type_id}',\n                   field_size    = '{$old_field_size}',\n                   col_name      = '{$old_col_name}'\n            WHERE  field_id = {$field_id}\n              ");
                    return array(false, $LANG["phrase_query_problem"] . $err_message);
                }
            }
            // if the field type just changed, the field-specific settings are orphaned. Drop them. In this instance, the
            // client-side code ensures that the contents of the second tab are always passed so the code below will add
            // any default values that are needed
            if ($old_field_type_id != $field_type_id) {
                ft_delete_extended_field_settings($field_id);
            }
        }
    }
    // if any of the database column names just changed we need to update any View filters that relied on them
    if (!empty($db_col_name_changes)) {
        foreach ($db_col_name_changes as $field_id) {
            ft_update_field_filters($field_id);
        }
    }
    // TAB 2: update the custom field settings for this field type. tab2 can be any of these values:
    //  1. a string "null": indicating that the user didn't change anything on the tab)
    //  2. the empty string: indicating that things DID change, but nothing is being passed on. This can happen
    //                      when the user checked the "Use Default Value" for all fields on the tab & the tab
    //                      doesn't contain an option list or form field
    //  3. an array of values
    if (isset($tab_info["tab2"]) && $tab_info["tab2"] != "null") {
        $info = is_array($tab_info["tab2"]) ? $tab_info["tab2"] : array();
        // since the second tab is being updated, we can rely on all the latest & greatest values being passed
        // in the request, so clean out all old values
        ft_delete_extended_field_settings($field_id);
        // convert the $info (which is an array of hashes) into a friendlier hash. This makes detecting for Option
        // List fields much easier
        $setting_hash = array();
        for ($i = 0; $i < count($info); $i++) {
            $setting_hash[$info[$i]["name"]] = $info[$i]["value"];
        }
        $new_settings = array();
        while (list($setting_name, $setting_value) = each($setting_hash)) {
            // ignore the additional field ID and field order rows that are custom to Option List / Form Field types. They'll
            // be handled below
            if (preg_match("/edit_field__setting_(\\d)+_field_id/", $setting_name) || preg_match("/edit_field__setting_(\\d)+_field_order/", $setting_name)) {
                continue;
            }
            // TODO BUG. newlines aren't surviving this... why was it added? double quotes? single quotes?
            $setting_value = ft_sanitize(stripslashes($setting_value));
            $setting_id = preg_replace("/edit_field__setting_/", "", $setting_name);
            // if this field is being mapped to a form field, we serialize the form ID, field ID and order into a single var and
            // give it a "form_field:" prefix, so we know exactly what the data contains & we can select the appropriate form ID
            // and not Option List ID on re-editing. This keeps everything pretty simple, rather than spreading the data amongst
            // multiple fields
            if (preg_match("/^ft/", $setting_value)) {
                $setting_value = preg_replace("/^ft/", "", $setting_value);
                $setting_value = "form_field:{$setting_value}|" . $setting_hash["edit_field__setting_{$setting_id}_field_id"] . "|" . $setting_hash["edit_field__setting_{$setting_id}_field_order"];
            }
            $new_settings[] = "({$field_id}, {$setting_id}, '{$setting_value}')";
        }
        if (!empty($new_settings)) {
            $new_settings_str = implode(",", $new_settings);
            $query = "\n        INSERT INTO {$g_table_prefix}field_settings (field_id, setting_id, setting_value)\n        VALUES {$new_settings_str}\n      ";
            $result = @mysql_query($query) or die($query . " - " . mysql_error());
            if (!$result) {
                return array(false, $LANG["phrase_query_problem"] . $query . ", " . mysql_error());
            }
        }
    }
    if (isset($tab_info["tab3"]) && $tab_info["tab3"] != "null") {
        $validation = is_array($tab_info["tab3"]) ? $tab_info["tab3"] : array();
        mysql_query("DELETE FROM {$g_table_prefix}field_validation WHERE field_id = {$field_id}");
        $new_rules = array();
        foreach ($validation as $rule_info) {
            // ignore the checkboxes - we don't need 'em
            if (!preg_match("/^edit_field__v_(.*)_message\$/", $rule_info["name"], $matches)) {
                continue;
            }
            $rule_id = $matches[1];
            $error_message = ft_sanitize($rule_info["value"]);
            mysql_query("\n        INSERT INTO {$g_table_prefix}field_validation (rule_id, field_id, error_message)\n        VALUES ({$rule_id}, {$field_id}, '{$error_message}')\n      ");
        }
    }
    $success = true;
    $message = $LANG["notify_form_field_options_updated"];
    extract(ft_process_hook_calls("end", compact("field_id"), array("success", "message")), EXTR_OVERWRITE);
    return array($success, $message);
}