<?PHP

#
#   FILE:  SPT--File.php
#
#   METHODS PROVIDED:
#       File()
#           - constructor
#       SomeMethod($SomeParameter, $AnotherParameter)
#           - short description of method
#
#   AUTHOR:  Edward Almasy
#
#   Part of the Collection Workflow Integration System
#   Copyright 2007 Edward Almasy and Internet Scout
#   http://scout.wisc.edu
#


class File {

    # ---- PUBLIC INTERFACE --------------------------------------------------

    
    # status codes (set by constructor and returned by File::Status())
    const FILESTAT_OK =             0;
    const FILESTAT_COPYERROR =      1;
    const FILESTAT_PARAMERROR =     2;
    const FILESTAT_ZEROLENGTH =     3;
    const FILESTAT_DOESNOTEXIST =   4;
    const FILESTAT_UNREADABLE =     5;

    # object constructor
    function File($IdOrFileName, $ResourceId = NULL, $FieldId = NULL, 
            $DesiredFileName = NULL, $CheckFileLength = TRUE)
    {
        # assume constructor will succeed
        $this->Status = File::FILESTAT_OK;

        # get our own database handle
        $DB = new SPTDatabase();
        $this->DB = $DB;

        # if ID supplied
        if (is_int($IdOrFileName))
        {
            # set file ID from supplied value
            $this->Id = intval($IdOrFileName);

            # load file info from database
            $DB->Query("SELECT * FROM Files WHERE FileId = ".$this->Id);
            $this->DBFields = $DB->FetchRow();
        }
        # else if file name and resource ID and field ID supplied
        elseif (strlen($IdOrFileName) && ($ResourceId != NULL) && ($FieldId != NULL))
        {
            # if file does not exist
            $TempFileName = $IdOrFileName;
            if (!file_exists($TempFileName) || !is_readable($TempFileName))
            {
                # set status indicating appropriate error
                $this->Status = file_exists($TempFileName) 
                        ? File::FILESTAT_DOESNOTEXIST : File::FILESTAT_UNREADABLE;
            }
            else
            {
                # if we were asked to check file length and file was zero length
                $FileLength = filesize($TempFileName);
                if ($CheckFileLength && !$FileLength)
                {
                    # set status indicating zero length file
                    $this->Status = File::FILESTAT_ZEROLENGTH;
                }
                else
                {
                    # generate secret string (used to protect from unauthorized download)
                    srand((double)microtime() * 1000000);
                    $SecretString = sprintf("%04X", rand(1, 30000));
    
                    # attempt to get file type
                    if (function_exists("finfo_open"))
                    {
                        $FInfoHandle = finfo_open(FILEINFO_MIME);
                        $FileType = finfo_file($FInfoHandle, $TempFileName);
                        finfo_close($FInfoHandle);
                    }
                    else
                    {
                        $FileType = "";
                    }
    
                    # add file info to database
                    $BaseFileName = $DesiredFileName 
                            ? basename($DesiredFileName) : basename($TempFileName);
                    $DB->Query("INSERT INTO Files"
                            ." (ResourceId, FieldId, FileName, FileLength, FileType,"
                                ." SecretString)"
                            ." VALUES ("
                                .intval($ResourceId).", "
                                .intval($FieldId).", "
                                ."'".addslashes($BaseFileName)."', "
                                .$FileLength.", "
                                ."'".$FileType."', "
                                ."'".$SecretString."')");
        
                    # retrieve ID of new file
                    $this->Id = $DB->LastInsertId("Files");
    
                    # load file info back in from database
                    $DB->Query("SELECT * FROM Files WHERE FileId = ".$this->Id);
                    $this->DBFields = $DB->FetchRow();
        
                    # copy file to storage
                    $CopySucceeded = copy($IdOrFileName, $this->GetNameOfStoredFile());
        
                    # if copy failed
                    if (!$CopySucceeded)
                    {
                        # remove file info from database
                        $DB->Query("DELETE FROM Files WHERE FileId = ".$this->Id);
        
                        # set status indicating constructor failed
                        $this->Status = File::FILESTAT_COPYERROR;
                    }
                }
            }
        }
        else
        {
            # set status indicating constructor failed
            $this->Status = File::FILESTAT_PARAMERROR;
        }
    }

    # return object status (used to report errors occurring in constructor)
    function Status() {  return $this->Status;  }

    # get various attributes
    function Id() {  return $this->Id;  }
    function Name() {  return $this->DBFields["FileName"];  }
    function GetLength() {  return $this->DBFields["FileLength"];  }
    function GetType() {  return $this->DBFields["FileType"];  }

    # get/set various attributes
    function Comment($NewValue = DB_NOVALUE)
            {  return $this->UpdateValue("FileComment", $NewValue);  }
    function FieldId($NewValue = DB_NOVALUE)
            {  return $this->UpdateValue("FieldId", $NewValue);  }
    function ResourceId($NewValue = DB_NOVALUE)
            {  return $this->UpdateValue("ResourceId", $NewValue);  }

    # get MIME type (defaults to "application/octet-stream" if not available)
    function GetMimeType()
    {
        return strlen($this->GetType()) 
                ? $this->GetType() : "application/octet-stream";
    }

    # get link for downloading file
    function GetLink()
    {
        global $NavDirCorrection;
        return $NavDirCorrection."SPT--DownloadFile.php?Id=".$this->Id;
    }

    # delete file (other methods are invalid after calling this!)
    function Delete()
    {
        # remove file entry from DB
        $this->DB->Query("DELETE FROM Files WHERE FileId = ".$this->Id);

        # delete file
        unlink($this->GetNameOfStoredFile());
    }

    # retrieve actual name of stored file
    function GetNameOfStoredFile()
    {
        global $NavDirCorrection;
        return sprintf($NavDirCorrection."FileStorage/%06d-%s-%s", 
                $this->Id, $this->DBFields["SecretString"], $this->Name());
    }


    # ---- PRIVATE INTERFACE -------------------------------------------------

    var $DB;
    var $Status;
    var $Id;
    var $DBFields;

    # convenience function to supply parameters to Database->UpdateValue()
    function UpdateValue($FieldName, $NewValue)
    {
        return $this->DB->UpdateValue("Files", $FieldName, $NewValue,
                               "FileId = ".intval($this->Id),
                               $this->DBFields, TRUE);
    }
}


?>
