<?PHP
#
#   FILE:  EditResourceComplete.php
#
#   Part of the Collection Workflow Integration System (CWIS)
#   Copyright 2011-2013 Edward Almasy and Internet Scout Project
#   http://scout.wisc.edu/
#

PageTitle("Resource Editing Complete");

# ----- CONFIGURATION  -------------------------------------------------------

# ----- EXPORTED FUNCTIONS ---------------------------------------------------
# (functions intended for use in corresponding HTML file)

# ----- LOCAL FUNCTIONS ------------------------------------------------------
# (functions intended for use only within this file)

# error codes  (NOTE:  Must match codes in SPT--EditResourceCommon.html!!!)
define("ERR_FILEUPLOADERROR", 1);
define("ERR_ZEROLENGTH", 2);
define("ERR_IMAGEUPLOADERROR", 3);
define("ERR_UNSUPPORTEDIMAGEFORMAT", 4);
define("ERR_UNKNOWNIMAGETYPE", 5);
define("ERR_REQUIREDFIELDEMPTY", 6);
define("ERR_POSTEMPTY", 7);
define("ERR_IMAGESTORE", 8);
define("ERR_TEMPSTORE", 9);
define("ERR_FILESTORE", 10);


/**
* Return to editing page with error info.
* @param Resource $Resource Resource being edited.
* @param array $Fields Required fields left empty
* @param int $Error Error code describing the problem.
* @param mixed $ErrOne First error parameter (OPTIONAL).
* @param mixed $ErrTwo Second error parameter (OPTIONAL).
*/
function ReturnWithErrors($Resource, $Fields, $Error, $ErrOne = NULL, $ErrTwo = NULL)
{
    global $AF;

    if (is_array($Fields))
    {
        $FieldName = "Required Fields";
        $FieldInfo = "";
        foreach ($Fields as $Field)
        {
            $FieldInfo .= (strlen($FieldInfo) ? "-" : "").$Field->Id();
        }
    }
    else
    {
        $FieldName = $Fields->Name();
        $FieldInfo = $Fields->Id();
    }

    $AF->SetJumpToPage("EditResource&ID=".$Resource->Id()
                       ."&ER=".$Error."&EF=".$FieldInfo
                       .($ErrOne ? "&E1=".urlencode($ErrOne) : "")
                       .($ErrTwo ? "&E2=".urlencode($ErrTwo) : ""));
}

