/** * This constructs and sends the email from an individual email template for a single form * submission. * * @param integer $form_id * @param integer $submission_id * @param integer $email_id */ function ft_process_email_template($form_id, $submission_id, $email_id) { list($success, $email_components) = ft_get_email_components($form_id, $submission_id, $email_id); if (!$success) { return array(false, "Email components not returned properly (ft_get_email_components)."); } extract(ft_process_hook_calls("start", compact("form_id", "submission_id", "email_id", "email_components"), array("email_components")), EXTR_OVERWRITE); // if Swift Mailer is enabled, send the emails with that $continue = true; if (ft_check_module_enabled("swift_mailer")) { $sm_settings = ft_get_module_settings("", "swift_mailer"); if (isset($sm_settings["swiftmailer_enabled"]) && $sm_settings["swiftmailer_enabled"] == "yes") { ft_include_module("swift_mailer"); list($success, $message) = swift_send_email($email_components); $continue = false; } } // if it was sent (or was attempted to have been sent) by the Swift Mailer module, stop here if (!$continue) { return array($success, $message); } $eol = _ft_get_email_eol_char(); $recipient_list = array(); foreach ($email_components["to"] as $to_info) { $recipient_list[] = $to_info["recipient_line"]; } $to = join(", ", $recipient_list); $to = htmlspecialchars_decode($to); if (empty($to)) { return array(false, "No main recipient specified."); } $headers = "MIME-Version: 1.0{$eol}"; if (!empty($email_components["from"])) { $from = htmlspecialchars_decode($email_components["from"]["recipient_line"]); $headers .= "From: {$from}{$eol}"; } if (!empty($email_components["reply_to"])) { $reply_to = htmlspecialchars_decode($email_components["reply_to"]["recipient_line"]); $headers .= "Reply-to: {$reply_to}{$eol}"; } if (!empty($email_components["cc"])) { $cc_list = array(); foreach ($email_components["cc"] as $cc_info) { $cc_list[] = $cc_info["recipient_line"]; } $cc = join(", ", $cc_list); $cc = htmlspecialchars_decode($cc); $headers .= "Cc: {$cc}{$eol}"; } if (!empty($email_components["bcc"])) { $bcc_list = array(); foreach ($email_components["bcc"] as $bcc_info) { $bcc_list[] = $bcc_info["recipient_line"]; } $bcc = join(", ", $bcc_list); $bcc = htmlspecialchars_decode($bcc); $headers .= "Bcc: {$bcc}{$eol}"; } $message = ""; $html_content = isset($email_components["html_content"]) ? $email_components["html_content"] : ""; $text_content = isset($email_components["text_content"]) ? $email_components["text_content"] : ""; $html_content = trim($html_content); $text_content = trim($text_content); // if there's no TO line or there's no email content for either types, we can't send the email if (empty($html_content) && empty($text_content)) { return array(false, "No text or HTML email content specified"); } if (!empty($html_content) && !empty($text_content)) { $headers .= _ft_get_multipart_message($html_content, $text_content, $eol); } else { if (!empty($html_content)) { $message = $html_content; $headers .= "Content-type: text/html; charset=UTF-8"; } else { if (!empty($text_content)) { $message = $text_content; $headers .= "Content-type: text/plain; charset=UTF-8"; } } } $subject = $email_components["subject"]; // send the email $email_sent = @mail("{$to}", $subject, $message, $headers); if ($email_sent) { return array(true, ""); } else { return array(false, "The mail() function failed to send the email."); } }
/** * Loads a theme opening page for a module. This should be used loaded for every page in a * module. It serves the same function as ft_display_page, except that it sets the appropriate root * folder for the module and loads the * * @param array $page_vars a hash of information to display / provide to the template. * @param string $theme */ function ft_display_module_page($template, $page_vars = array(), $theme = "", $swatch = "") { global $g_root_dir, $g_root_url, $g_success, $g_message, $g_link, $g_smarty_debug, $g_language, $LANG, $g_smarty, $L, $g_smarty_use_sub_dirs, $g_js_debug, $g_benchmark_start, $g_enable_benchmarking, $g_hide_upgrade_link; $module_folder = _ft_get_current_module_folder(); // $module_id = ft_get_module_id_from_module_folder($module_folder); $default_module_language = "en_us"; if (empty($theme) && isset($_SESSION["ft"]["account"]["theme"])) { $theme = $_SESSION["ft"]["account"]["theme"]; $swatch = isset($_SESSION["ft"]["account"]["swatch"]) ? $_SESSION["ft"]["account"]["swatch"] : ""; } elseif (empty($theme)) { $settings = ft_get_settings(array("default_theme", "default_client_swatch")); $theme = $settings["default_theme"]; $swatch = $settings["default_client_swatch"]; } if (!isset($_SESSION["ft"]["account"]["is_logged_in"])) { $_SESSION["ft"]["account"]["is_logged_in"] = false; } if (!isset($_SESSION["ft"]["account"]["account_type"])) { $_SESSION["ft"]["account"]["account_type"] = ""; } // common variables. These are sent to EVERY template $g_smarty->template_dir = "{$g_root_dir}/themes/{$theme}"; $g_smarty->compile_dir = "{$g_root_dir}/themes/{$theme}/cache/"; $g_smarty->use_sub_dirs = $g_smarty_use_sub_dirs; $g_smarty->assign("LANG", $LANG); // this contains the custom language content of the module, in the language required. It's populated by // ft_init_module_page(), called on every module page $g_smarty->assign("L", $L); $g_smarty->assign("SESSION", $_SESSION["ft"]); $settings = isset($_SESSION["ft"]["settings"]) ? $_SESSION["ft"]["settings"] : array(); $g_smarty->assign("settings", $settings); $g_smarty->assign("account", $_SESSION["ft"]["account"]); $g_smarty->assign("g_root_dir", $g_root_dir); $g_smarty->assign("g_root_url", $g_root_url); $g_smarty->assign("g_js_debug", $g_js_debug ? "true" : "false"); $g_smarty->assign("g_hide_upgrade_link", $g_hide_upgrade_link); $g_smarty->assign("same_page", ft_get_clean_php_self()); $g_smarty->assign("query_string", $_SERVER["QUERY_STRING"]); // TODO FIX $g_smarty->assign("dir", $LANG["special_text_direction"]); $g_smarty->assign("g_enable_benchmarking", $g_enable_benchmarking); $g_smarty->assign("swatch", $swatch); // if this page has been told to dislay a custom message, override g_success and g_message if (isset($_GET["message"])) { list($g_success, $g_message) = ft_display_custom_page_message($_GET["message"]); } $g_smarty->assign("g_success", $g_success); $g_smarty->assign("g_message", $g_message); $module_id = ft_get_module_id_from_module_folder($module_folder); $module_nav = ft_get_module_menu_items($module_id, $module_folder); $g_smarty->assign("module_nav", $module_nav); // if there's no module title, display the module name. TODO not compatible with languages... if (!isset($page_vars["head_title"])) { $module_id = ft_get_module_id_from_module_folder($module_folder); $module_info = ft_get_module($module_id); $page_vars["head_title"] = $module_info["module_name"]; } // check the "required" vars are at least set so they don't produce warnings when smarty debug is enabled if (!isset($page_vars["head_css"])) { $page_vars["head_css"] = ""; } if (!isset($page_vars["head_js"])) { $page_vars["head_js"] = ""; } if (!isset($page_vars["page"])) { $page_vars["page"] = ""; } // if we need to include custom JS messages in the page, add it to the generated JS. Note: even if the js_messages // key is defined but still empty, the ft_generate_js_messages function is called, returning the "base" JS - like // the JS version of g_root_url. Only if it is not defined will that info not be included. This feature was hacked // in 2.1 to support js_messages from a single module file $js_messages = ""; if (isset($page_vars["js_messages"]) || isset($page_vars["module_js_messages"])) { $core_js_messages = isset($page_vars["js_messages"]) ? $page_vars["js_messages"] : ""; $module_js_messages = isset($page_vars["module_js_messages"]) ? $page_vars["module_js_messages"] : ""; $js_messages = ft_generate_js_messages($core_js_messages, $module_js_messages); } if (!empty($page_vars["head_js"]) || !empty($js_messages)) { $page_vars["head_js"] = "<script type=\"text/javascript\">\n//<![CDATA[\n{$page_vars["head_js"]}\n{$js_messages}\n//]]>\n</script>"; } if (!isset($page_vars["head_css"])) { $page_vars["head_css"] = ""; } else { if (!empty($page_vars["head_css"])) { $page_vars["head_css"] = "<style type=\"text/css\">\n{$page_vars["head_css"]}\n</style>"; } } // theme-specific vars $g_smarty->assign("images_url", "{$g_root_url}/themes/{$theme}/images"); $g_smarty->assign("theme_url", "{$g_root_url}/themes/{$theme}"); $g_smarty->assign("theme_dir", "{$g_root_dir}/themes/{$theme}"); // if there's a Smarty folder, import any of its resources if (is_dir("{$g_root_dir}/modules/{$module_folder}/smarty")) { $g_smarty->plugins_dir[] = "{$g_root_dir}/modules/{$module_folder}/smarty"; } // now add the custom variables for this template, as defined in $page_vars foreach ($page_vars as $key => $value) { $g_smarty->assign($key, $value); } // if smarty debug is on, enable Smarty debugging if ($g_smarty_debug) { $g_smarty->debugging = true; } extract(ft_process_hook_calls("main", compact("g_smarty", "template", "page_vars"), array("g_smarty")), EXTR_OVERWRITE); $g_smarty->display("{$g_root_dir}/modules/{$module_folder}/{$template}"); ft_db_disconnect($g_link); }
/** * Returns an array of account IDs of those clients in the omit list for this public form. * * @param integer $form_id * @return array */ function ft_get_public_form_omit_list($form_id) { global $g_table_prefix; $query = mysql_query("\n SELECT account_id\n FROM {$g_table_prefix}public_form_omit_list\n WHERE form_id = {$form_id}\n "); $client_ids = array(); while ($row = mysql_fetch_assoc($query)) { $client_ids[] = $row["account_id"]; } extract(ft_process_hook_calls("end", compact("clients_id", "form_id"), array("client_ids")), EXTR_OVERWRITE); return $client_ids; }
/** * Returns all files associated with a particular form field or fields. Different field types may store the * files differently, so EVERY file upload module needs to add a hook to this function to return the * appropriate information. * * The module functions should return an array of hashes with the following structure: * array( * "submission_id" => * "field_id" => * "field_type_id" => * "folder_path" => * "folder_url" => * "filename" => * ), * ... * * @param integer $form_id the unique form ID * @param array $field_ids an array of field IDs */ function ft_get_uploaded_files($form_id, $field_ids) { $uploaded_files = array(); extract(ft_process_hook_calls("start", compact("form_id", "field_ids"), array("uploaded_files")), EXTR_OVERWRITE); return $uploaded_files; }
/** * Deletes a client menu. Since it's possible for one or more clients to already be associated with the * menu, those clients will be orphaned by this action. In this situation, it refuses to delete the * menu, and lists all clients that will be affected (each a link to their account). It also provides * an option to bulk assign them to another menu. * * In all likelihood, however, the administrator will already be aware of this, having seen their names * listed in the table where they chose to delete the menu. * * @param integer $menu_id * @return array [0] T/F, [1] message */ function ft_delete_client_menu($menu_id) { global $g_table_prefix, $g_root_url, $LANG; extract(ft_process_hook_calls("start", compact("menu_id"), array()), EXTR_OVERWRITE); // confirm that there are no client accounts that currently use this menu $query = mysql_query("\r\n SELECT account_id, first_name, last_name\r\n FROM {$g_table_prefix}accounts\r\n WHERE menu_id = {$menu_id}\r\n "); $client_info = array(); while ($row = mysql_fetch_assoc($query)) { $client_info[] = $row; } if (!empty($client_info)) { $message = $LANG["notify_deleted_menu_already_assigned"]; $placeholder_str = $LANG["phrase_assign_all_listed_client_accounts_to_menu"]; $menus = ft_get_menu_list(); $dd = "<select id=\"mass_update_client_menu\">"; foreach ($menus as $menu_info) { if ($menu_info["menu_type"] == "admin") { continue; } $dd .= "<option value=\"{$menu_info["menu_id"]}\">{$menu_info["menu"]}</option>"; } $dd .= "</select>"; // a bit bad (hardcoded HTML!), but organize the account list in 3 columns $client_links_table = "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\">\n<tr>"; $num_affected_clients = count($client_info); for ($i = 0; $i < $num_affected_clients; $i++) { $account_info = $client_info[$i]; $client_id = $account_info["account_id"]; $first_name = $account_info["first_name"]; $last_name = $account_info["last_name"]; $client_ids[] = $client_id; if ($i != 0 && $i % 3 == 0) { $client_links_table .= "</tr>\n<tr>"; } $client_links_table .= "<td width=\"33%\">• <a href=\"{$g_root_url}/admin/clients/edit.php?page=settings&client_id={$client_id}\" target=\"_blank\">{$first_name} {$last_name}</a></td>\n"; } $client_id_str = join(",", $client_ids); // close the table if ($num_affected_clients % 3 == 1) { $client_links_table .= "<td colspan=\"2\" width=\"66%\"> </td>"; } else { if ($num_affected_clients % 3 == 2) { $client_links_table .= "<td width=\"33%\"> </td>"; } } $client_links_table .= "</tr></table>"; $submit_button = "<input type=\"button\" value=\"{$LANG["phrase_update_accounts"]}\" onclick=\"window.location='index.php?page=menus&mass_assign=1&accounts={$client_id_str}&menu_id=' + \$('#mass_update_client_menu').val()\" />"; $placeholders = array("menu_dropdown" => $dd, "submit_button" => $submit_button); $mass_assign_html = "<div class=\"margin_top_large margin_bottom_large\">" . ft_eval_smarty_string($placeholder_str, $placeholders) . "</div>"; $html = $message . $mass_assign_html . $client_links_table; return array(false, $html); } // ------------------------------------------------------------ $client_account_query = mysql_query("\r\n SELECT account_id, first_name, last_name\r\n FROM {$g_table_prefix}accounts\r\n WHERE menu_id = {$menu_id}\r\n "); // delete the menu mysql_query("DELETE FROM {$g_table_prefix}menus WHERE menu_id = {$menu_id}"); mysql_query("DELETE FROM {$g_table_prefix}menu_items WHERE menu_id = {$menu_id}"); // construct the message to return to the administrator $client_accounts = array(); while ($row = mysql_fetch_assoc($client_account_query)) { $client_accounts[] = $row; } if (empty($client_accounts)) { $success = true; $message = $LANG["notify_client_menu_deleted"]; } else { $success = false; $message = $LANG["notify_client_menu_deleted_orphaned_accounts"]; $accounts_str = "<br />"; foreach ($client_accounts as $account_info) { $client_id = $account_info["account_id"]; $first_name = $account_info["first_name"]; $last_name = $account_info["last_name"]; $accounts_str .= "• <a href=\"{$g_root_url}/admin/clients/edit.php?client_id={$client_id}\" target=\"_blank\">{$first_name} {$last_name}</a><br />\n"; } $message .= $accounts_str; } return array($success, $message); }
/** * This is called by the ft_delete_submission and ft_delete_submissions function. It's passed all relevant * information about the submission & file fields that need to be deleted. The function is just a stub to * allow file upload modules to add their hooks to. * * Modules that extend this function should return $problems. That should be an array of hashes. Each hash * having keys "filename" and "error". Since the calling functions will blithely delete the submissions even * if the file deletion fails, no other info is worth returning. * * @param integer $form_id * @param array $file_info an array of hashes. Each hash has the following keys (all self-explanatory): * submission_id - * field_id - * filename - * field_type_id - * @param string $context. Just used to pass a little more info to the hook. This is the context in which this * function is being called; i.e. the function name / action. */ function ft_delete_submission_files($form_id, $file_field_info, $context = "") { $success = true; $problems = array(); extract(ft_process_hook_calls("start", compact("form_id", "file_field_info"), array("success", "problems")), EXTR_OVERWRITE); return array($success, $problems); }
/** * Called by the administrator from the Themes settings page. It updates the list of enabled * themes, and which theme is assigned to the administrator and (default) client accounts. Note: * it doesn't disable any themes that are already assigned to a user account. If that happens, * it returns a message listing the accounts (each clickable) and an option to bulk assign them * to a different theme. * * @param array $infohash this parameter should be a hash (e.g. $_POST or $_GET) containing the * various fields from the main settings admin page. * @return array Returns array with indexes:<br/> * [0]: true/false (success / failure)<br/> * [1]: message string<br/> */ function ft_update_theme_settings($infohash) { global $g_table_prefix, $g_root_url, $g_root_dir, $LANG; // lots to validate! First, check the default admin & client themes have been entered $rules = array(); $rules[] = "required,admin_theme,{$LANG["validation_no_admin_theme"]}"; $rules[] = "required,default_client_theme,{$LANG["validation_no_default_client_theme"]}"; $errors = validate_fields($infohash, $rules); if (!isset($infohash["is_enabled"])) { $errors[] = $LANG["validation_no_enabled_themes"]; } if (!empty($errors)) { $success = false; array_walk($errors, create_function('&$el', '$el = "• " . $el;')); $message = join("<br />", $errors); return array($success, $message); } $enabled_themes = $infohash["is_enabled"]; // next, check that both the admin and default client themes are enabled $admin_theme = $infohash["admin_theme"]; $default_client_theme = $infohash["default_client_theme"]; if (!in_array($admin_theme, $enabled_themes) || !in_array($default_client_theme, $enabled_themes)) { return array(false, $LANG["validation_default_admin_and_client_themes_not_enabled"]); } // lastly, if there are already client accounts assigned to disabled themes, we need to sort it out. // We handle it the same way as deleting the client menus: if anyone is assigned to this theme, // we generate a list of their names, each a link to their account page (in a _blank link). We // then inform the user of what's going on, and underneath the name list, give them the option of // assigning ALL affected accounts to another (enabled) theme. $theme_clauses = array(); foreach ($enabled_themes as $theme) { $theme_clauses[] = "theme != '{$theme}'"; } $theme_clause = join(" AND ", $theme_clauses); $query = mysql_query("\n SELECT account_id, first_name, last_name\n FROM {$g_table_prefix}accounts\n WHERE {$theme_clause}\n "); $client_info = array(); while ($row = mysql_fetch_assoc($query)) { $client_info[] = $row; } if (!empty($client_info)) { $message = $LANG["notify_disabled_theme_already_assigned"]; $placeholder_str = $LANG["phrase_assign_all_listed_client_accounts_to_theme"]; $themes = ft_get_themes(true); $dd = "<select id=\"mass_update_client_theme\">"; foreach ($themes as $theme) { $dd .= "<option value=\"{$theme["theme_id"]}\">{$theme["theme_name"]}</option>"; } $dd .= "</select>"; // a bit bad (hardcoded HTML!), but organize the account list in 3 columns $client_links_table = "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\">\n<tr>"; $num_affected_clients = count($client_info); for ($i = 0; $i < $num_affected_clients; $i++) { $account_info = $client_info[$i]; $client_id = $account_info["account_id"]; $first_name = $account_info["first_name"]; $last_name = $account_info["last_name"]; $client_ids[] = $client_id; if ($i != 0 && $i % 3 == 0) { $client_links_table .= "</tr>\n<tr>"; } $client_links_table .= "<td width=\"33%\">• <a href=\"{$g_root_url}/admin/clients/edit.php?page=settings&client_id={$client_id}\" target=\"_blank\">{$first_name} {$last_name}</a></td>\n"; } $client_id_str = join(",", $client_ids); // close the table if ($num_affected_clients % 3 == 1) { $client_links_table .= "<td colspan=\"2\" width=\"66%\"> </td>"; } else { if ($num_affected_clients % 3 == 2) { $client_links_table .= "<td width=\"33%\"> </td>"; } } $client_links_table .= "</tr></table>"; $submit_button = "<input type=\"button\" value=\"{$LANG["phrase_update_accounts"]}\" onclick=\"window.location='index.php?page=themes&mass_assign=1&accounts={$client_id_str}&theme_id=' + \$('#mass_update_client_theme').val()\" />"; $placeholders = array("theme_dropdown" => $dd, "submit_button" => $submit_button); $mass_assign_html = "<div class=\"margin_top_large margin_bottom_large\">" . ft_eval_smarty_string($placeholder_str, $placeholders) . "</div>"; $html = $message . $mass_assign_html . $client_links_table; return array(false, $html); } // hoorah! Validation complete, let's update the bloomin' database at last // update the admin settings $admin_id = $_SESSION["ft"]["account"]["account_id"]; $admin_swatch = ""; if (isset($infohash["{$admin_theme}_admin_theme_swatches"])) { $admin_swatch = $infohash["{$admin_theme}_admin_theme_swatches"]; } mysql_query("\n UPDATE {$g_table_prefix}accounts\n SET theme = '{$admin_theme}',\n swatch = '{$admin_swatch}'\n WHERE account_id = {$admin_id}\n "); $_SESSION["ft"]["account"]["theme"] = $admin_theme; $_SESSION["ft"]["account"]["swatch"] = $admin_swatch; $default_client_swatch = ""; if (isset($infohash["{$default_client_theme}_default_client_theme_swatches"])) { $default_client_swatch = $infohash["{$default_client_theme}_default_client_theme_swatches"]; } // update the default client theme & swatch $new_settings = array("default_theme" => $default_client_theme, "default_client_swatch" => $default_client_swatch); ft_set_settings($new_settings); // finally, update the enabled themes list. Only set the theme as enabled if the // cache folder is writable mysql_query("UPDATE {$g_table_prefix}themes SET is_enabled = 'no'"); foreach ($enabled_themes as $theme) { $cache_folder = "{$g_root_dir}/themes/{$theme}/cache"; // try and set the cache folder as writable if (!is_writable($cache_folder)) { @chmod($cache_folder, 0777); } if (!is_writable($cache_folder)) { continue; } mysql_query("\n UPDATE {$g_table_prefix}themes\n SET is_enabled = 'yes'\n WHERE theme_folder = '{$theme}'\n "); } // reset the settings in sessions $_SESSION["ft"]["settings"] = ft_get_settings(); $success = true; $message = $LANG["notify_themes_settings_updated"]; extract(ft_process_hook_calls("end", compact("infohash"), array("success", "message")), EXTR_OVERWRITE); return array($success, $message); }
/** * Generates the placeholders for a particular form submission. This is used in the email templates, and here and there * for providing placeholder functionality to fields (like the "Edit Submission Label" textfield for a form, where they can * enter placeholders populated here). * * This returns ALL available placeholders for a form, regardless of View. * * @param integer $form_id * @param integer $submission_id * @param array $client_info a hash of information about the appropriate user (optional) * @return array a hash of placeholders and their replacement values (e.g. $arr["FORMURL"] => 17) */ function ft_get_submission_placeholders($form_id, $submission_id, $client_info = "") { global $g_root_url; $placeholders = array(); $settings = ft_get_settings(); $form_info = ft_get_form($form_id); $submission_info = ft_get_submission($form_id, $submission_id); $admin_info = ft_get_admin_info(); $file_field_type_ids = ft_get_file_field_type_ids(); $field_types = ft_get_field_types(true); // now loop through the info stored for this particular submission and for this particular field, // add the custom submission responses to the placeholder hash $form_field_params = array("include_field_type_info" => true, "include_field_settings" => true, "evaluate_dynamic_settings" => true); $form_fields = ft_get_form_fields($form_id, $form_field_params); foreach ($submission_info as $field_info) { $field_id = $field_info["field_id"]; $field_name = $field_info["field_name"]; $field_type_id = $field_info["field_type_id"]; if ($field_info["is_system_field"] == "no") { $placeholders["QUESTION_{$field_name}"] = $field_info["field_title"]; } if (in_array($field_type_id, $file_field_type_ids)) { $field_settings = ft_get_field_settings($field_id); $placeholders["FILENAME_{$field_name}"] = $field_info["content"]; $placeholders["FILEURL_{$field_name}"] = "{$field_settings["folder_url"]}/{$field_info["content"]}"; } else { $detailed_field_info = array(); foreach ($form_fields as $curr_field_info) { if ($curr_field_info["field_id"] != $field_id) { continue; } $detailed_field_info = $curr_field_info; break; } $params = array("form_id" => $form_id, "submission_id" => $submission_id, "value" => $field_info["content"], "field_info" => $detailed_field_info, "field_types" => $field_types, "settings" => $settings, "context" => "email_template"); $value = ft_generate_viewable_field($params); $placeholders["ANSWER_{$field_name}"] = $value; // for backward compatibility if ($field_name == "core__submission_date") { $placeholders["SUBMISSIONDATE"] = $value; } else { if ($field_name == "core__last_modified") { $placeholders["LASTMODIFIEDDATE"] = $value; } else { if ($field_name == "core__ip_address") { $placeholders["IPADDRESS"] = $value; } } } } } // other misc placeholders $placeholders["ADMINEMAIL"] = $admin_info["email"]; $placeholders["FORMNAME"] = $form_info["form_name"]; $placeholders["FORMURL"] = $form_info["form_url"]; $placeholders["SUBMISSIONID"] = $submission_id; $placeholders["LOGINURL"] = $g_root_url . "/index.php"; if (!empty($client_info)) { $placeholders["EMAIL"] = $client_info["email"]; $placeholders["FIRSTNAME"] = $client_info["first_name"]; $placeholders["LASTNAME"] = $client_info["last_name"]; $placeholders["COMPANYNAME"] = $client_info["company_name"]; } extract(ft_process_hook_calls("end", compact("placeholders"), array("placeholders")), EXTR_OVERWRITE); return $placeholders; }
/** * Updates the administrator account. With the addition of the "UI Language" option, this action * gets a little more complicated. The problem is that we can't just update the UI language in * sessions *within* this function, because by the time this function is called, the appropriate * language file is already in memory and being used. So, to get around this problem, the login * information form now passes along both the new and old UI languages. If it's different, AFTER * this function is called, you need to reset sessions and refresh the page. So be aware that * this problem is NOT handled by this function, see: * /admin/accounts/index.php to see how it's solved. * * @param array $infohash This parameter should be a hash (e.g. $_POST or $_GET) containing the * following keys: first_name, last_name, user_name, password. * @param integer $user_id the administrator's user ID * @return array [0]: true/false (success / failure) * [1]: message string */ function ft_update_admin_account($infohash, $account_id) { global $g_table_prefix, $g_root_url, $LANG; $success = true; $message = $LANG["notify_account_updated"]; $infohash = ft_sanitize($infohash); extract(ft_process_hook_calls("start", compact("infohash", "account_id"), array("infohash")), EXTR_OVERWRITE); $rules = array(); $rules[] = "required,first_name,{$LANG["validation_no_first_name"]}"; $rules[] = "required,last_name,{$LANG["validation_no_last_name"]}"; $rules[] = "required,email,{$LANG["validation_no_email"]}"; $rules[] = "required,theme,{$LANG["validation_no_theme"]}"; $rules[] = "required,login_page,{$LANG["validation_no_login_page"]}"; $rules[] = "required,logout_url,{$LANG["validation_no_account_logout_url"]}"; $rules[] = "required,ui_language,{$LANG["validation_no_ui_language"]}"; $rules[] = "required,sessions_timeout,{$LANG["validation_no_sessions_timeout"]}"; $rules[] = "required,date_format,{$LANG["validation_no_date_format"]}"; $rules[] = "required,username,{$LANG["validation_no_username"]}"; $rules[] = "if:password!=,required,password_2,{$LANG["validation_no_account_password_confirmed"]}"; $rules[] = "if:password!=,same_as,password,password_2,{$LANG["validation_passwords_different"]}"; $errors = validate_fields($infohash, $rules); if (!empty($errors)) { $success = false; array_walk($errors, create_function('&$el', '$el = "• " . $el;')); $message = implode("<br />", $errors); return array($success, $message); } $first_name = $infohash["first_name"]; $last_name = $infohash["last_name"]; $email = $infohash["email"]; $theme = $infohash["theme"]; $login_page = $infohash["login_page"]; $logout_url = $infohash["logout_url"]; $ui_language = $infohash["ui_language"]; $timezone_offset = $infohash["timezone_offset"]; $sessions_timeout = $infohash["sessions_timeout"]; $date_format = $infohash["date_format"]; $username = $infohash["username"]; $password = $infohash["password"]; $swatch = ""; if (isset($infohash["{$theme}_theme_swatches"])) { $swatch = $infohash["{$theme}_theme_swatches"]; } // if the password is defined, md5 it $password_sql = !empty($password) ? "password = '******', " : ""; // check to see if username is already taken list($valid_username, $problem) = _ft_is_valid_username($username, $account_id); if (!$valid_username) { return array(false, $problem); } $query = "\n UPDATE {$g_table_prefix}accounts\n SET {$password_sql}\n first_name = '{$first_name}',\n last_name = '{$last_name}',\n email = '{$email}',\n theme = '{$theme}',\n swatch = '{$swatch}',\n login_page = '{$login_page}',\n logout_url = '{$logout_url}',\n ui_language = '{$ui_language}',\n timezone_offset = '{$timezone_offset}',\n sessions_timeout = '{$sessions_timeout}',\n date_format = '{$date_format}',\n username = '******'\n WHERE account_id = {$account_id}\n "; mysql_query($query) or ft_handle_error("Failed query in <b>" . __FUNCTION__ . "</b>: <i>{$query}</i>", mysql_error()); // update the settings $_SESSION["ft"]["settings"] = ft_get_settings(); $_SESSION["ft"]["account"] = ft_get_account_info($account_id); $_SESSION["ft"]["account"]["is_logged_in"] = true; // if the password just changed, update sessions and empty any temporary password that happens to have been // stored if (!empty($password)) { $_SESSION["ft"]["account"] = ft_get_account_info($account_id); $_SESSION["ft"]["account"]["is_logged_in"] = true; $_SESSION["ft"]["account"]["password"] = md5(md5($password)); mysql_query("UPDATE {$g_table_prefix}accounts SET temp_reset_password = NULL where account_id = {$account_id}"); } extract(ft_process_hook_calls("end", compact("infohash", "account_id"), array("success", "message")), EXTR_OVERWRITE); return array($success, $message); }
/** * Processes a form submission, either for a single page of a multi-page form or the entire form itself. If the * "submit_button_name key exists in $params (i.e. if the user just submitted the form), it updates the database for * the submission ID. * * Assumption: the ft_api_init_form_page function has been called on the page prior to calling this function. * * @param array $params * * Required keys: * "submit_button": the "name" attribute value of the form submit button * "form_data": the contents of $_POST (or $_GET, if "method" setting is set to "GET" ... ) * "file_data": the contents of $_FILES (only needed if your form contained file fields) * * Optional keys: * "next_page": the URL (relative or absolute) of which page to redirect to (e.g. the next page * in the form or the "thankyou" page). * "finalize": this tells the function to finalize the submission. This prevents it being subsequently * editable via this function and makes the submission appear in the Form Tools UI. * "no_sessions_url": for multi-page forms it's a good idea to pass along this value. It should be the URL * of a page (usually the FIRST page in the form sequence) where the user will be redirected to if * they didn't start the form from the first page. It ensures the form submission gets created & * submitted properly. * "may_update_finalized_submissions": true / false (true by default) * "namespace": if you specified a custom namespace for ft_api_init_form_page, for where the form values will * be stored temporarily in sessions, you need to pass that same value to this function - otherwise * it won't be able to retrieve the form and submission ID * "send_emails": (boolean). By default, Form Tools will trigger any emails that have been attached to the * "on submission" event ONLY when the submission is finalized (finalize=true). This setting provides * you with direct control over when the emails get sent. If not specified, will use the default * behaviour. * * @return mixed ordinarily, this function will just redirect the user to whatever URL is specified in the * "next_page" key. But if that value isn't set, it returns an array: * [0] success / false * [1] if failure, the API Error Code, otherwise blank */ function ft_api_process_form($params) { global $g_table_prefix, $g_multi_val_delimiter, $LANG, $g_api_debug, $g_api_recaptcha_private_key, $g_api_recaptcha_error; // the form data parameter must ALWAYS be defined if (!isset($params["form_data"])) { if ($g_api_debug) { $page_vars = array("message_type" => "error", "error_code" => 306, "error_type" => "user"); ft_display_page("error.tpl", $page_vars); exit; } else { return array(false, 306); } } // special case: if "form_tools_delete_image_field__[fieldname]" exists, the user is just deleting an image // already uploaded through the form using the HTML generated by the ft_api_display_image_field function. // In this case, we process the page normally - even though the form data wasn't submitted & the page may // contain nothing in $form_data $is_deleting_file = false; $file_field_to_delete = ""; $namespace = isset($params["namespace"]) ? $params["namespace"] : "form_tools_form"; $form_id = isset($_SESSION[$namespace]["form_tools_form_id"]) ? $_SESSION[$namespace]["form_tools_form_id"] : ""; $submission_id = isset($_SESSION[$namespace]["form_tools_submission_id"]) ? $_SESSION[$namespace]["form_tools_submission_id"] : ""; while (list($key, $value) = each($params["form_data"])) { if (preg_match("/form_tools_delete_image_field__(.*)\$/", $key, $matches)) { $file_field_to_delete = $matches[1]; $is_deleting_file = true; $field_id = ft_get_form_field_id_by_field_name($file_field_to_delete, $form_id); ft_delete_file_submission($form_id, $submission_id, $field_id, true); unset($_SESSION[$namespace][$file_field_to_delete]); unset($params["form_data"][$key]); } } // check the submission exists if (is_numeric($form_id) && is_numeric($submission_id) && !ft_check_submission_exists($form_id, $submission_id)) { if ($g_api_debug) { $page_vars = array("message_type" => "error", "error_code" => 305, "error_type" => "user", "debugging" => "{$LANG["phrase_submission_id"]}: {$submission_id}"); ft_display_page("error.tpl", $page_vars); exit; } else { return array(false, 305); } } // extract the submission ID and form ID from sessions $form_data = $params["form_data"]; $form_id = isset($_SESSION[$namespace]["form_tools_form_id"]) ? $_SESSION[$namespace]["form_tools_form_id"] : ""; $submission_id = isset($_SESSION[$namespace]["form_tools_submission_id"]) ? $_SESSION[$namespace]["form_tools_submission_id"] : ""; $has_captcha = isset($form_data["recaptcha_response_field"]) ? true : false; $no_sessions_url = isset($params["no_sessions_url"]) ? $params["no_sessions_url"] : false; if (!isset($_GET["ft_sessions_url_override"]) && (empty($form_id) || empty($submission_id))) { if (!empty($no_sessions_url)) { header("location: {$no_sessions_url}"); exit; } else { if ($g_api_debug) { $page_vars = array("message_type" => "error", "error_code" => 300, "error_type" => "user"); ft_display_page("error.tpl", $page_vars); exit; } else { return array(false, 300); } } } // if the user is neither deleting a file or making a regular form submission, it means they've just // arrived at the page. Cool! Do nothing! if (!$is_deleting_file && !isset($params["form_data"][$params["submit_button"]])) { return; } $submit_button_name = $params["submit_button"]; $next_page = isset($params["next_page"]) ? $params["next_page"] : ""; $file_data = isset($params["file_data"]) ? $params["file_data"] : array(); $finalize = isset($params["finalize"]) ? $params["finalize"] : false; $namespace = isset($params["namespace"]) ? $params["namespace"] : "form_tools_form"; $may_update_finalized_submissions = isset($params["may_update_finalized_submissions"]) ? $params["may_update_finalized_submissions"] : true; // if we're in test mode, we don't do anything with the database - just store the fields in // sessions to emulate if ($form_id == "test" || $submission_id == "test") { reset($form_data); while (list($field_name, $value) = each($form_data)) { $_SESSION[$namespace][$field_name] = $value; } } else { if (isset($_SESSION[$namespace]["form_tools_initialize_form"])) { // only process the form if this submission is being set to be finalized if ($finalize) { // if the user is just putting through a test submission and we've reached the finalization step, // overwrite $form_data with ALL the $all_form_data = array_merge($_SESSION[$namespace], $form_data); ft_initialize_form($all_form_data); } reset($form_data); while (list($field_name, $value) = each($form_data)) { $_SESSION[$namespace][$field_name] = $value; } } else { // check the form ID is valid if (!ft_check_form_exists($form_id)) { if ($g_api_debug) { $page_vars = array("message_type" => "error", "error_code" => 301, "error_type" => "user"); ft_display_page("error.tpl", $page_vars); exit; } else { return array(false, 301); } } // check the submission ID isn't finalized if (!$may_update_finalized_submissions && ft_check_submission_finalized($form_id, $submission_id)) { if ($g_api_debug) { $page_vars = array("message_type" => "error", "error_code" => 302, "error_type" => "user", "debugging" => "{$LANG["phrase_submission_id"]}: {$submission_id}"); ft_display_page("error.tpl", $page_vars); exit; } else { return array(false, 302); } } $form_info = ft_get_form($form_id); // check to see if this form has been disabled if ($form_info["is_active"] == "no") { if (isset($form_data["form_tools_inactive_form_redirect_url"])) { header("location: {$form_data["form_tools_inactive_form_redirect_url"]}"); exit; } if ($g_api_debug) { $page_vars = array("message_type" => "error", "error_code" => 303, "error_type" => "user"); ft_display_page("error.tpl", $page_vars); exit; } else { return array(false, 303); } } // now we sanitize the data (i.e. get it ready for the DB query) $form_data = ft_sanitize($form_data); extract(ft_process_hook_calls("start", compact("form_info", "form_id", "form_data"), array("form_data")), EXTR_OVERWRITE); // get a list of the custom form fields (i.e. non-system) for this form $form_fields = ft_get_form_fields($form_id, array("include_field_type_info" => true)); $custom_form_fields = array(); $file_fields = array(); foreach ($form_fields as $field_info) { $field_id = $field_info["field_id"]; $is_system_field = $field_info["is_system_field"]; $field_name = $field_info["field_name"]; // ignore system fields if ($is_system_field == "yes") { continue; } if ($field_info["is_file_field"] == "no") { $custom_form_fields[$field_name] = array("field_id" => $field_id, "col_name" => $field_info["col_name"], "field_title" => $field_info["field_title"], "include_on_redirect" => $field_info["include_on_redirect"], "field_type_id" => $field_info["field_type_id"], "is_date_field" => $field_info["is_date_field"]); } else { $file_fields[] = array("field_id" => $field_id, "field_info" => $field_info); } } // now examine the contents of the POST/GET submission and get a list of those fields // which we're going to update $valid_form_fields = array(); while (list($form_field, $value) = each($form_data)) { if (array_key_exists($form_field, $custom_form_fields)) { $curr_form_field = $custom_form_fields[$form_field]; $cleaned_value = $value; if (is_array($value)) { if ($form_info["submission_strip_tags"] == "yes") { for ($i = 0; $i < count($value); $i++) { $value[$i] = strip_tags($value[$i]); } } $cleaned_value = implode("{$g_multi_val_delimiter}", $value); } else { if ($form_info["submission_strip_tags"] == "yes") { $cleaned_value = strip_tags($value); } } $valid_form_fields[$curr_form_field["col_name"]] = "'{$cleaned_value}'"; } } $now = ft_get_current_datetime(); $ip_address = $_SERVER["REMOTE_ADDR"]; $is_finalized = $finalize ? "yes" : "no"; $set_query = ""; while (list($col_name, $value) = each($valid_form_fields)) { $set_query .= "{$col_name} = {$value},\n"; } // in this section, we update the database submission info & upload files. Note: we don't do ANYTHING // if the form_tools_ignore_submission key is set in the POST data if (!isset($form_data["form_tools_ignore_submission"])) { // construct our query. Note that we do TWO queries: one if there was no CAPTCHA sent with this // post (which automatically finalizes the result), and one if there WAS. For the latter, the submission // is finalized later if ($has_captcha && $finalize) { $query = "\n UPDATE {$g_table_prefix}form_{$form_id}\n SET {$set_query}\n last_modified_date = '{$now}',\n ip_address = '{$ip_address}'\n WHERE submission_id = {$submission_id}\n "; } else { // only update the is_finalized setting if $may_update_finalized_submissions === false if (!$finalize && $may_update_finalized_submissions) { $is_finalized_clause = ""; } else { $is_finalized_clause = ", is_finalized = '{$is_finalized}'"; } $query = "\n UPDATE {$g_table_prefix}form_{$form_id}\n SET {$set_query}\n last_modified_date = '{$now}',\n ip_address = '{$ip_address}'\n {$is_finalized_clause}\n WHERE submission_id = {$submission_id}\n "; } // only process the query if the form_tools_ignore_submission key isn't defined if (!mysql_query($query)) { if ($g_api_debug) { $page_vars = array("message_type" => "error", "error_code" => 304, "error_type" => "system", "debugging" => "Failed query in <b>" . __FUNCTION__ . ", " . __FILE__ . "</b>, line " . __LINE__ . ": <i>" . nl2br($query) . "</i> " . mysql_error()); ft_display_page("error.tpl", $page_vars); exit; } else { return array(false, 304); } } // used for uploading files. The error handling is incomplete here, like previous versions. Although the hooks // are permitted to return values, they're not used extract(ft_process_hook_calls("manage_files", compact("form_id", "submission_id", "file_fields", "namespace"), array("success", "message")), EXTR_OVERWRITE); } // store all the info in sessions reset($form_data); while (list($field_name, $value) = each($form_data)) { $_SESSION[$namespace][$field_name] = $value; } } } // was there a reCAPTCHA response? If so, a recaptcha was just submitted, check it was entered correctly $passes_captcha = true; if ($has_captcha) { $passes_captcha = false; $recaptcha_challenge_field = $form_data["recaptcha_challenge_field"]; $recaptcha_response_field = $form_data["recaptcha_response_field"]; $folder = dirname(__FILE__); require_once "{$folder}/recaptchalib.php"; $resp = recaptcha_check_answer($g_api_recaptcha_private_key, $_SERVER["REMOTE_ADDR"], $recaptcha_challenge_field, $recaptcha_response_field); if ($resp->is_valid) { $passes_captcha = true; // if the developer wanted the submission to be finalized at this step, do so - it wasn't earlier! if ($finalize) { mysql_query("\n UPDATE {$g_table_prefix}form_{$form_id}\n SET is_finalized = 'yes'\n WHERE submission_id = {$submission_id}\n "); } } else { // register the recaptcha as a global, which can be picked up silently by ft_api_display_captcha to // let them know they entered it wrong $g_api_recaptcha_error = $resp->error; } } if ($passes_captcha && !empty($next_page) && !$is_deleting_file) { // if the user wasn't putting through a test submission or initializing the form, we can send safely // send emails at this juncture, but ONLY if it was just finalized OR if the send_emails parameter // allows for it if ($form_id != "test" && $submission_id != "test" && !isset($_SESSION[$namespace]["form_tools_initialize_form"]) && !isset($form_data["form_tools_ignore_submission"])) { // send any emails attached to the on_submission trigger if (isset($params["send_emails"]) && $params["send_emails"] === true) { ft_send_emails("on_submission", $form_id, $submission_id); } else { if ($is_finalized == "yes" && (!isset($params["send_emails"]) || $params["send_emails"] !== false)) { ft_send_emails("on_submission", $form_id, $submission_id); } } } header("location: {$next_page}"); exit; } return array(true, ""); }
/** * This returns all forms and form Views that a client account may access. * * @param array $account_id */ function ft_get_client_form_views($account_id) { $client_forms = ft_search_forms($account_id); $info = array(); foreach ($client_forms as $form_info) { $form_id = $form_info["form_id"]; $views = ft_get_form_views($form_id, $account_id); $view_ids = array(); foreach ($views as $view_info) { $view_ids[] = $view_info["view_id"]; } $info[$form_id] = $view_ids; } extract(ft_process_hook_calls("end", compact("account_id", "info"), array("info")), EXTR_OVERWRITE); return $info; }
/** * A very simple getter function that retrieves an an ordered array of view_id => view name hashes for a * particular form. * * @param integer $form_id * @return array */ function ft_get_view_list($form_id) { global $g_table_prefix; $query = mysql_query("\r\n SELECT view_id, view_name\r\n FROM {$g_table_prefix}views\r\n WHERE form_id = {$form_id}\r\n ORDER BY view_order\r\n ") or dir(mysql_error()); $result = array(); while ($row = mysql_fetch_assoc($query)) { $result[] = $row; } extract(ft_process_hook_calls("end", compact("form_id", "result"), array("result")), EXTR_OVERWRITE); return $result; }
/** * This function processes the form submissions, after the form has been set up in the database. */ function ft_process_form($form_data) { global $g_table_prefix, $g_multi_val_delimiter, $g_query_str_multi_val_separator, $g_root_dir, $LANG, $g_api_version, $g_api_recaptcha_private_key; // ensure the incoming values are escaped $form_data = ft_sanitize($form_data); $form_id = $form_data["form_tools_form_id"]; $form_info = ft_get_form($form_id); // do we have a form for this id? if (!ft_check_form_exists($form_id)) { $page_vars = array("message_type" => "error", "message" => $LANG["processing_invalid_form_id"]); ft_display_page("error.tpl", $page_vars); exit; } extract(ft_process_hook_calls("start", compact("form_info", "form_id", "form_data"), array("form_data")), EXTR_OVERWRITE); // check to see if this form has been completely set up if ($form_info["is_complete"] == "no") { $page_vars = array("message_type" => "error", "message" => $LANG["processing_form_incomplete"]); ft_display_page("error.tpl", $page_vars); exit; } // check to see if this form has been disabled if ($form_info["is_active"] == "no") { if (isset($form_data["form_tools_inactive_form_redirect_url"])) { header("location: {$form_data["form_tools_inactive_form_redirect_url"]}"); exit; } $page_vars = array("message_type" => "error", "message" => $LANG["processing_form_disabled"]); ft_display_page("error.tpl", $page_vars); exit; } // do we have a form for this id? if (!ft_check_form_exists($form_id)) { $page_vars = array("message_type" => "error", "message" => $LANG["processing_invalid_form_id"]); ft_display_page("error.tpl", $page_vars); exit; } // was there a reCAPTCHA response? If so, a recaptcha was just submitted. This generally implies the // form page included the API, so check it was entered correctly. If not, return the user to the webpage if (isset($g_api_version) && isset($form_data["recaptcha_response_field"])) { $passes_captcha = false; $recaptcha_challenge_field = $form_data["recaptcha_challenge_field"]; $recaptcha_response_field = $form_data["recaptcha_response_field"]; $folder = dirname(__FILE__); require_once "{$folder}/global/api/recaptchalib.php"; $resp = recaptcha_check_answer($g_api_recaptcha_private_key, $_SERVER["REMOTE_ADDR"], $recaptcha_challenge_field, $recaptcha_response_field); if ($resp->is_valid) { $passes_captcha = true; } else { // since we need to pass all the info back to the form page we do it by storing the data in sessions. Enable 'em. @ft_api_start_sessions(); $_SESSION["form_tools_form_data"] = $form_data; $_SESSION["form_tools_form_data"]["api_recaptcha_error"] = $resp->error; // if there's a form_tools_form_url specified, redirect to that if (isset($form_data["form_tools_form_url"])) { header("location: {$form_data["form_tools_form_url"]}"); exit; } else { if (isset($_SERVER["HTTP_REFERER"])) { header("location: {$_SERVER["HTTP_REFERER"]}"); exit; } else { $page_vars = array("message_type" => "error", "message" => $LANG["processing_no_form_url_for_recaptcha"]); ft_display_page("error.tpl", $page_vars); exit; } } } } // get a list of the custom form fields (i.e. non-system) for this form $form_fields = ft_get_form_fields($form_id, array("include_field_type_info" => true)); $custom_form_fields = array(); $file_fields = array(); foreach ($form_fields as $field_info) { $field_id = $field_info["field_id"]; $is_system_field = $field_info["is_system_field"]; $field_name = $field_info["field_name"]; // ignore system fields if ($is_system_field == "yes") { continue; } if ($field_info["is_file_field"] == "no") { $custom_form_fields[$field_name] = array("field_id" => $field_id, "col_name" => $field_info["col_name"], "field_title" => $field_info["field_title"], "include_on_redirect" => $field_info["include_on_redirect"], "field_type_id" => $field_info["field_type_id"], "is_date_field" => $field_info["is_date_field"]); } else { $file_fields[] = array("field_id" => $field_id, "field_info" => $field_info); } } // now examine the contents of the POST/GET submission and get a list of those fields // which we're going to update $valid_form_fields = array(); while (list($form_field, $value) = each($form_data)) { // if this field is included, store the value for adding to DB if (array_key_exists($form_field, $custom_form_fields)) { $curr_form_field = $custom_form_fields[$form_field]; $cleaned_value = $value; if (is_array($value)) { if ($form_info["submission_strip_tags"] == "yes") { for ($i = 0; $i < count($value); $i++) { $value[$i] = strip_tags($value[$i]); } } $cleaned_value = implode("{$g_multi_val_delimiter}", $value); } else { if ($form_info["submission_strip_tags"] == "yes") { $cleaned_value = strip_tags($value); } } $valid_form_fields[$curr_form_field["col_name"]] = "'{$cleaned_value}'"; } } $now = ft_get_current_datetime(); $ip_address = $_SERVER["REMOTE_ADDR"]; $col_names = array_keys($valid_form_fields); $col_names_str = join(", ", $col_names); if (!empty($col_names_str)) { $col_names_str .= ", "; } $col_values = array_values($valid_form_fields); $col_values_str = join(", ", $col_values); if (!empty($col_values_str)) { $col_values_str .= ", "; } // build our query $query = "\r\n INSERT INTO {$g_table_prefix}form_{$form_id} ({$col_names_str} submission_date, last_modified_date, ip_address, is_finalized)\r\n VALUES ({$col_values_str} '{$now}', '{$now}', '{$ip_address}', 'yes')\r\n "; // add the submission to the database (if form_tools_ignore_submission key isn't set by either the form or a module) $submission_id = ""; if (!isset($form_data["form_tools_ignore_submission"])) { $result = mysql_query($query); if (!$result) { $page_vars = array("message_type" => "error", "error_code" => 304, "error_type" => "system", "debugging" => "Failed query in <b>" . __FUNCTION__ . ", " . __FILE__ . "</b>, line " . __LINE__ . ": <i>" . nl2br($query) . "</i>", mysql_error()); ft_display_page("error.tpl", $page_vars); exit; } $submission_id = mysql_insert_id(); extract(ft_process_hook_calls("end", compact("form_id", "submission_id"), array()), EXTR_OVERWRITE); } $redirect_query_params = array(); // build the redirect query parameter array foreach ($form_fields as $field_info) { if ($field_info["include_on_redirect"] == "no" || $field_info["is_file_field"] == "yes") { continue; } switch ($field_info["col_name"]) { case "submission_id": $redirect_query_params[] = "submission_id={$submission_id}"; break; case "submission_date": $settings = ft_get_settings(); $submission_date_formatted = ft_get_date($settings["default_timezone_offset"], $now, $settings["default_date_format"]); $redirect_query_params[] = "submission_date=" . rawurlencode($submission_date_formatted); break; case "last_modified_date": $settings = ft_get_settings(); $submission_date_formatted = ft_get_date($settings["default_timezone_offset"], $now, $settings["default_date_format"]); $redirect_query_params[] = "last_modified_date=" . rawurlencode($submission_date_formatted); break; case "ip_address": $redirect_query_params[] = "ip_address={$ip_address}"; break; default: $field_name = $field_info["field_name"]; // if $value is an array, convert it to a string, separated by $g_query_str_multi_val_separator if (isset($form_data[$field_name])) { if (is_array($form_data[$field_name])) { $value_str = join($g_query_str_multi_val_separator, $form_data[$field_name]); $redirect_query_params[] = "{$field_name}=" . rawurlencode($value_str); } else { $redirect_query_params[] = "{$field_name}=" . rawurlencode($form_data[$field_name]); } } break; } } // only upload files & send emails if we're not ignoring the submission if (!isset($form_data["form_tools_ignore_submission"])) { // now process any file fields. This is placed after the redirect query param code block above to allow whatever file upload // module to append the filename to the query string, if needed extract(ft_process_hook_calls("manage_files", compact("form_id", "submission_id", "file_fields", "redirect_query_params"), array("success", "message", "redirect_query_params")), EXTR_OVERWRITE); // send any emails ft_send_emails("on_submission", $form_id, $submission_id); } // if the redirect URL has been specified either in the database or as part of the form // submission, redirect the user [form submission form_tools_redirect_url value overrides // database value] if (!empty($form_info["redirect_url"]) || !empty($form_data["form_tools_redirect_url"])) { // build redirect query string $redirect_url = isset($form_data["form_tools_redirect_url"]) && !empty($form_data["form_tools_redirect_url"]) ? $form_data["form_tools_redirect_url"] : $form_info["redirect_url"]; $query_str = ""; if (!empty($redirect_query_params)) { $query_str = join("&", $redirect_query_params); } if (!empty($query_str)) { // only include the ? if it's not already there if (strpos($redirect_url, "?")) { $redirect_url .= "&" . $query_str; } else { $redirect_url .= "?" . $query_str; } } header("Location: " . $redirect_url); exit; } // the user should never get here! This means that the no redirect URL has been specified $page_vars = array("message_type" => "error", "message" => $LANG["processing_no_redirect_url"]); ft_display_page("error.tpl", $page_vars); exit; }
/** * Deletes a file that has been uploaded through a particular form submission file field. * * Now say that 10 times fast. * * @param integer $form_id the unique form ID * @param integer $submission_id a unique submission ID * @param integer $field_id a unique form field ID * @param boolean $force_delete this forces the file to be deleted from the database, even if the * file itself doesn't exist or doesn't have the right permissions. * @return array Returns array with indexes:<br/> * [0]: true/false (success / failure)<br/> * [1]: message string<br/> */ function ft_file_delete_file_submission($form_id, $submission_id, $field_id, $force_delete = false) { global $g_table_prefix, $LANG; // get the column name and upload folder for this field $field_info = ft_get_form_field($field_id); $col_name = $field_info["col_name"]; // if the column name wasn't found, the $field_id passed in was invalid. Return false. if (empty($col_name)) { return array(false, $LANG["notify_submission_no_field_id"]); } $field_settings = ft_get_field_settings($field_id); $file_folder = $field_settings["folder_path"]; $query = "\n SELECT {$col_name}\n FROM {$g_table_prefix}form_{$form_id}\n WHERE submission_id = {$submission_id}\n "; $result = mysql_query($query); $file_info = mysql_fetch_row($result); $file = $file_info[0]; $update_database_record = false; $success = true; $message = ""; if (!empty($file)) { if ($force_delete) { @unlink("{$file_folder}/{$file}"); $message = $LANG["notify_file_deleted"]; $update_database_record = true; } else { if (@unlink("{$file_folder}/{$file}")) { $success = true; $message = $LANG["notify_file_deleted"]; $update_database_record = true; } else { if (!is_file("{$file_folder}/{$file}")) { $success = false; $update_database_record = false; $replacements = array("js_link" => "return files_ns.delete_submission_file({$field_id}, true)"); $message = ft_eval_smarty_string($LANG["notify_file_not_deleted_no_exist"] . "({$file_folder}/{$file})", $replacements); } else { if (is_file("{$file_folder}/{$file}") && (!is_readable("{$file_folder}/{$file}") || !is_writable("{$file_folder}/{$file}"))) { $success = false; $update_database_record = false; $replacements = array("js_link" => "return files_ns.delete_submission_file({$field_id}, true)"); $message = ft_eval_smarty_string($LANG["notify_file_not_deleted_permissions"], $replacements); } else { $success = false; $update_database_record = false; $replacements = array("js_link" => "return files_ns.delete_submission_file({$field_id}, true)"); $message = ft_eval_smarty_string($LANG["notify_file_not_deleted_unknown_error"], $replacements); } } } } } // if need be, update the database record to remove the reference to the file in the database. Generally this // should always work, but in case something funky happened, like the permissions on the file were changed to // forbid deleting, I think it's best if the record doesn't get deleted to remind the admin/client it's still // there. if ($update_database_record) { $query = mysql_query("\n UPDATE {$g_table_prefix}form_{$form_id}\n SET {$col_name} = ''\n WHERE submission_id = {$submission_id}\n "); } extract(ft_process_hook_calls("end", compact("form_id", "submission_id", "field_id", "force_delete"), array("success", "message")), EXTR_OVERWRITE); return array($success, $message); }
/** * Deletes an option list from the database. Note: it only deletes lists that don't have any * form fields assigned to them; generally this is prevented from being called unless that condition is * met, but it also checks here just in case. * * @param integer $list_id * @return array [0] T/F<br /> * [1] error/success message */ function ft_delete_option_list($list_id) { global $g_table_prefix, $LANG; // slight behavioural change in 2.1.0. Now you CAN delete Option Lists that are used by one or more fields. // It just clears any references, thus leaving those fields incompletely configured (which isn't the end of // the world!) $fields = ft_get_fields_using_option_list($list_id); foreach ($fields as $field_info) { $field_id = $field_info["field_id"]; $field_type_id = $field_info["field_type_id"]; $settings = ft_get_field_type_settings($field_type_id); $setting_ids = array(); foreach ($settings as $setting_info) { if ($setting_info["field_type"] == "option_list_or_form_field") { $setting_ids[] = $setting_info["setting_id"]; } } if (empty($setting_ids)) { continue; } $setting_id_str = implode(",", $setting_ids); // now we delete any entries in the field_settings table with field_id, setting_id and a NUMERIC value for the // setting_value column. That column is also mysql_query("\n DELETE FROM {$g_table_prefix}field_settings\n WHERE field_id = {$field_id} AND\n setting_id IN ({$setting_id_str}) AND\n setting_value NOT LIKE 'form_field%'\n "); } mysql_query("DELETE FROM {$g_table_prefix}field_options WHERE list_id = {$list_id}"); mysql_query("DELETE FROM {$g_table_prefix}option_lists WHERE list_id = {$list_id}"); mysql_query("DELETE FROM {$g_table_prefix}list_groups WHERE group_type = 'option_list_{$list_id}'"); $success = true; $message = $LANG["notify_option_list_deleted"]; extract(ft_process_hook_calls("end", compact("list_id"), array("success", "message")), EXTR_OVERWRITE); return array(true, $message); }
/** * Updates any number of settings for a particular user account. As with the similar ft_set_settings * function, it creates the record if it doesn't already exist. * * @param integer $account_id * @param array $settings a hash of setting name => setting value. */ function ft_set_account_settings($account_id, $settings) { global $g_table_prefix; extract(ft_process_hook_calls("start", compact("account_id", "settings"), array("settings")), EXTR_OVERWRITE); while (list($setting_name, $setting_value) = each($settings)) { // find out if it already exists $result = mysql_query("\r\n SELECT count(*) as c\r\n FROM {$g_table_prefix}account_settings\r\n WHERE setting_name = '{$setting_name}' AND\r\n account_id = {$account_id}\r\n "); $info = mysql_fetch_assoc($result); if ($info["c"] == 0) { mysql_query("\r\n INSERT INTO {$g_table_prefix}account_settings (account_id, setting_name, setting_value)\r\n VALUES ({$account_id}, '{$setting_name}', '{$setting_value}')\r\n "); } else { mysql_query("\r\n UPDATE {$g_table_prefix}account_settings\r\n SET setting_value = '{$setting_value}'\r\n WHERE setting_name = '{$setting_name}' AND\r\n account_id = {$account_id}\r\n "); } } extract(ft_process_hook_calls("end", compact("account_id", "settings"), array()), EXTR_OVERWRITE); }
/** * Added in 2.1.0. This lets modules add an icon to a "quicklink" icon row on the Submission Listing page. To add it, * they need to define a hook call and return a $quicklinks hash with the following keys: * icon_url * alt_text * * @param $context "admin" or "client" */ function ft_display_submission_listing_quicklinks($context, $page_data) { global $g_root_url; $quicklinks = array(); extract(ft_process_hook_calls("main", compact("context"), array("quicklinks"), array("quicklinks")), EXTR_OVERWRITE); if (empty($quicklinks)) { return ""; } echo "<ul id=\"ft_quicklinks\">"; $num_quicklinks = count($quicklinks); for ($i = 0; $i < $num_quicklinks; $i++) { $classes = array(); if ($i == 0) { $classes[] = "ft_quicklinks_first"; } if ($i == $num_quicklinks - 1) { $classes[] = "ft_quicklinks_last"; } $class = implode(" ", $classes); $quicklink_info = $quicklinks[$i]; $icon_url = isset($quicklink_info["icon_url"]) ? $quicklink_info["icon_url"] : ""; $title_text = isset($quicklink_info["title_text"]) ? $quicklink_info["title_text"] : ""; $onclick = isset($quicklink_info["onclick"]) ? $quicklink_info["onclick"] : ""; $title_text = htmlspecialchars($title_text); if (empty($icon_url)) { continue; } echo "<li class=\"{$class}\" onclick=\"{$onclick}\"><img src=\"{$icon_url}\" title=\"{$title_text}\" /></li>\n"; } echo "</ul>"; }
/** * Added in 2.1.6, to allow for simple "inline" hook overriding from within the PHP pages. * * @param string $location * @param mixed $data */ function ft_module_override_data($location, $data) { extract(ft_process_hook_calls("start", compact("location", "data"), array("data")), EXTR_OVERWRITE); return $data; }