<?PHP
#
#   FILE:  EditResourceComplete.php
#
#   Copyright 2007-2010 Edward Almasy and Internet Scout
#   http://scout.wisc.edu
#

PageTitle("Resource Editing Complete");

if (!CheckAuthorization(PRIV_RESOURCEADMIN, PRIV_MYRESOURCEADMIN, PRIV_RELEASEADMIN)) {  return;  }

# ----- 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 back to editing page with error info
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) : ""));
}

# update search and recommender system DBs (if dynamic updates enabled)
function UpdateSearchAndRecommender($Resource)
{
    if (!$Resource->IsTempResource())
    {
        $SearchEngine = new SPTSearchEngine();
        $SearchEngine->QueueUpdateForItem($Resource->Id());
        $Recommender = new SPTRecommender();
        $Recommender->QueueUpdateForItem($Resource->Id());
    }
}

# save values from editing form
# (returns array of any unfilled required fields)
# 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.
function UpdateResource($Resource, $User)
{
    # for each metadata field
    $Schema = new MetadataSchema();
    $Fields = $Schema->GetFields(NULL, MetadataSchema::MDFORDER_EDITING);
    $EmptyFields = array();
    $RecordChanged = FALSE;
    foreach ($Fields as $Field)
    {
        # if user has permission to edit field
        if ($Resource->UserCanEditField($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
                        global $AF;
                        $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
                        $NewValue = StripXSSThreats($NewValue);
                        if (($Field->Name() == "Release Flag")
                            && (!$Resource->Get("Release Flag"))
                            && ($NewValue == "1"))
                        {
                            $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:
                    # run value through any hooked filters
                    global $AF;
                    $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();
                    $BaseFieldName = "D_".$Field->DBFieldName()."_";
                    $FieldIndex = 1;
                    $ValuesToSet = array();
                    while (isset($_POST[$BaseFieldName.$FieldIndex]))
                    {
                        # retrieve value from form field
                        $Value = $_POST[$BaseFieldName.$FieldIndex];

                        # 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;
                        }

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

                    # if value found to set or field is not required
                    if (count($ValuesToSet) || $Field->Optional())
                    {
                        # clear any existing values for this field
                        $Resource->ClearByField($Field);

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

                case MetadataSchema::MDFTYPE_OPTION:
                    # clear any existing values for this field
                    $Resource->ClearByField($Field);

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

                        # for each possible value
                        foreach ($PossibleValues as $ValueId => $ValueName)
                        {
                            # if form field is set for value
                            if (isset($_POST["D_".$Field->DBFieldName()."_".$ValueId]))
                            {
                                # 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())
                    {
                        # clear any existing values for this field
                        $Resource->ClearByField($Field);

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

                case MetadataSchema::MDFTYPE_USER:
                    # (this type not currently editable)
                    break;

                case MetadataSchema::MDFTYPE_IMAGE:
                case MetadataSchema::MDFTYPE_FILE:
                    # (these types handled via special upload mechanisms)
                    break;

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


    # If the record was changed, modify the appropriate timestamp fields
    if ($RecordChanged)
    {

        $TimestampFields = $Schema->GetFields(
            MetadataSchema::MDFTYPE_TIMESTAMP);
        foreach ($TimestampFields as $Field)
        {
            if ($Field->UpdateMethod() ==
                MetadataField::UPDATEMETHOD_ONRECORDCHANGE)
            {
                $Resource->Set($Field, "now");
            }
        }

        $Resource->Set("Last Modified By Id", $User);

        # update search and recommender DBs if configured to do so
        UpdateSearchAndRecommender($Resource);
    }

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

# process File upload requests
function UploadFiles($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 that FileStorage is okay
    elseif( !(is_dir("FileStorage") && is_writeable("FileStorage")) )
    {
        ReturnWithErrors($Resource, array(), ERR_FILESTORE);
    }
    else
    {
    # for each metadata field that might have an uploaded file
        $Schema = new MetadataSchema();
        $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->UserCanEditField($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 failed
                if ($NewFile->Status() != File::FILESTAT_OK)
                {
                    # 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
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();
        $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->UserCanEditField($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'];
                        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());
                }
            }
        }
    }
}


# ----- MAIN -----------------------------------------------------------------
# PrintForDebug("_POST", $_POST);
# exit(1);

# 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"]))
{
    $G_Resource = new Resource(intval($_GET["ID"]));
}

# if we could not find resource with supplied ID
if (!isset($G_Resource) || !is_object($G_Resource) || ($G_Resource->Status() != 1))
{
    $AF->SetJumpToPage("EditResource".
                       (isset($_GET["ID"])?"&ID=".intval($_GET["ID"]):""));
}
else
{
    if( count($_POST)==0 )
    {
        ReturnWithErrors($G_Resource, array(), ERR_POSTEMPTY);
    }
    else
    {
        # 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);

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

        case "Save":
            # 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);
            }
            break;

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

            # modify likely field to indicate resource is duplicate
            $Schema = new MetadataSchema();
            if ($Schema->GetFieldByName("Title"))
            {
                $DupResource->Set("Title",
                        $DupResource->GetMapped("Title")." [DUPLICATE]");
            }
            elseif ($Schema->GetFieldByName("Description"))
            {
                $DupResource->Set("Description",
                        $DupResource->GetMapped("Description")." [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->Get("Release Flag"))
                {
                    $DupResource->Set($TimestampField, NULL);
                }

                # otherwise update it to now
                else
                {
                    $DupResource->Set($TimestampField, date("Y-m-d H:i:s"));
                }
            }

            # 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 File":
            # return to editing page
            $AF->SetJumpToPage("EditResource&ID=".$G_Resource->Id());

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

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

            break;

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

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

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

            break;

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

            # for each controlled name and tree field
            $Schema = new MetadataSchema();
            $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
            $Schema = new MetadataSchema();
            $Fields = $Schema->GetFields(MetadataSchema::MDFTYPE_FILE
                    | MetadataSchema::MDFTYPE_IMAGE);
            foreach ($Fields as $Field)
            {
                # if user has permission to modify field
                if ($G_Resource->UserCanEditField($G_User, $Field))
                {
                    # if files uploaded for this field for this resource
                    $Files = $G_Resource->GetByField($Field, TRUE);
                    if (is_object($Files) || count($Files))
                    {
                        if (is_object($Files)) {  $Files = array($Files);  }

                        # for each existing file for field
                        foreach ($Files as $File)
                        {
                            # if user pressed delete button for file
                            if (isset($_POST["DeleteFile_".$File->Id()]))
                            {
                                # delete file
                                $File->Delete();

                                # return to edit page with appropriate message
                                $AF->SetJumpToPage("EditResource&ID="
                                                   .$G_Resource->Id());
                            }
                            # else if user pressed delete button for image
                            elseif (isset($_POST["DeleteImage_".$File->Id()]))
                            {
                                # clear image
                                $G_Resource->ClearByField($Field);

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

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

function DeleteResourceIfAppropriate($Resource, $ResourceDeleted, $AddingCancelled)
{
    # delete resource if appropriate (now that we are done with it)
    if ($ResourceDeleted || $AddingCancelled)
    {
        if (file_exists("include/SPT--Local.php"))
        {
            if (function_exists("Local_ResourceDeletionHook"))
            {
                Local_ResourceDeletionHook($Resource->Id());
            }
        }
        $Resource->Delete();
    }
}
$AF->AddPostProcessingCall("DeleteResourceIfAppropriate",
        $G_Resource, $G_ResourceDeleted, $G_AddingCancelled);

?>