/**
* save values from editing form
# NOTE: Assumes that all form vars have been set appropriately
# before being called. Single-value fields are checked for existence
# with isset()'s, but because of how HTML forms work, multi-value fields
# cannot be.  If this function is called when coming from a page that
# didn't display a form, values in multi-select fields will be
# squashed.
* @param Resource $Resource Target resource.
* @param User $User User who edited the resource.
* @return array of unfilled rquired fields
*/
function UpdateResource($Resource, $User)
{
    global $AF;

    # for each metadata field
    $Schema = new MetadataSchema($Resource->SchemaId());
    $Fields = $Schema->GetFields(NULL, MetadataSchema::MDFORDER_EDITING);
    $EmptyFields = array();
    $RecordChanged = FALSE;
    foreach ($Fields as $Field)
    {
        # if user has permission to edit field
        if ($Resource->UserCanModifyField($User, $Field))
        {
            $OldValue = $Resource->Get($Field);
            switch ($Field->Type())
            {
                case MetadataSchema::MDFTYPE_TEXT:
                case MetadataSchema::MDFTYPE_NUMBER:
                case MetadataSchema::MDFTYPE_DATE:
                case MetadataSchema::MDFTYPE_TIMESTAMP:
                case MetadataSchema::MDFTYPE_PARAGRAPH:
                case MetadataSchema::MDFTYPE_FLAG:
                case MetadataSchema::MDFTYPE_URL:
                    # if we have a value from the form for this field
                    # (check necessary because user may push Save button
                    #       before form has finished loading)
                    if (isset($_POST["F_".$Field->DBFieldName()]))
                    {
                        # run value through any hooked filters
                        $NewValue = trim($_POST["F_".$Field->DBFieldName()]);
                        $SignalResult = $AF->SignalEvent(
                                "EVENT_POST_FIELD_EDIT_FILTER", array(
                                       "Field" => $Field,
                                       "Resource" => $Resource,
                                       "Value" => $NewValue));
                        $NewValue = $SignalResult["Value"];

                        # update release date if release flag was toggled to true
                        #       or if the release flag was true and the
                        #       Date of Record Release was NULL
                        $NewValue = StripXSSThreats($NewValue);
                        if ($Resource->SchemaId() == MetadataSchema::SCHEMAID_DEFAULT)
                        {
                            if ($Field->Name()=="Release Flag" &&
                                ((!$Resource->Get("Release Flag") && $NewValue == "1") ||
                                 ($NewValue == "1" &&
                                  is_null($Resource->Get("Date Of Record Release")))))
                            {
                                $Resource->Set("Date Of Record Release",
                                        date("Y-m-d H:i:s"));
                            }
                        }

                        # filter date field values for validity
                        if ($Field->Type() & (MetadataSchema::MDFTYPE_DATE
                                |MetadataSchema::MDFTYPE_TIMESTAMP))
                        {
                            $TestDate = new Date($NewValue);
                            if ($TestDate->Precision() == 0) {  $NewValue = "";  }
                        }

                        # filter url values for URI
                        if ($Field->Type() == MetadataSchema::MDFTYPE_URL
                            && strlen($NewValue)
                            && !preg_match('/^[a-zA-Z]+:\/\//', $NewValue))
                        {
                            $NewValue = "http://".$NewValue;
                        }

                        # filter HTML tags out of text values if appropriate
                        if ($Field->Type() & (MetadataSchema::MDFTYPE_TEXT
                                |MetadataSchema::MDFTYPE_PARAGRAPH))
                        {
                            if (!$Field->AllowHTML())
                            {
                                $NewValue = strip_tags($NewValue);
                            }
                        }

                        # if value was supplied or field is not required
                        if (strlen($NewValue) || $Field->Optional())
                        {
                            # save field
                            $Resource->Set($Field, $NewValue);
                        }
                        else
                        {
                            # add field to error list
                            $EmptyFields[] = $Field;
                        }
                    }
                    break;

                case MetadataSchema::MDFTYPE_POINT:
                    # if there are no values set
                    if (!isset($_POST["F_".$Field->DBFieldName()."X"])
                        || !isset($_POST["F_".$Field->DBFieldName()."Y"]))
                    {
                        # if the field isn't optional, add it to the error list
                        if (!$Field->Optional())
                        {
                            $EmptyFields[] = $Field;
                        }

                        # go to the next field
                        continue;
                    }

                    # run value through any hooked filters
                    $NewValue = array(
                            "X" => $_POST["F_".$Field->DBFieldName()."X"],
                            "Y" => $_POST["F_".$Field->DBFIeldName()."Y"]);
                    $SignalResult = $AF->SignalEvent(
                            "EVENT_POST_FIELD_EDIT_FILTER", array(
                                   "Field" => $Field,
                                   "Resource" => $Resource,
                                   "Value" => $NewValue));
                    $NewValue = $SignalResult["Value"];

                    # if value looks valid
                    if (is_numeric($NewValue["X"])
                        && is_numeric($NewValue["Y"]))
                    {
                        # save new value
                        $Resource->Set($Field, $NewValue);
                    }

                    # the field is optional and the values are blank
                    else if ($Field->Optional()
                        && !strlen(trim($NewValue["X"]))
                        && !strlen(trim($NewValue["Y"])))
                    {
                        # save blank value
                        $Resource->Set($Field, array("X" => "", "Y" => ""));
                    }

                    # empty fields and field is required
                    else if (!$Field->Optional())
                    {
                        # flag field as empty
                        $EmptyFields[] = $Field;
                    }
                    break;

                case MetadataSchema::MDFTYPE_TREE:
                case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
                    # while there are form values for this field
                    $ValueCount = $Field->GetCountOfPossibleValues();
                    $InterfaceToggleThreshold = 250;
                    $Factory = $Field->GetFactory();
                    $ValuesToSet = array();
                    $InputValues = array();

                    # old way it was being set
                    $BaseFieldName = "D_".$Field->DBFieldName()."_";
                    $FieldIndex = 1;

                    # set values the old way
                    if (isset($_POST[$BaseFieldName.$FieldIndex]))
                    {
                        while (isset($_POST[$BaseFieldName.$FieldIndex]))
                        {
                            # retrieve value from form field
                            $InputValues[] = $_POST[$BaseFieldName.$FieldIndex];

                            # move to the next form value
                            $FieldIndex++;
                        }
                    }

                    # set values the new way
                    else if (isset($_POST["F_".$Field->DBFieldName()]))
                    {
                        $InputValues = $_POST["F_".$Field->DBFieldName()];
                    }

                    # if we only got a single value, convert it to an array
                    if (!is_array($InputValues))
                    {
                        $InputValues = array($InputValues);
                    }

                    foreach ($InputValues as $Value)
                    {
                        # If we have a non-empty value
                        if (strlen($Value))
                        {
                            # Check to see if it was a name, and if so
                            # convert it to an index.  Otherwise,
                            # it was already an index and we should use it
                            # directly.
                            $Item = $Factory->GetItemByName($Value);
                            if ($Item)
                            {
                                $Value = $Item->Id();
                            }

                            # it looks like it was being wrongly assumed that
                            # this would always be a number, but when there's
                            # an error, it won't find an item and display SQL
                            # errors later on. So, if the value isn't numeric,
                            # refuse to work with it
                            else if (!is_numeric($Value))
                            {
                                $Value = -1;
                            }

                            else
                            {
                                $Value = intval($Value);
                            }
                        }
                        else
                        {
                            $Value = -1;
                        }

                        # if form value appears valid
                        if ($Value >= 0)
                        {
                            # add value to list of those to be set
                            # (set method expects IDs to appear as indexes)
                            $ValuesToSet[$Value] = 1;
                        }
                    }

                    # if value found to set or field is not required
                    if (count($ValuesToSet) || $Field->Optional())
                    {
                        $OldKeys = array_keys($OldValue);
                        $NewKeys = array_keys($ValuesToSet);

                        sort($OldKeys);
                        sort($NewKeys);

                        if ($OldKeys != $NewKeys)
                        {
                            # set values in resource
                            $Resource->Set($Field, $ValuesToSet, TRUE);
                        }
                    }
                    else
                    {
                        # add field to error list
                        $EmptyFields[] = $Field;
                    }
                    break;

                case MetadataSchema::MDFTYPE_OPTION:
                    # if field allows multiple values
                    $ValuesToSet = array();
                    if ($Field->AllowMultiple())
                    {
                        # retrieve possible values for this field
                        $PossibleValues = $Field->GetPossibleValues();

                        # newer way to get the values
                        if (isset($_POST["F_".$Field->DBFieldName()]))
                        {
                            $GivenValues = $_POST["F_".$Field->DBFieldName()];

                            # for each possible value
                            foreach ($PossibleValues as $ValueId => $ValueName)
                            {
                                # if form field is set for value
                                if (in_array($ValueId, $GivenValues))
                                {
                                    # add value to list of those to be set
                                    $ValuesToSet[$ValueId] = 1;
                                }
                            }
                        }

                        # old way to get the values
                        else
                        {
                            # for each possible value
                            foreach ($PossibleValues as $ValueId => $ValueName)
                            {
                                # if form field is set for value
                                $FKey = "D_".$Field->DBFieldName()."_".$ValueId;
                                if (isset($_POST[$FKey]))
                                {
                                    # add value to list of those to be set
                                    $ValuesToSet[$ValueId] = 1;
                                }
                            }
                        }
                    }
                    else
                    {
                        # retrieve value for this field (if available)
                        if (isset($_POST["F_".$Field->DBFieldName()]))
                        {
                            $ValuesToSet[$_POST["F_".$Field->DBFieldName()]] = 1;
                        }
                    }

                    # if value found to set or field is not required
                    if (count($ValuesToSet) || $Field->Optional())
                    {
                        $OldKeys = array_keys($OldValue);
                        $NewKeys = array_keys($ValuesToSet);

                        sort($OldKeys);
                        sort($NewKeys);

                        if ($OldKeys != $NewKeys)
                        {
                            # set values in resource
                            $Resource->Set($Field, $ValuesToSet, TRUE);
                        }
                    }
                    else
                    {
                        # add field to error list
                        $EmptyFields[] = $Field;
                    }
                    break;

                case MetadataSchema::MDFTYPE_USER:
                    $NewValue = array();

                    $PossibleValues = $Field->GetPossibleValues();
                    $PossibleValues += $Resource->Get($Field);

                    # if values were being set via multiple dropdown lists
                    $BaseFieldName = "D_".$Field->DBFieldName()."_";
                    $FieldIndex = 1;

                    # extract values from lists
                    if (isset($_POST[$BaseFieldName.$FieldIndex]))
                    {
                        while (isset($_POST[$BaseFieldName.$FieldIndex]))
                        {
                            # retrieve value from form field
                            $ThisVal = $_POST[$BaseFieldName.$FieldIndex];

                            # make sure this value is legit for this field
                            if (array_key_exists($ThisVal, $PossibleValues))
                            {
                                $NewValue[$ThisVal] = 1;
                            }
                            # move to the next form value
                            $FieldIndex++;
                        }
                    }

                    # otherwise we're getting values from either a single list
                    # or a search box
                    else if (isset($_POST["F_".$Field->DBFieldName()]))
                    {
                        $InputVal = $_POST["F_".$Field->DBFieldName()];

                        # it was a single value, then use that as a UserId
                        if (!is_array($InputVal))
                        {
                            if (array_key_exists($InputVal, $PossibleValues))
                            {
                                $NewValue[$InputVal] = 1;
                            }
                        }
                        else
                        {
                            foreach ($InputVal as $ThisVal)
                            {
                                # this covers the single-value user search
                                if (array_key_exists($ThisVal, $PossibleValues))
                                {
                                    $NewValue[$ThisVal] = $ThisVal;
                                }
                            }
                        }
                    }

                    if (count($NewValue) || $Field->Optional())
                    {
                        $SignalResult = $AF->SignalEvent(
                                "EVENT_POST_FIELD_EDIT_FILTER", array(
                                       "Field" => $Field,
                                       "Resource" => $Resource,
                                       "Value" => $NewValue));

                        $NewValue = $SignalResult["Value"];
                        $Resource->Set($Field, $NewValue, TRUE);
                    }

                    break;

                case MetadataSchema::MDFTYPE_REFERENCE:
                    # get the new value from the submitted form data
                    $NewValue = GetArrayValue(
                        $_POST,
                        "F_".$Field->DBFieldName(),
                        array());

                    foreach ($NewValue as $Key => $ReferenceId)
                    {
                        # remove any blank values
                        if (strlen(trim($ReferenceId)) < 1)
                        {
                            unset($NewValue[$Key]);
                        }

                        # remove any values that don't look right
                        if (!is_numeric($ReferenceId))
                        {
                            unset($NewValue[$Key]);
                        }
                    }

                    # set the new value
                    $Resource->Set($Field, $NewValue);
                    break;

                case MetadataSchema::MDFTYPE_IMAGE:
                    # for each image description
                    $Images = $Resource->Get($Field, TRUE);
                    foreach ($Images as $ImageId => $Image)
                    {
                        # if description has changed
                        $NewValue = GetArrayValue($_POST, "ImageAlt_".$ImageId);
                        if (!is_null($NewValue) && $NewValue != $Image->AltText())
                        {
                            # set new description
                            $Image->AltText($NewValue);

                            # set flag indicating record has changed
                            $RecordChanged = TRUE;
                        }
                    }
                    break;

                case MetadataSchema::MDFTYPE_FILE:
                    # (handled via special upload mechanisms)
                    if (!$Field->Optional())
                    {
                        $Files = $Resource->Get($Field);
                        if (!count($Files))
                        {
                            $EmptyFields[]= $Field;
                        }
                    }

                    break;

                default:
                    break;
            }
            # If anything changed, set the update flag.
            $RecordChanged |= ($OldValue
                               != $Resource->Get($Field));
        }
    }


    # If the record was changed and all required fields were specifed,
    # modify the appropriate timestamp fields
    if ($RecordChanged && count($EmptyFields)==0)
    {
        $Resource->UpdateAutoupdateFields(
            MetadataField::UPDATEMETHOD_ONRECORDCHANGE,
            $GLOBALS["G_User"]);

        # update search and recommender DBs if configured to do so
        $Resource->QueueSearchAndRecommenderUpdate();

        # signal the modified event if the resource isn't a temp one
        if (!$Resource->IsTempResource())
        {
            $AF->SignalEvent("EVENT_RESOURCE_MODIFY", array("Resource" => $Resource));
        }
    }

    # return list of any empty required fields to caller
    return $EmptyFields;
}

