Collections functions
General functions
Node functions
Render functions
Theme permission functions
User functions
Resource functions

save_resource_data()

Description

Save resource data

IMPORTANT: inactive nodes should be left alone (don't add/remove) except when processing fixed list field types that
only hold one value (dropdown, radio). Plugins should determine this based on their use cases when hooking.

Parameters

ColumnTypeDefaultDescription
$ref int
$multi bool
$autosave_field string|int ""

Return

true|array List of errors if unsuccessful, true otherwise

Location

include/resource_functions.php lines 644 to 1369

Definition

 
function save_resource_data($ref,$multi,$autosave_field="")
    {
    
debug_function_call("save_resource_data"func_get_args());
    
# Save all submitted data for resource $ref.
    # Also re-index all keywords from indexable fields.
    
global $lang$languages$language$FIXED_LIST_FIELD_TYPES,
           
$DATE_FIELD_TYPES$reset_date_field$reset_date_upload_template,
           
$edit_contributed_by$new_checksums$upload_review_mode$blank_edit_template$is_template$NODE_FIELDS,
           
$userref$userresourcedefaults;

    
hook("befsaveresourcedata""", array($ref));
    
// Ability to avoid editing conflicts by checking checksums.
    // NOTE: this should NOT apply to upload.
    
$check_edit_checksums true;

    
// Save resource defaults (functionality available for upload only)
    // Call it here so that if users have access to the field and want
    // to override it, they can do so
    
if($ref)
        {
        
set_resource_defaults($ref);

        
$check_edit_checksums false;
        }

    
# Loop through the field data and save (if necessary)
    
$errors=array();
    
$fields=get_resource_field_data($ref,$multi, !hook("customgetresourceperms"));

    
$expiry_field_edited=false;
    
$resource_data=get_resource_data($ref);

    if(
$resource_data["lock_user"] > && $resource_data["lock_user"] != $userref)
        {
        
$errors[] = get_resource_lock_message($resource_data["lock_user"]);
        return 
$errors;
        }

    
# Load the configuration for the selected resource type. Allows for alternative notification addresses, etc.
    
resource_type_config_override($resource_data["resource_type"]);

    
# Set up arrays of node ids to add/remove. We can't remove all nodes as user may not have access
    
$nodes_to_add               = [];
    
$nodes_to_remove            = [];
    
$oldnodenames               = [];
    
$nodes_check_delete         = [];
    
$resource_update_log_sql    = [];
    
$ui_selected_node_values    = [];
    
$all_current_field_nodes    = [];
    
$new_node_values            = [];
    
$updated_resources          = [];

    
$node_not_active = fn(array $node): bool => !node_is_active($node);

    
// All the nodes passed for editing. Some of them were already a value
    // of the fields while others have been added/removed
    
$user_set_values getval('nodes', [], false'is_array');

    
// Initialise array to store new checksums that client needs after autosave, without which subsequent edits will fail
    
$new_checksums = array();
    for (
$n=0;$n<count($fields);$n++)
        {
        if(!(
            
checkperm('F' $fields[$n]['ref'])
            || (
checkperm("F*") && !checkperm('F-' $fields[$n]['ref']))
            
// If we hide on upload the field, there is no need to check values passed from the UI as there shouldn't be any
            
|| (($ref || $upload_review_mode) && $fields[$n]['hide_when_uploading'])
            )
            && (
'' == $autosave_field || $autosave_field == $fields[$n]['ref']
                || (
is_array($autosave_field) && in_array($fields[$n]['ref'], $autosave_field))
            )
        )
            {
            
// Fixed list  fields use node IDs directly
            
if(in_array($fields[$n]['type'], $FIXED_LIST_FIELD_TYPES))
                {
                
debug("save_resource_data(): Checking nodes to add/ remove for field {$fields[$n]['ref']} - {$fields[$n]['title']}");

                
// Get currently selected nodes for this field
                
$current_field_nodes get_resource_nodes($ref$fields[$n]['ref']);
                
$all_current_field_nodes array_merge($all_current_field_nodes,$current_field_nodes);
                
// Check if resource field data has been changed between form being loaded and submitted
                
$post_cs getval("field_" $fields[$n]['ref'] . "_checksum","");
                
sort($current_field_nodes);
                
$current_cs md5(implode(",",$current_field_nodes));
                if(
$check_edit_checksums && $post_cs != "" && $post_cs != $current_cs)
                    {
                    
$errors[$fields[$n]["ref"]] = i18n_get_translated($fields[$n]['title']) . ': ' $lang["save-conflict-error"];
                    continue;
                    }

                
debug("save_resource_data(): Current nodes for resource " $ref ": " implode(",",$current_field_nodes));

                
// Work out nodes submitted by user
                
if(isset($user_set_values[$fields[$n]['ref']])
                    && !
is_array($user_set_values[$fields[$n]['ref']])
                    && 
'' != $user_set_values[$fields[$n]['ref']]
                    && 
is_numeric($user_set_values[$fields[$n]['ref']]))
                    {
                    
$ui_selected_node_values[] = $user_set_values[$fields[$n]['ref']];
                    }
                elseif(isset(
$user_set_values[$fields[$n]['ref']])
                    && 
is_array($user_set_values[$fields[$n]['ref']]))
                    {
                    
$ui_selected_node_values $user_set_values[$fields[$n]['ref']];
                    }
                
// Check nodes are valid for this field
                
if(FIELD_TYPE_CATEGORY_TREE === $fields[$n]['type'])
                    {
                    
$all_tree_nodes_ordered get_cattree_nodes_ordered($fields[$n]['ref'], nulltrue);
                    
// remove the fake "root" node which get_cattree_nodes_ordered() is adding since we won't be using get_cattree_node_strings()
                    
array_shift($all_tree_nodes_ordered);
                    
$inactive_nodes array_column(array_filter($all_tree_nodes_ordered$node_not_active), 'ref');
                    
$all_tree_nodes_ordered array_values($all_tree_nodes_ordered);

                    
$node_options array_column($all_tree_nodes_ordered'name''ref');
                    
$validnodes array_keys($node_options);
                    }
                else
                    {
                    
$fieldnodes   get_nodes($fields[$n]['ref'], ''false);
                    
$node_options array_column($fieldnodes'name''ref');
                    
$validnodes   array_column($fieldnodes'ref');
                    
$inactive_nodes array_column(array_filter($fieldnodes$node_not_active), 'ref');
                    }

                
// $validnodes are already sorted by the order_by (default for get_nodes). This is needed for the data_joins fields later
                
$ui_selected_node_values array_values(array_intersect($validnodes$ui_selected_node_values));
                
debug("save_resource_data(): UI selected nodes for resource {$ref}: " implode(','$ui_selected_node_values));

                
// Set new value for logging
                
$new_node_values array_merge($new_node_values$ui_selected_node_values);

                
$added_nodes array_diff($ui_selected_node_values$current_field_nodes$inactive_nodes);
                
debug("save_resource_data(): Adding nodes to resource " $ref ": " implode(",",$added_nodes));
                
$nodes_to_add array_merge($nodes_to_add$added_nodes);

                if (
                    
// We must release an inactive node if the type can only hold one value...
                    
in_array($fields[$n]['type'], [FIELD_TYPE_DROP_DOWN_LISTFIELD_TYPE_RADIO_BUTTONS])
                    
// ...but prevent direct removals (ie. no value)
                    
&& $ui_selected_node_values !== []
                ) {
                    
$removed_nodes array_diff($current_field_nodes$ui_selected_node_values);
                    
$current_inactive_resource_field_nodes = [];
                } else {
                    
$removed_nodes array_diff($current_field_nodes$ui_selected_node_values$inactive_nodes);
                    
$current_inactive_resource_field_nodes array_intersect($current_field_nodes$inactive_nodes);
                }
                
debug("save_resource_data(): Removed nodes from resource " $ref ": " implode(",",$removed_nodes));
                
$nodes_to_remove array_merge($nodes_to_remove$removed_nodes);

                if(
count($added_nodes) > || count($removed_nodes) > 0)
                    {
                    
$new_nodevals = array();
                    
// Build new value
                    
foreach($ui_selected_node_values as $ui_selected_node_value)
                        {
                        if(
FIELD_TYPE_CATEGORY_TREE === $fields[$n]['type'])
                            {
                            
$new_nodevals[] = implode(
                                
'/',
                                
array_column(
                                    
compute_node_branch_path($all_tree_nodes_ordered$ui_selected_node_value),
                                    
'name'
                                
)
                            );
                            continue;
                            }

                        
$new_nodevals[] = $node_options[$ui_selected_node_value];
                        }
                    
# Is this is a 'joined' field?
                    
$joins=get_resource_table_joins();
                    if (
in_array($fields[$n]["ref"],$joins))
                        {
                        
$new_nodes_val implode($GLOBALS['field_column_string_separator'], $new_nodevals);
                        if ((
== $fields[$n]['required'] && "" != $new_nodes_val) || == $fields[$n]['required']) # If joined field is required we shouldn't be able to clear it.
                            
{
                            
update_resource_field_column($ref,$fields[$n]["ref"],$new_nodes_val);
                            }
                        }
                    
$val implode(",",$new_nodevals);
                    
$ui_selected_node_values array_merge($ui_selected_node_values$current_inactive_resource_field_nodes);
                    
sort($ui_selected_node_values);
                    
$new_checksums[$fields[$n]['ref']] = md5(implode(',',$ui_selected_node_values));
                    
$updated_resources[$ref][$fields[$n]['ref']] = $new_nodevals// To pass to hook
                    
}
                else
                    {
                    
$val "";
                    }
                } 
// End of if in $FIXED_LIST_FIELD_TYPES
            
else
                {
                if(
$fields[$n]['type']==FIELD_TYPE_DATE_RANGE)
                    {
                    
# date range type
                    # each value will be a node so we end up with a pair of nodes to represent the start and end dates

                    
$daterangenodes=array();
                    
$newval="";

                    if((
$date_edtf=getval("field_" $fields[$n]["ref"] . "_edtf",""))!=="")
                        {
                        
// We have been passed the range in EDTF format, check it is in the correct format
                        
$rangeregex="/^(\d{4})(-\d{2})?(-\d{2})?\/(\d{4})(-\d{2})?(-\d{2})?/";
                        if(!
preg_match($rangeregex,$date_edtf,$matches))
                            {
                            
$errors[$fields[$n]["ref"]] = $lang["information-regexp_fail"] . " : " $date_edtf;
                            continue;
                            }
                        if(
is_int_loose($fields[$n]["linked_data_field"]))
                            {
                            
// Update the linked field with the raw EDTF string submitted
                            
update_field($ref,$fields[$n]["linked_data_field"],$date_edtf);
                            }
                        
$rangedates explode("/",$date_edtf);
                        
$rangestart=str_pad($rangedates[0],  10"-00");
                        
$rangeendparts=explode("-",$rangedates[1]);
                        
$rangeendyear=$rangeendparts[0];
                        
$rangeendmonth=isset($rangeendparts[1])?$rangeendparts[1]:12;
                        
$rangeendday=isset($rangeendparts[2])?$rangeendparts[2]:cal_days_in_month(CAL_GREGORIAN$rangeendmonth$rangeendyear);
                        
$rangeend=$rangeendyear "-" $rangeendmonth "-" $rangeendday;

                        
$newval $rangestart DATE_RANGE_SEPARATOR $rangeend;
                        
$daterangenodes[]=set_node(null$fields[$n]["ref"], $rangestartnullnull);
                        
$daterangenodes[]=set_node(null$fields[$n]["ref"], $rangeendnullnull);
                        }
                    else
                        {
                        
// Range has been passed via normal inputs, construct the value from the date/time dropdowns
                        
$date_parts=array("_start_","_end_");

                        foreach(
$date_parts as $date_part)
                            {
                            
$val getval("field_" $fields[$n]["ref"] . $date_part "year","");
                            if (
intval($val)<=0)
                                {
                                
$val="";
                                }
                            elseif ((
$field=getval("field_" $fields[$n]["ref"] . $date_part "month",""))!="")
                                {
                                
$val.="-" $field;
                                if ((
$field=getval("field_" $fields[$n]["ref"] . $date_part "day",""))!="")
                                    {
                                    
$val.="-" $field;
                                    }
                                    else
                                    {
                                    
$val.="-00";
                                    }
                                }
                            else
                                {
                                
$val.="-00-00";
                                }

                            
$newval.= ($newval != "" DATE_RANGE_SEPARATOR "") . $val;
                            if(
$val!=="")
                                {
                                
$daterangenodes[]=set_node(null$fields[$n]["ref"], $valnullnull);
                                }
                            }
                        }
                    
natsort($daterangenodes);

                    
// Set new value for logging
                    
$new_node_values array_merge($new_node_values,$daterangenodes);

                    
// Get currently selected nodes for this field
                    
$current_field_nodes get_resource_nodes($ref$fields[$n]['ref'], falseSORT_ASC);
                    
$all_current_field_nodes array_merge($all_current_field_nodes,$current_field_nodes);

                        
// Check if resource field data has been changed between form being loaded and submitted
                        
$post_cs getval("field_" $fields[$n]['ref'] . "_checksum","");
                        
sort($current_field_nodes);
                        
$current_cs md5(implode(",",$current_field_nodes));
                        if(
$check_edit_checksums && $post_cs != "" && $post_cs != $current_cs)
                            {
                            
$errors[$fields[$n]["ref"]] = i18n_get_translated($fields[$n]['title']) . ': ' $lang["save-conflict-error"];
                            continue;
                            }

                    if(
$daterangenodes !== $current_field_nodes)
                        {
                        
$added_nodes array_diff($daterangenodes$current_field_nodes);
                        
debug("save_resource_data(): Adding nodes to resource " $ref ": " implode(",",$added_nodes));
                        
$nodes_to_add array_merge($nodes_to_add$added_nodes);
                        
$removed_nodes array_diff($current_field_nodes,$daterangenodes);
                        
debug("save_resource_data(): Removed nodes from resource " $ref ": " implode(",",$removed_nodes));
                        
$nodes_to_remove array_merge($nodes_to_remove$removed_nodes);

                        
$val $newval;
                        
# If this is a 'joined' field it still needs to be added to the resource column
                        
$joins=get_resource_table_joins();
                        if (
in_array($fields[$n]["ref"],$joins))
                            {
                            
update_resource_field_column($ref,$fields[$n]["ref"],$newval);
                            }
                        
sort($daterangenodes);
                        
$new_checksums[$fields[$n]['ref']] = md5(implode(",",$daterangenodes));
                        
$updated_resources[$ref][$fields[$n]['ref']][] = $newval// To pass to hook
                        
}
                    }
                elseif(
in_array($fields[$n]['type'], $DATE_FIELD_TYPES))
                    {
                    
# date type, construct the value from the date/time dropdowns to be used in DB
                    
$val=sanitize_date_field_input($fields[$n]["ref"], false);

                    
// A proper input:date field
                    
if ($GLOBALS['use_native_input_for_date_field'] && $fields[$n]['type'] === FIELD_TYPE_DATE)
                        {
                        
$val getval("field_{$fields[$n]['ref']}"'');
                        if(
$val !== '' && !validateDatetime($val'Y-m-d'))
                            {
                            
$errors[$fields[$n]['ref']] = $lang['error_invalid_date'] . ' : ' $val;
                            continue;
                            }
                        }

                    
// Upload template: always reset to today's date, if configured and field is hidden
                    
if($ref
                        
&& $reset_date_upload_template
                        
&& $reset_date_field == $fields[$n]['ref']
                        && 
$fields[$n]['hide_when_uploading']
                    )
                        {
                        
$val date('Y-m-d H:i');
                        }

                    
// Check if resource field data has been changed between form being loaded and submitted
                    
$post_cs getval("field_" $fields[$n]['ref'] . "_checksum","");
                    
$current_cs md5((string)$fields[$n]['value']);
                    if(
$check_edit_checksums && $post_cs != "" && $post_cs != $current_cs)
                        {
                        
$errors[$fields[$n]["ref"]] = i18n_get_translated($fields[$n]['title']) . ': ' $lang["save-conflict-error"];
                        continue;
                        }

                    
$new_checksums[$fields[$n]['ref']] = md5($val);
                    
$updated_resources[$ref][$fields[$n]['ref']][] = $val// To pass to hook
                    
}
                else
                    {
                    
# Set the value exactly as sent.
                    
$val=getval("field_" $fields[$n]["ref"],"");
                    
$rawval getval("field_" $fields[$n]["ref"],"");
                    
# Check if resource field data has been changed between form being loaded and submitted
                    # post_cs is the checksum of the data when it was loaded from the database
                    # current_cs is the checksum of the data on the database now
                    # if they are the same then there has been no intervening update and so its ok to update with our new value
                    # if our new data yields a different checksum, then we know the new value represents a change
                    # the new checksum for the new value of a field is stored in $new_checksums[$fields[$n]['ref']]
                    
$post_cs getval("field_" $fields[$n]['ref'] . "_checksum","");
                    
$current_cs md5(trim(preg_replace('/\s\s+/'' ', (string) $fields[$n]['value'])));

                    if(
$check_edit_checksums && $post_cs != "" && $post_cs != $current_cs)
                        {
                        
$errors[$fields[$n]["ref"]] = i18n_get_translated($fields[$n]['title']) . ': ' $lang["save-conflict-error"];
                        continue;
                        }
                    
$new_checksums[$fields[$n]['ref']] = md5(trim(preg_replace('/\s\s+/'' '$rawval)));
                    
$updated_resources[$ref][$fields[$n]['ref']][] = $val// To pass to hook
                    
}

                
# Check for regular expression match
                
if (strlen(trim((string)$fields[$n]["regexp_filter"]))>=&& strlen((string) $val)>0)
                    {
                    global 
$regexp_slash_replace;
                    if(
preg_match("#^" str_replace($regexp_slash_replace'\\',$fields[$n]["regexp_filter"]) . "$#",$val,$matches)<=0)
                        {
                        global 
$lang;
                        
debug($lang["information-regexp_fail"] . ": -" "reg exp: " str_replace($regexp_slash_replace'\\',$fields[$n]["regexp_filter"]) . ". Value passed: " $val);
                        
$errors[$fields[$n]["ref"]]=$lang["information-regexp_fail"] . " : " $val;
                        continue;
                        }
                    }
                
$modified_val=hook("modifiedsavedfieldvalue",'',array($fields,$n,$val));
                if(!empty(
$modified_val))
                    {
                    
$val=$modified_val;
                    
$new_checksums[$fields[$n]['ref']] = md5(trim(preg_replace('/\s\s+/'' '$val)));
                    }

                
$error hook("additionalvalcheck""all", array($fields$fields[$n]));
                if(
$error)
                    {
                    
$errors[$fields[$n]["ref"]]=$error;
                    continue;
                    }
                } 
// End of if not a fixed list field

            // Determine whether a required field has a default for the user
            
$field_has_default_for_user=false;
            if(
$userresourcedefaults != '')
                {
                foreach(
explode(';'$userresourcedefaults) as $rule)
                    {
                    
$rule_detail         explode('='trim($rule));
                    
$field_shortname     $rule_detail[0];
                    
$field_default_value $rule_detail[1];
                    if(
$field_shortname  == $fields[$n]['name'] && $field_default_value !=""
                        {
                        
$field_has_default_for_user=true;
                        break;
                        }
                    }
                }

            
// Populate empty field with the default if necessary
            
if($field_has_default_for_user && strlen((string) $val)==0) {
                
$val=$field_default_value;
                
$new_checksums[$fields[$n]['ref']] = md5(trim(preg_replace('/\s\s+/'' '$val)));
            }

            if( 
$fields[$n]['required'] == 1
                
&& check_display_condition($n$fields[$n], $fieldsfalse$ref)
                && (
                    
// Required node field with no nodes submitted is a candidate for error
                    
(in_array($fields[$n]['type'], $FIXED_LIST_FIELD_TYPES) && count($ui_selected_node_values) == 0)
                    
// Required continuous field with no value is a candidate for error
                    
|| (!in_array($fields[$n]['type'], $FIXED_LIST_FIELD_TYPES) && trim(strip_leading_comma($val)) == '')
                )
                && (
                    
// An existing resource node field with neither any nodes submitted nor a resource default
                    
($ref && in_array($fields[$n]['type'], $FIXED_LIST_FIELD_TYPES) && count($ui_selected_node_values) == && !$field_has_default_for_user)
                    
// An existing resource continuous field with neither an input value nor a resource default
                    
|| ($ref && !in_array($fields[$n]['type'], $FIXED_LIST_FIELD_TYPES) && strlen((string) $val)==&& !$field_has_default_for_user)
                    
// A template node field with neither any nodes submitted nor a resource default
                    
|| ($ref && in_array($fields[$n]['type'], $FIXED_LIST_FIELD_TYPES) && count($ui_selected_node_values) == && !$field_has_default_for_user)
                    
// A template continuous field with neither an input value nor a resource default
                    
|| ($ref && !in_array($fields[$n]['type'], $FIXED_LIST_FIELD_TYPES) && strlen((string) $val)==&& !$field_has_default_for_user)
                )
                
// Not a metadata template
                
&& !$is_template
            
)
                {
                
$field_visibility_status=getval("field_".$fields[$n]['ref']."_displayed","");
                
# Register an error only if the empty required field was actually displayed
                
if (is_field_displayed($fields[$n]) && $field_visibility_status=="block")
                    {
                    
$errors[$fields[$n]['ref']] = i18n_get_translated($fields[$n]['title']) . ": {$lang['requiredfield']}";
                    continue;
                    }
                }

            
// If all good so far, then save the data
            
if(
                
in_array($fields[$n]['type'], NON_FIXED_LIST_SINGULAR_RESOURCE_VALUE_FIELD_TYPES)
                && 
str_replace("\r\n""\n"trim((string) $fields[$n]['value'])) !== str_replace("\r\n""\n"trim((string) $val))
            )
                {
                
# This value is different from the value we have on record.
                # Expiry field? Set that expiry date(s) have changed so the expiry notification flag will be reset later in this function.
                
if ($fields[$n]["type"]==FIELD_TYPE_EXPIRY_DATE)
                    {
                    
$expiry_field_edited=true;
                    }

                
$use_node null;
                if(
trim((string) $fields[$n]["nodes"]) != "")
                    {
                    
// Remove any existing node IDs for this non-fixed list field (there should only be one) unless used by other resources.
                    
$current_field_nodes array_filter(explode(",",$fields[$n]["nodes"]),"is_int_loose");
                    
$all_current_field_nodes array_merge($all_current_field_nodes,$current_field_nodes);
                    foreach(
$current_field_nodes as $current_field_node)
                        {
                        
$inuse get_nodes_use_count([$current_field_node]);
                        
$inusecount $inuse[$current_field_node] ?? 0;
                        if (
$current_field_node && $inusecount == 1)
                            {
                            
// Reuse same node
                            
$use_node $current_field_node;
                            }
                        else
                            {
                            
// Remove node from resource and create new node
                            
$nodes_to_remove[] = $current_field_node;
                            
$nodes_check_delete[] = $current_field_node;
                            }
                        }
                    }

                
# Add new node unless empty string
                
if($val == '')
                    {
                    
// Remove and delete node
                    
$nodes_to_remove[] = $current_field_node;
                    
$nodes_check_delete[] = $current_field_node;
                    }
                else
                    {
                    
// Update the existing node
                    
$newnode set_node($use_node$fields[$n]["ref"], $valnullnull);
                    if((int)
$newnode != (int)$use_node)
                        {
                        
// Node already exists, remove current node and replace
                        
$nodes_to_add[] = $newnode;
                        
$nodes_to_remove[] = $use_node;
                        
$nodes_check_delete[]=$use_node;
                        
// Set new value for logging
                        
$new_node_values[] = $newnode;
                        }
                    else
                        {
                        
$new_node_values[] = $use_node;
                        }

                    
// Add to array for logging
                    
if (!is_null($use_node))
                        {
                        
$oldnodenames[$use_node] = $fields[$n]['value'];
                        }
                    }

                
# If this is a 'joined' field we need to add it to the resource column
                
$joins=get_resource_table_joins();
                if (
in_array($fields[$n]["ref"],$joins))
                    {
                    
update_resource_field_column($ref,$fields[$n]["ref"],$val);
                    }

                }

            
# Add any onchange code if new checksum for field shows that it has changed
            
if(isset($fields[$n]["onchange_macro"]) && $fields[$n]["onchange_macro"]!=="" 
                    
&& $post_cs !==""
                    
&& isset($new_checksums[$fields[$n]["ref"]]) 
                    && 
$post_cs !== $new_checksums[$fields[$n]["ref"]])
                {
                
$macro_resource_id=$ref;
                eval(
eval_check_signed($fields[$n]["onchange_macro"]));
                }

            } 
# End of if "allowed to edit field conditions"        
        
# End of for $fields

    // When editing a resource, prevent applying the change to the resource if there are any errors
    
if(count($errors) > && $ref 0)
        {
        return 
$errors;
        }

   
# Save related resource field if value for Related input field is autosaved, or if form has been submitted by user
    
if (($autosave_field=="" || $autosave_field=="Related") && isset($_POST["related"]))
        {
        
# save related resources field
        
$related=explode(",",getval("related",""));
        
# Trim whitespace from each entry
        
foreach ($related as &$relatedentry)
            {
            
$relatedentry trim($relatedentry);
            }
        
# Make sure all submitted values are numeric
        
$to_relate array_filter($related,"is_int_loose");

        
$currently_related get_related_resources($ref);
        
$to_add array_diff($to_relate$currently_related);
        
$to_delete array_diff($currently_related$to_relate);

        if(
count($to_add) > 0)
            {
            
update_related_resource($ref$to_addtrue);
            }
        if(
count($to_delete) > 0)
            {
            
update_related_resource($ref$to_deletefalse);
            }
        }

    
// Update resource_node table
    
db_begin_transaction("update_resource_node");
    if(
count($nodes_to_remove)>0)
        {
        
delete_resource_nodes($ref,$nodes_to_removefalse);
        }

    if(
count($nodes_to_add)>0)
        {
        
add_resource_nodes($ref,$nodes_to_addfalsefalse);
        }
    
log_node_changes($ref,$new_node_values$all_current_field_nodes,"",$oldnodenames);

    if(
count($nodes_check_delete)>0)
        {
        
// This has to be after call to log_node_changes() or nodes cannot be resolved
        
check_delete_nodes($nodes_check_delete);
        }

    
db_end_transaction("update_resource_node");

    
// Autocomplete any blank fields without overwriting any existing metadata
    
$autocomplete_fields autocomplete_blank_fields($reffalsetrue);
    foreach(
$autocomplete_fields as $autocomplete_field_ref => $autocomplete_field_value)
        {
        
$new_checksums[$autocomplete_field_ref] = md5((string)$autocomplete_field_value);
        }

    
// Initialise an array of updates for the resource table
    
$resource_update_sql = array();
    
$resource_update_params = array();
    if(
$edit_contributed_by)
        {
        
$created_by $resource_data['created_by'];
        
$new_created_by getval("created_by",0,true);
        if((
getval("created_by",0,true) > 0) && $new_created_by != $created_by)
            {
            
# Also update created_by
            
$resource_update_sql[] = "created_by= ?";
            
$resource_update_params[]="i";$resource_update_params[]=$new_created_by;
            
$olduser=get_user($created_by);
            
$newuser=get_user($new_created_by);
            
$resource_update_log_sql[] = array(
                    
"ref"=>$ref,
                    
"type"=>LOG_CODE_CREATED_BY_CHANGED,
                    
"field"=>0,
                    
"notes"=>"",
                    
"from"=>$created_by " (" . ($olduser["fullname"]=="" $olduser["username"] : $olduser["fullname"])  . ")","to"=>$new_created_by " (" . ($newuser["fullname"]=="" $newuser["username"] : $newuser["fullname"])  . ")");
            }
        }

    
# Expiry field(s) edited? Reset the notification flag so that warnings are sent again when the date is reached.
    
if ($expiry_field_edited)
        {
        
$resource_update_sql[] = "expiry_notification_sent='0'";
        }

    if (!
hook('forbidsavearchive''', array($errors)))
        {
        
# Also update archive status and access level
        
$oldaccess=$resource_data['access'];
        
$access=getval("access",$oldaccess,true);

        
$oldarchive=$resource_data['archive'];
        
$setarchivestate=getval("status",$oldarchive,true);
        if(
$setarchivestate!=$oldarchive && !checkperm("e" $setarchivestate)) // don't allow change if user has no permission to change archive state
            
{
            
$setarchivestate=$oldarchive;
            }

        
// Only if changed
        
if(($autosave_field=="" || $autosave_field=="Status") && $setarchivestate != $oldarchive)
            {
            
// Check if resource status has already been changed between form being loaded and submitted
            
if(getval("status_checksum","") != "" && getval("status_checksum","") != $oldarchive)
                {
                
$errors["status"] = $lang["status"] . ': ' $lang["save-conflict-error"];
                }
            else
                {
                
// update archive status if different (doesn't matter whether it is a user template or a genuine resource)
                
if($setarchivestate != $oldarchive)
                    {
                    
update_archive_status($ref,$setarchivestate,array($oldarchive));
                    }

                
$new_checksums["status"] = $setarchivestate;
                }
            }

        if((
$autosave_field=="" || $autosave_field=="Access") && $access != $oldaccess)
            {
            
// Check if resource access has already been changed between form being loaded and submitted
            
if(getval("access_checksum","") != "" && getval("access_checksum","") != $oldaccess)
                {
                
$errors["access"] = $lang["access"] . ': ' $lang["save-conflict-error"];
                }
            else
                {
                
$resource_update_sql[] = "access= ?";
                
$resource_update_params[]="i";$resource_update_params[]=$access;
                if(
$access != $oldaccess && $ref)
                    {
                    
$resource_update_log_sql[] = array(
                        
'ref'   => $ref,
                        
'type'  => 'a',
                        
'field' => 0,
                        
'notes' => '',
                        
'from'  => $oldaccess,
                        
'to'    => $access);
                    }

                if (
$oldaccess==&& $access!=3)
                    {
                    
# Moving out of the custom state. Delete any usergroup specific access.
                    # This can delete any 'manual' usergroup grants also as the user will have seen this as part of the custom access.
                    
delete_resource_custom_access_usergroups($ref);
                    }
                
$new_checksums["access"] = $access;
                }
            }
        }

    if(
count($resource_update_sql)>0)
        {
        
$sql "UPDATE resource SET " implode(",",$resource_update_sql) . " WHERE ref=?";
        
$sqlparams array_merge($resource_update_params,["i",$ref]);
        
ps_query($sql,$sqlparams);
        }

    foreach(
$resource_update_log_sql as $log_sql)
        {
        
resource_log($log_sql["ref"],$log_sql["type"],$log_sql["field"],$log_sql["notes"],$log_sql["from"],$log_sql["to"]);
        }

    
# Save any custom permissions
    
if (getval("access",0)==RESOURCE_ACCESS_CUSTOM_GROUP)
        {
        
save_resource_custom_access($ref);
        }

    
// Plugins can do extra actions once all fields have been saved and return errors back if needed
    
$plg_errors hook('aftersaveresourcedata''', array($ref$nodes_to_add$nodes_to_remove$autosave_field$fields,$updated_resources));
    if(
is_array($plg_errors) && !empty($plg_errors))
        {
        
$errors array_merge($errors$plg_errors);
        }

    if (
count($errors)==0)
        {
        
daily_stat("Resource edit"$ref);
        return 
true;
        }
    return 
$errors;
    }

This article was last updated 19th April 2024 00:05 Europe/London time based on the source file dated 18th April 2024 16:10 Europe/London time.