/** * Implements a generic insert and update logic for any SugarBean * This method only works for subclasses that implement the same variable names. * This method uses the presence of an id field that is not null to signify and update. * The id field should not be set otherwise. * * @param boolean $check_notify Optional, default false, if set to true assignee of the record is notified via email. * @todo Add support for field type validation and encoding of parameters. */ function save($check_notify = FALSE) { // cn: SECURITY - strip XSS potential vectors $this->cleanBean(); // This is used so custom/3rd-party code can be upgraded with fewer issues, this will be removed in a future release $this->fixUpFormatting(); global $timedate; global $current_user, $action; $isUpdate = true; if (empty($this->id)) { $isUpdate = false; } if ($this->new_with_id == true) { $isUpdate = false; } if (empty($this->date_modified) || $this->update_date_modified) { $this->date_modified = $GLOBALS['timedate']->nowDb(); } $this->_checkOptimisticLocking($action, $isUpdate); if (!empty($this->modified_by_name)) { $this->old_modified_by_name = $this->modified_by_name; } if ($this->update_modified_by) { $this->modified_user_id = 1; if (!empty($current_user)) { $this->modified_user_id = $current_user->id; $this->modified_by_name = $current_user->user_name; } } if ($this->deleted != 1) { $this->deleted = 0; } if ($isUpdate) { $query = "Update "; } else { if (empty($this->date_entered)) { $this->date_entered = $this->date_modified; } if ($this->set_created_by == true) { // created by should always be this user $this->created_by = isset($current_user) ? $current_user->id : ""; } if ($this->new_with_id == false) { $this->id = create_guid(); } $query = "INSERT into "; } if ($isUpdate && !$this->update_date_entered) { unset($this->date_entered); } // call the custom business logic $custom_logic_arguments['check_notify'] = $check_notify; $this->call_custom_logic("before_save", $custom_logic_arguments); unset($custom_logic_arguments); if (isset($this->custom_fields)) { $this->custom_fields->bean = $this; $this->custom_fields->save($isUpdate); } // use the db independent query generator $this->preprocess_fields_on_save(); //construct the SQL to create the audit record if auditing is enabled. $dataChanges = array(); if ($this->is_AuditEnabled()) { if ($isUpdate && !isset($this->fetched_row)) { $GLOBALS['log']->debug('Auditing: Retrieve was not called, audit record will not be created.'); } else { $dataChanges = $this->dbManager->helper->getDataChanges($this); } } $this->_sendNotifications($check_notify); if ($this->db->dbType == "oci8") { } if ($this->db->dbType == 'mysql') { // write out the SQL statement. $query .= $this->table_name . " set "; $firstPass = 0; foreach ($this->field_defs as $field => $value) { if (!isset($value['source']) || $value['source'] == 'db') { // Do not write out the id field on the update statement. // We are not allowed to change ids. if ($isUpdate && 'id' == $field) { continue; } //custom fields handle there save seperatley if (isset($this->field_name_map) && !empty($this->field_name_map[$field]['custom_type'])) { continue; } // Only assign variables that have been set. if (isset($this->{$field})) { //bug: 37908 - this is to handle the issue where the bool value is false, but strlen(false) <= so it will //set the default value. TODO change this code to esend all fields through getFieldValue() like DbHelper->insertSql if (!empty($value['type']) && $value['type'] == 'bool') { $this->{$field} = $this->getFieldValue($field); } if (strlen($this->{$field}) <= 0) { if (!$isUpdate && isset($value['default']) && strlen($value['default']) > 0) { $this->{$field} = $value['default']; } else { $this->{$field} = null; } } // Try comparing this element with the head element. if (0 == $firstPass) { $firstPass = 1; } else { $query .= ", "; } if (is_null($this->{$field})) { $query .= $field . "=null"; } else { //added check for ints because sql-server does not like casting varchar with a decimal value //into an int. if (isset($value['type']) and $value['type'] == 'int') { $query .= $field . "=" . $this->db->quote($this->{$field}); } elseif (isset($value['len'])) { $query .= $field . "='" . $this->db->quote($this->db->truncate(from_html($this->{$field}), $value['len'])) . "'"; } else { $query .= $field . "='" . $this->db->quote($this->{$field}) . "'"; } } } } } if ($isUpdate) { $query = $query . " WHERE ID = '{$this->id}'"; $GLOBALS['log']->info("Update {$this->object_name}: " . $query); } else { $GLOBALS['log']->info("Insert: " . $query); } $GLOBALS['log']->info("Save: {$query}"); $this->db->query($query, true); } //process if type is set to mssql if ($this->db->dbType == 'mssql') { if ($isUpdate) { // build out the SQL UPDATE statement. $query = "UPDATE " . $this->table_name . " SET "; $firstPass = 0; foreach ($this->field_defs as $field => $value) { if (!isset($value['source']) || $value['source'] == 'db') { // Do not write out the id field on the update statement. // We are not allowed to change ids. if ($isUpdate && 'id' == $field) { continue; } // If the field is an auto_increment field, then we shouldn't be setting it. This was added // specially for Bugs and Cases which have a number associated with them. if ($isUpdate && isset($this->field_name_map[$field]['auto_increment']) && $this->field_name_map[$field]['auto_increment'] == true) { continue; } //custom fields handle their save seperatley if (isset($this->field_name_map) && !empty($this->field_name_map[$field]['custom_type'])) { continue; } // Only assign variables that have been set. if (isset($this->{$field})) { //bug: 37908 - this is to handle the issue where the bool value is false, but strlen(false) <= so it will //set the default value. TODO change this code to esend all fields through getFieldValue() like DbHelper->insertSql if (!empty($value['type']) && $value['type'] == 'bool') { $this->{$field} = $this->getFieldValue($field); } if (strlen($this->{$field}) <= 0) { if (!$isUpdate && isset($value['default']) && strlen($value['default']) > 0) { $this->{$field} = $value['default']; } else { $this->{$field} = null; } } // Try comparing this element with the head element. if (0 == $firstPass) { $firstPass = 1; } else { $query .= ", "; } if (is_null($this->{$field})) { $query .= $field . "=null"; } elseif (isset($value['len'])) { $query .= $field . "='" . $this->db->quote($this->db->truncate(from_html($this->{$field}), $value['len'])) . "'"; } else { $query .= $field . "='" . $this->db->quote($this->{$field}) . "'"; } } } } $query = $query . " WHERE ID = '{$this->id}'"; $GLOBALS['log']->info("Update {$this->object_name}: " . $query); } else { $colums = array(); $values = array(); foreach ($this->field_defs as $field => $value) { if (!isset($value['source']) || $value['source'] == 'db') { // Do not write out the id field on the update statement. // We are not allowed to change ids. //if($isUpdate && ('id' == $field)) continue; //custom fields handle there save seperatley if (isset($this->field_name_map) && !empty($this->field_name_map[$field]['custom_module'])) { continue; } // Only assign variables that have been set. if (isset($this->{$field})) { //trim the value in case empty space is passed in. //this will allow default values set in db to take effect, otherwise //will insert blanks into db $trimmed_field = trim($this->{$field}); //if this value is empty, do not include the field value in statement if ($trimmed_field == '') { continue; } //bug: 37908 - this is to handle the issue where the bool value is false, but strlen(false) <= so it will //set the default value. TODO change this code to esend all fields through getFieldValue() like DbHelper->insertSql if (!empty($value['type']) && $value['type'] == 'bool') { $this->{$field} = $this->getFieldValue($field); } //added check for ints because sql-server does not like casting varchar with a decimal value //into an int. if (isset($value['type']) and $value['type'] == 'int') { $values[] = $this->db->quote($this->{$field}); } elseif (isset($value['len'])) { $values[] = "'" . $this->db->quote($this->db->truncate(from_html($this->{$field}), $value['len'])) . "'"; } else { $values[] = "'" . $this->db->quote($this->{$field}) . "'"; } $columns[] = $field; } } } // build out the SQL INSERT statement. $query = "INSERT INTO {$this->table_name} (" . implode(",", $columns) . " ) VALUES ( " . implode(",", $values) . ')'; $GLOBALS['log']->info("Insert: " . $query); } $GLOBALS['log']->info("Save: {$query}"); $this->db->query($query, true); } if (!empty($dataChanges) && is_array($dataChanges)) { foreach ($dataChanges as $change) { $this->dbManager->helper->save_audit_records($this, $change); } } // let subclasses save related field changes $this->save_relationship_changes($isUpdate); //If we aren't in setup mode and we have a current user and module, then we track if (isset($GLOBALS['current_user']) && isset($this->module_dir)) { $this->track_view($current_user->id, $this->module_dir, 'save'); } $this->call_custom_logic('after_save', ''); return $this->id; }
/** * Implements a generic insert and update logic for any SugarBean * This method only works for subclasses that implement the same variable names. * This method uses the presence of an id field that is not null to signify and update. * The id field should not be set otherwise. * * @param boolean $check_notify Optional, default false, if set to true assignee of the record is notified via email. * @todo Add support for field type validation and encoding of parameters. */ function save($check_notify = FALSE) { // cn: SECURITY - strip XSS potential vectors $this->cleanBean(); $this->unformat_all_fields(); global $timedate; global $current_user, $action; $isUpdate = true; if (empty($this->id)) { $isUpdate = false; } if ($this->new_with_id == true) { $isUpdate = false; } if (empty($this->date_modified) || $this->update_date_modified) { $this->date_modified = gmdate($GLOBALS['timedate']->get_db_date_time_format()); } $this->_checkOptimisticLocking($action, $isUpdate); if (!empty($this->modified_by_name)) { $this->old_modified_by_name = $this->modified_by_name; } if ($this->update_modified_by) { $this->modified_user_id = 1; if (!empty($current_user)) { $this->modified_user_id = $current_user->id; $this->modified_by_name = $current_user->user_name; } } if ($this->deleted != 1) { $this->deleted = 0; } if ($isUpdate) { $query = "Update "; } else { if (empty($this->date_entered)) { $this->date_entered = $this->date_modified; } if ($this->set_created_by == true) { // created by should always be this user $this->created_by = isset($current_user) ? $current_user->id : ""; } if ($this->new_with_id == false) { $this->id = create_guid(); } $query = "INSERT into "; } if ($isUpdate && !$this->update_date_entered) { unset($this->date_entered); } // call the custom business logic $custom_logic_arguments['check_notify'] = $check_notify; //////////////////////// /////nsingh: bug fix 13334. Following code makes a copy of the date with the format found in user preferences. It then converts the date format to //SQL date format. After the custom logic is called, it restores the date to the original date format. $field_name = null; $before_save_date_values = array(); foreach ($this->field_defs as $field_def) { $type = $field_def['type']; $field_name = $field_def['name']; if ($type == 'date' && isset($this->{$field_name}) && !empty($this->{$field_name})) { $format_swapped = false; if (!$timedate->check_matching_format($this->{$field_name}, $GLOBALS['timedate']->dbDayFormat)) { $this->{$field_name} = $timedate->swap_formats($this->{$field_name}, $timedate->get_date_format(), $GLOBALS['timedate']->dbDayFormat); $format_swapped = true; } $before_save_date_values[$field_name] = $format_swapped; } } $this->call_custom_logic("before_save", $custom_logic_arguments); unset($custom_logic_arguments); foreach ($before_save_date_values as $field_name => $format_swapped) { //rrs bug: 23856. Any changes to date fields in logic hooks were being overwritten. //bug 25523. Sometimes, the $this->$field_name may not be in format $GLOBALS['timedate']->dbDayFormat, it may already be changed to $timedate->get_date_format(), so we should check it first. //rrs 23856: this reveals a deeper problem from above where we are trying to convert a date from user format that is already in db_format, so it will //convert to a bad date. if ($format_swapped && $timedate->check_matching_format($this->{$field_name}, $GLOBALS['timedate']->dbDayFormat)) { $this->{$field_name} = $timedate->swap_formats($this->{$field_name}, $GLOBALS['timedate']->dbDayFormat, $timedate->get_date_format()); } } unset($before_save_date_values); ////////////////////////// //end bug fix 13334. // use the db independent query generator $this->preprocess_fields_on_save(); //construct the SQL to create the audit record if auditing is enabled. $dataChanges = array(); if ($this->is_AuditEnabled()) { if ($isUpdate && !isset($this->fetched_row)) { $GLOBALS['log']->debug('Auditing: Retrieve was not called, audit record will not be created.'); } else { $dataChanges = $this->dbManager->helper->getDataChanges($this); } } $this->_sendNotifications($check_notify); if (isset($this->custom_fields)) { $this->custom_fields->bean =& $this; $this->custom_fields->save($isUpdate); } if ($this->db->dbType == "oci8") { } if ($this->db->dbType == 'mysql') { // write out the SQL statement. $query .= $this->table_name . " set "; $firstPass = 0; foreach ($this->field_defs as $field => $value) { if (!isset($value['source']) || $value['source'] == 'db') { // Do not write out the id field on the update statement. // We are not allowed to change ids. if ($isUpdate && 'id' == $field) { continue; } //custom fields handle there save seperatley if (isset($this->field_name_map) && !empty($this->field_name_map[$field]['custom_type'])) { continue; } // Only assign variables that have been set. if (isset($this->{$field})) { if (strlen($this->{$field}) <= 0) { if (!$isUpdate && isset($value['default']) && strlen($value['default']) > 0) { $this->{$field} = $value['default']; } else { $this->{$field} = null; } } // Try comparing this element with the head element. if (0 == $firstPass) { $firstPass = 1; } else { $query .= ", "; } if (is_null($this->{$field})) { $query .= $field . "=null"; } else { //added check for ints because sql-server does not like casting varchar with a decimal value //into an int. if (isset($value['type']) and $value['type'] == 'int') { $query .= $field . "=" . $this->db->quote($this->{$field}); } elseif (isset($value['len'])) { $query .= $field . "='" . $this->db->quote($this->db->truncate(from_html($this->{$field}), $value['len'])) . "'"; } else { $query .= $field . "='" . $this->db->quote($this->{$field}) . "'"; } } } } } if ($isUpdate) { $query = $query . " WHERE ID = '{$this->id}'"; $GLOBALS['log']->info("Update {$this->object_name}: " . $query); } else { $GLOBALS['log']->info("Insert: " . $query); } $GLOBALS['log']->info("Save: {$query}"); $this->db->query($query, true); } //process if type is set to mssql if ($this->db->dbType == 'mssql') { if ($isUpdate) { // build out the SQL UPDATE statement. $query = "UPDATE " . $this->table_name . " SET "; $firstPass = 0; foreach ($this->field_defs as $field => $value) { if (!isset($value['source']) || $value['source'] == 'db') { // Do not write out the id field on the update statement. // We are not allowed to change ids. if ($isUpdate && 'id' == $field) { continue; } // If the field is an auto_increment field, then we shouldn't be setting it. This was added // specially for Bugs and Cases which have a number associated with them. if ($isUpdate && isset($this->field_name_map[$field]['auto_increment']) && $this->field_name_map[$field]['auto_increment'] == true) { continue; } //custom fields handle their save seperatley if (isset($this->field_name_map) && !empty($this->field_name_map[$field]['custom_type'])) { continue; } // Only assign variables that have been set. if (isset($this->{$field})) { if (strlen($this->{$field}) <= 0) { if (!$isUpdate && isset($value['default']) && strlen($value['default']) > 0) { $this->{$field} = $value['default']; } else { $this->{$field} = null; } } // Try comparing this element with the head element. if (0 == $firstPass) { $firstPass = 1; } else { $query .= ", "; } if (is_null($this->{$field})) { $query .= $field . "=null"; } elseif (isset($value['len'])) { $query .= $field . "='" . $this->db->quote($this->db->truncate(from_html($this->{$field}), $value['len'])) . "'"; } else { $query .= $field . "='" . $this->db->quote($this->{$field}) . "'"; } } } } $query = $query . " WHERE ID = '{$this->id}'"; $GLOBALS['log']->info("Update {$this->object_name}: " . $query); } else { $colums = array(); $values = array(); foreach ($this->field_defs as $field => $value) { if (!isset($value['source']) || $value['source'] == 'db') { // Do not write out the id field on the update statement. // We are not allowed to change ids. //if($isUpdate && ('id' == $field)) continue; //custom fields handle there save seperatley if (isset($this->field_name_map) && !empty($this->field_name_map[$field]['custom_module'])) { continue; } // Only assign variables that have been set. if (isset($this->{$field})) { //trim the value in case empty space is passed in. //this will allow default values set in db to take effect, otherwise //will insert blanks into db $trimmed_field = trim($this->{$field}); //if this value is empty, do not include the field value in statement if ($trimmed_field == '') { continue; } //added check for ints because sql-server does not like casting varchar with a decimal value //into an int. if (isset($value['type']) and $value['type'] == 'int') { $values[] = $this->db->quote($this->{$field}); } elseif (isset($value['len'])) { $values[] = "'" . $this->db->quote($this->db->truncate(from_html($this->{$field}), $value['len'])) . "'"; } else { $values[] = "'" . $this->db->quote($this->{$field}) . "'"; } $columns[] = $field; } } } // build out the SQL INSERT statement. $query = "INSERT INTO {$this->table_name} (" . implode(",", $columns) . " ) VALUES ( " . implode(",", $values) . ')'; $GLOBALS['log']->info("Insert: " . $query); } $GLOBALS['log']->info("Save: {$query}"); $this->db->query($query, true); } if (!empty($dataChanges) && is_array($dataChanges)) { foreach ($dataChanges as $change) { $this->dbManager->helper->save_audit_records($this, $change); } } // let subclasses save related field changes $this->save_relationship_changes($isUpdate); //If we aren't in setup mode and we have a current user and module, then we track if (isset($GLOBALS['current_user']) && isset($this->module_dir)) { $this->track_view($current_user->id, $this->module_dir, 'save'); } $this->call_custom_logic('after_save', ''); return $this->id; }