/**
* Process file upload requests.
* @param Resource $Resource Target resource.
* @param User $User User who edited the resource.
*/
function UploadFiles($Resource, $User)
{
    global $AF;

    # check to make sure that tmp is okay
    if ( !(is_dir("tmp") && is_writeable("tmp")) )
    {
        ReturnWithErrors($Resource, array(), ERR_TEMPSTORE);
    }
    # check to make sure that file storage directory is okay
    elseif (!is_dir(File::GetStorageDirectory())
            || !is_writeable(File::GetStorageDirectory()))
    {
        ReturnWithErrors($Resource, array(), ERR_FILESTORE);
    }
    else
    {
        # for each metadata field that might have an uploaded file
        $Schema = new MetadataSchema($Resource->SchemaId());
        $Fields = $Schema->GetFields(MetadataSchema::MDFTYPE_FILE);
        foreach ($Fields as $Field)
        {
            # if field is modifiable by specified user
            #       and we have an uploaded file for this field
            if ($Resource->UserCanModifyField($User, $Field)
                && isset($_FILES[$Field->DBFieldName()]["tmp_name"])
                && is_uploaded_file($_FILES[$Field->DBFieldName()]["tmp_name"]))
            {
                # save uploaded file
                $TmpFileName = $_FILES[$Field->DBFieldName()]["tmp_name"];
                $NewFile = new File($TmpFileName, $Resource->Id(), $Field->Id(),
                                    $_FILES[$Field->DBFieldName()]["name"]);

                # if file save went fine
                if ($NewFile->Status() == File::FILESTAT_OK)
                {
                    $AF->SignalEvent(
                        "EVENT_RESOURCE_FILE_ADD",
                        array(
                            "Field" => $Field,
                            "Resource" => $Resource,
                            "File" => $NewFile));
                }

                else
                {
                    # set error message and error out
                    switch ($NewFile->Status())
                    {
                        case File::FILESTAT_ZEROLENGTH:
                            $Error = ERR_ZEROLENGTH;
                            break;

                        default:
                            $Error = ERR_FILEUPLOADERROR;
                            break;
                    }
                    $ErrParamOne = $_FILES[$Field->DBFieldName()]['name'];
                    $ErrParamTwo = $NewFile->Status();
                    ReturnWithErrors($Resource, $Field,
                                     $Error, $ErrParamOne, $ErrParamTwo);
                }

                # remove temp file
                unlink($TmpFileName);
            }
        }
    }
}

/**
* Process image upload requests.
* @param Resource $Resource Target resource.
* @param User $User User who edited the resource.
*/
function UploadImages($Resource, $User)
{
    # Check to make sure that tmp is okay
    if ( !(is_dir("tmp") && is_writeable("tmp")) )
    {
        ReturnWithErrors($Resource, array(), ERR_TEMPSTORE);
    }
    # check to make sure image storage directories are okay
    elseif ($ErrorMsgs = SPTImage::CheckDirectories())
    {
        $ImagestoreErrors = "" ;

        foreach( $ErrorMsgs as $Message )
        {
            $ImagestoreErrors .= "<br>".$Message;
        }

        ReturnWithErrors($Resource, array(), ERR_IMAGESTORE,
                         $ImagestoreErrors);
    }
    else
    {
        # for each metadata field that might have an uploaded image
        $Schema = new MetadataSchema($Resource->SchemaId());
        $Fields = $Schema->GetFields(MetadataSchema::MDFTYPE_IMAGE);
        foreach ($Fields as $Field)
        {
            # if field is modifiable by specified user
            #       and we have an uploaded file for this field
            if ($Resource->UserCanModifyField($User, $Field)
                && isset($_FILES[$Field->DBFieldName()]["tmp_name"])
                && is_uploaded_file($_FILES[$Field->DBFieldName()]["tmp_name"]))
            {
                # create temp copy of file with correct name
                $TempFile = "tmp/".$_FILES[$Field->DBFieldName()]['name'];
                copy($_FILES[$Field->DBFieldName()]["tmp_name"], $TempFile);

                # create new Image object from uploaded file
                $Image = new SPTImage($TempFile,
                                      $Field->MaxWidth(),
                                      $Field->MaxHeight(),
                                      $Field->MaxPreviewWidth(),
                                      $Field->MaxPreviewHeight(),
                                      $Field->MaxThumbnailWidth(),
                                      $Field->MaxThumbnailHeight());

                # if file save failed
                if ($Image->Status() != AI_OKAY)
                {
                    # set error message and error out
                    switch ($Image->Status())
                    {
                        case AI_UNKNOWNTYPE:
                        case AI_UNSUPPORTEDFORMAT:
                            $Error = ($Image->Status() == AI_UNSUPPORTEDFORMAT)
                                    ? ERR_UNSUPPORTEDIMAGEFORMAT
                                    : ERR_UNKNOWNIMAGETYPE;
                            $ErrParamOne = $_FILES[$Field->DBFieldName()]['name']
                                    ." (".$Image->Format().")";
                            ReturnWithErrors($Resource, $Field, $Error, $ErrParamOne);
                            break;

                        default:
                            $Error = ERR_IMAGEUPLOADERROR;
                            $ErrParamOne = $_FILES[$Field->DBFieldName()]['name'];
                            $ErrParamTwo = $Image->Status();
                            ReturnWithErrors($Resource, $Field, $Error,
                                    $ErrParamOne, $ErrParamTwo);
                            break;
                    }
                }
                else
                {
                    # attach image to resource
                    $Resource->Set($Field, $Image->Id());

                    # set the image's alternate text
                    $AltText = GetArrayValue($_POST, "ImageAlt_".$Field->DBFieldName());
                    $Image->AltText($AltText);
                }
            }
        }
    }
}

/**
 * Check if a Referer is allowed by signaling
 * EVENT_PLUGIN_EXTEND_EDIT_RESOURCE_COMPLETE_ACCESS_LIST event to all plugins
 * and asking them to add their pages' regexes to a allowed list, then check
 * this list against HTTP_REFERER.
 *
 * @return Returns TRUE if this referer is allowed, otherwise FALSE.
 */
function RefererIsAllowed()
{
    global $AF;
    $AllowRefererList = array(
        "%/r".intval($_GET["ID"])."/%",
        "%P=(FullRecord|EditResource(Complete)?)\&%");
    $AllowRefererList = $AF->SignalEvent(
            "EVENT_PLUGIN_EXTEND_EDIT_RESOURCE_COMPLETE_ACCESS_LIST",
            array("AllowList" => $AllowRefererList));
    $Allowed = FALSE;
    foreach ($AllowRefererList["AllowList"] as $Element)
    {
        $Allowed = $Allowed || preg_match($Element, $_SERVER["HTTP_REFERER"]);
        if ($Allowed)
        {
            break;
        }
    }
    return $Allowed;
}

# ----- MAIN -----------------------------------------------------------------

# set flags to defaults
$G_ResourceDeleted = FALSE;
$G_AddingCancelled = FALSE;
$G_EditingCancelled = FALSE;
$G_DeletionCancelled = FALSE;
$G_DeleteRequested = FALSE;
$G_FieldsEdited = FALSE;

# retrieve resource being edited
if (isset($_GET["ID"]) && Resource::ItemExists($_GET["ID"]))
{
    $G_Resource = new Resource(intval($_GET["ID"]));
}

# If the user was kind enough to tell us where they came from,
# but it's not in the allowed list
if (isset($_SERVER['HTTP_REFERER']) && !RefererIsAllowed())
{
    # If the referer was not allowed, but we do have a valid ID corresponding
    # to a resource, jump to corresponding FullRecord page.
    if (isset($_GET["ID"]) && isset($G_Resource))
    {
        $AF->SetJumpToPage("FullRecord"."&ID=".intval($_GET["ID"]));
    }
    else
    {
        $AF->SetJumpToPage("Home");
    }
}
elseif (!isset($G_Resource))
{
    # if we could not find resource with supplied ID
    $AF->SetJumpToPage("EditResource".
                       (isset($_GET["ID"])?"&ID=".intval($_GET["ID"]):""));
}
else
{
    # bail out if user is not authorized to edit this resource
    if (!$G_Resource->UserCanModify($User))
    {
        CheckAuthorization(-1);
        return;
    }
    elseif (isset($_GET["UpdateButton"]))
    {
        $Schema = new MetadataSchema($G_Resource->SchemaId());
        $Field = $Schema->GetField(intval($_GET["UpdateButton"]));

        if  ($Field->Status() == MetadataSchema::MDFSTAT_OK &&
             $GLOBALS["G_User"]->IsLoggedIn() &&
             $G_Resource->UserCanModifyField($GLOBALS["G_User"], $Field) &&
             $Field->UpdateMethod() == MetadataField::UPDATEMETHOD_BUTTON )
        {
            switch ($Field->Type())
            {
                case MetadataSchema::MDFTYPE_TIMESTAMP:
                    $G_Resource->Set($Field, "now");
                    break;
                case MetadataSchema::MDFTYPE_USER:
                    $G_Resource->Set($Field, $GLOBALS["G_User"]);
                    break;
                default:
                    throw new Exception("Unsupported field type for update button");
            }
        }

        $AF->SetJumpToPage("FullRecord&ID=".intval($_GET["ID"]));
    }
    elseif (count($_POST)==0)
    {
        ReturnWithErrors($G_Resource, array(), ERR_POSTEMPTY);
    }
    else
    {
        $Schema = new MetadataSchema($G_Resource->SchemaId());

        $H_ResourceName = $Schema->ResourceName();

        # take action based on which submit button was pressed
        switch (isset($_POST["Submit"]) ? $_POST["Submit"] : NULL)
        {
            case "Add":
                # save changes to record
                $EmptyFields = UpdateResource($G_Resource, $G_User);

                # if some required fields were not filled out
                if (count($EmptyFields))
                {
                    # return to editing page with error
                    ReturnWithErrors($G_Resource, $EmptyFields, ERR_REQUIREDFIELDEMPTY);
                }
                else
                {
                    # convert temporary record to permanent
                    $G_Resource->IsTempResource(FALSE);

                    $G_Resource->UpdateAutoupdateFields(
                        MetadataField::UPDATEMETHOD_ONRECORDCREATE,
                        $GLOBALS["G_User"]);

                    # update search and recommender DBs (if configured to do so)
                    $G_Resource->QueueSearchAndRecommenderUpdate();
                }
                break;

            case "Save":
                # save changes to record
                $EmptyFields = UpdateResource($G_Resource, $G_User);

                # process image upload requests
                UploadImages($G_Resource, $G_User);

                # process file upload requests
                UploadFiles($G_Resource, $G_User);

                # if some required fields were not filled out
                if (count($EmptyFields))
                {
                    # return to editing page with error
                    ReturnWithErrors($G_Resource, $EmptyFields, ERR_REQUIREDFIELDEMPTY);
                }
                break;

            case "Duplicate":
                # create duplicate resource
                $Factory = new ResourceFactory($G_Resource->SchemaId());
                $DupResource = $Factory->DuplicateResource($G_Resource->Id());

                # handle updating fields on resource duplication

                # modify likely field to indicate resource is duplicate
                $TitleField = $Schema->GetFieldByMappedName("Title");
                $DescriptionField = $Schema->GetFieldByMappedName("Description");
                if ($TitleField instanceof MetadataField)
                {
                    $DupResource->Set($TitleField,
                            $DupResource->Get($TitleField)." [DUPLICATE]");
                }
                elseif ($DescriptionField instanceof MetadataField)
                {
                    $DupResource->Set($DescriptionField,
                            $DupResource->Get($DescriptionField)." [DUPLICATE]");
                }

                # update the timestamp fields
                $TimestampFields =
                        $Schema->GetFields(MetadataSchema::MDFTYPE_TIMESTAMP);
                foreach ($TimestampFields as $TimestampField)
                {
                    # if the record is not released, blank the timestamp
                    if ($TimestampField->Name() == "Date Of Record Release")
                    {
                        $DupResource->Set($TimestampField, NULL);
                    }

                    # Blank the date of record creation for the duplicate:
                    if ($TimestampField->Name() == "Date Of Record Creation")
                    {
                        $DupResource->Set($TimestampField, NULL);
                    }

                    # Set date last modified = now to avoid being removed as "stale"
                    if ($TimestampField->Name() == "Date Last Modified")
                    {
                        $DupResource->Set($TimestampField, "now");
                    }
                }

                # set LastModifiedById to the currently logged in user
                if ($Schema->FieldExists("Last Modified By Id"))
                {
                    $DupResource->Set("Last Modified By Id",
                                      $GLOBALS["G_User"]->Get("UserId"));
                }

                # signal that a resource has been duplicated
                $AF->SignalEvent("EVENT_RESOURCE_DUPLICATE", array(
                    $DupResource, $G_Resource));

                # return to editing page with duplicate
                $AF->SetJumpToPage("EditResource&ID=".$DupResource->Id());
                break;

            case "Delete":
                # if delete confirmation already requested
                if (isset($_POST["H_DeletionRequested"]))
                {
                    # set flag to indicate resource should be deleted
                    $G_ResourceDeleted = TRUE;
                }
                else
                {
                    # save changes to record
                    UpdateResource($G_Resource, $G_User);

                    # set flag to indicate need to ask for delete confirmation
                    $G_DeleteRequested = TRUE;
                }
                break;

            case "Cancel":
                # if cancelling resource deletion
                if (isset($_POST["H_DeletionRequested"]))
                {
                    # set flag to indicate that deletion was cancelled
                    $G_DeletionCancelled = TRUE;
                }
                else
                {
                    # if this was a new resource being added
                    if ($G_Resource->IsTempResource())
                    {
                        # set flag to indicate that adding a new resource was cancelled
                        $G_AddingCancelled = TRUE;
                    }
                    else
                    {
                        # set flag to indicate that editing was cancelled
                        $G_EditingCancelled = TRUE;
                    }
                }
                break;

            case "Edit":
                $AF->SetJumpToPage("EditResource&ID="
                                   .$G_Resource->Id());
                break;

            case "Upload":
                # return to editing page
                $AF->SetJumpToPage("EditResource&ID=".$G_Resource->Id());

                # save changes to record
                UpdateResource($G_Resource, $G_User);

                # process image upload requests
                UploadImages($G_Resource, $G_User);

                # process file upload requests
                UploadFiles($G_Resource, $G_User);
                break;

            default:
                # save changes to record
                UpdateResource($G_Resource, $G_User);

                # process image upload requests
                UploadImages($G_Resource, $G_User);

                # process file upload requests
                UploadFiles($G_Resource, $G_User);

                # for each controlled name and tree field
                $Fields = $Schema->GetFields(MetadataSchema::MDFTYPE_CONTROLLEDNAME
                        | MetadataSchema::MDFTYPE_TREE);
                foreach ($Fields as $Field)
                {
                    # if user pressed search button for field
                    if (isset($_POST["SearchField_".$Field->Id()]))
                    {
                        # jump to search page for field
                        $AF->SetJumpToPage("EditResourceSearchName&ID="
                                .$G_Resource->Id()."&MF=".$Field->Id());
                    }
                }

                # for each file or image field
                $Fields = $Schema->GetFields(MetadataSchema::MDFTYPE_FILE
                        | MetadataSchema::MDFTYPE_IMAGE);
                foreach ($Fields as $Field)
                {
                    # if user has permission to modify field
                    if ($G_Resource->UserCanModifyField($G_User, $Field))
                    {
                        # if files uploaded for this field for this resource
                        $Files = $G_Resource->GetByField($Field, TRUE);
                        if (count($Files))
                        {
                            # for each existing file for field
                            foreach ($Files as $File)
                            {
                                # if user pressed delete button for file
                                if (($Field->Type() == MetadataSchema::MDFTYPE_FILE)
                                        && isset($_POST["DeleteFile_".$File->Id()]))
                                {
                                    $AF->SignalEvent(
                                        "EVENT_RESOURCE_FILE_DELETE",
                                        array(
                                            "Field" => $Field,
                                            "Resource" => $G_Resource,
                                            "File" => $File));

                                    # detach file from resource
                                    $G_Resource->Clear($Field, $File);

                                    # return to edit page with appropriate message
                                    $AF->SetJumpToPage("EditResource&ID="
                                                       .$G_Resource->Id());
                                }
                                # else if user pressed delete button for image
                                elseif (($Field->Type() == MetadataSchema::MDFTYPE_IMAGE)
                                        && isset($_POST["DeleteImage_".$File->Id()]))
                                {
                                    # detach image from resource
                                    $G_Resource->Clear($Field, $File);

                                    # return to edit page with appropriate message
                                    $AF->SetJumpToPage("EditResource&ID="
                                                       .$G_Resource->Id());
                                }
                            }
                        }
                    }
                }
                break;
        }

        # load fields for display usage
        $G_Fields = $Schema->GetFields(NULL, MetadataSchema::MDFORDER_EDITING);

        $AF->AddPostProcessingCall("DeleteResourceIfAppropriate",
                                   $G_Resource, $G_ResourceDeleted, $G_AddingCancelled);
    }
}

/**
* Post-processing function to handle resource deletions
* @param Resource $Resource Target resource.
* @param bool $ResourceDeleted Should resource be deleted ?
* @param bool $AddingCancelled Was adding cancelled?
*/
function DeleteResourceIfAppropriate($Resource, $ResourceDeleted, $AddingCancelled)
{
    # delete resource if appropriate (now that we are done with it)
    if ($ResourceDeleted || $AddingCancelled)
    {
        if (LocalFunctionExists("ResourceDeletionHook"))
        {
            Local_ResourceDeletionHook($Resource->Id());
        }
        $Resource->Delete();
    }
}
