Location: PHPKode > projects > PhpBMS > phpbms/modules/api/include/apiclass.php
<?php
/*
 $Rev: 285 $ | $LastChangedBy: brieb $
 $LastChangedDate: 2007-08-27 14:05:27 -0600 (Mon, 27 Aug 2007) $
 +-------------------------------------------------------------------------+
 | Copyright (c) 2004 - 2010, Kreotek LLC                                  |
 | All rights reserved.                                                    |
 +-------------------------------------------------------------------------+
 |                                                                         |
 | Redistribution and use in source and binary forms, with or without      |
 | modification, are permitted provided that the following conditions are  |
 | met:                                                                    |
 |                                                                         |
 | - Redistributions of source code must retain the above copyright        |
 |   notice, this list of conditions and the following disclaimer.         |
 |                                                                         |
 | - Redistributions in binary form must reproduce the above copyright     |
 |   notice, this list of conditions and the following disclaimer in the   |
 |   documentation and/or other materials provided with the distribution.  |
 |                                                                         |
 | - Neither the name of Kreotek LLC nor the names of its contributore may |
 |   be used to endorse or promote products derived from this software     |
 |   without specific prior written permission.                            |
 |                                                                         |
 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS     |
 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT       |
 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      |
 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,   |
 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT        |
 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   |
 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY   |
 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT     |
 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   |
 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    |
 |                                                                         |
 +-------------------------------------------------------------------------+
*/

/**
 * API level processing
 *
 * The api class handles processing and format conversion for API calls to the
 * system.
 * @author Brian Rieb <hide@address.com>
 */
class api{

    /**
     * phpBMS DB object
     * @var object
     */
    var $db;

    /**
     * incoming data format.
     * @var string
     */
    var $format;

    /**
     * decoded data passed by the post
     * @var array
     */
    var $data = array();

    /**
     * array of response to be displayed either after processing has completed
     * or when a fatal error occurs
     * @var array
     */
    var $response = array();

    /**
      *  $options
      *  @var object Object containing the current request's options
      */
    var $options;

    /**
     * function api
     *
     * Constructor sets up {@link $db}, decodes {@link $data} using the passed
     * format from {@link $format}
     *
     * @param db object $db database object
     * @param array $data data that is to be decoded
     * @param string $format format of the data
     */
    function api($db, $data, $format="json"){

        $this->db = $db;
        $this->db->errorFormat = $format;

        $this->format = $format;

        $this->data = $this->decode($data);

    }//end function init (api)


    /**
     * function decode
     * decodes data (usually the passed post data) depending on the {@link $format}
     *
     * Currently, this function can only decode JSON data, but support for SOAP,
     * generic XML, or some other format may be added
     *
     * @param string $data Information to be decoded
     *
     * @return array
     */
    function decode($data){

        if(get_magic_quotes_runtime() || get_magic_quotes_gpc())
            $data = stripslashes($data);

        switch($this->format){

            case "json":
                $data = json_decode($data, true);
                break;

        }//endswitch

        return $data;

    }//end function decode


    /**
     * function encode
     * encodes data (usually {@link $request}) depending on the {@link $format}
     *
     * Currently, this function can only encode JSON data, but support for SOAP,
     * generic XML, or some other format may be added
     *
     * @param string $data Information to be encoded
     */
    function encode($data){

        switch($this->format){

            case "json":
                $data = json_encode($data);
                break;

        }//endswitch

        return $data;

    }//end function encode


    /**
     * function isValidDateFormat
     *
     * @param $format Format to be checked.
     *
     * @return bool True if it is valid, false if not.
     */

    function isValidDateFormat($format) {

        switch($format){

            case "SQL":
            case "English, US":
            case "English, UK":
            case "Dutch, NL":
                $valid = true;
            break;

            default:
                $valid = false;
            break;

        }//end switch

        return $valid;

    }//end function isValidTimeFormat


    /**
     * function isValidTimeFormat
     *
     * @param $format Format to be checked.
     *
     * @return bool True if it is valid, false if not.
     */

    function isValidTimeFormat($format) {

        switch($format){

            case "24 Hour":
            case "12 Hour":
                $valid = true;
            break;

            default:
                $valid = false;
            break;

        }//end switch

        return $valid;

    }//end function isValidTimeFormat


    /**
     * function processOptions
     * @param array $options
     */

    function processOptions($options) {

        $this->options->useUuid = true;
            if(isset($options["useUuid"]))
                $this->options->useUuid = (bool)$options["useUuid"];

        /**
          *   date format options
          */

        $this->options->dateFormat = "SQL";
        if(defined("DATE_FORMAT"))
            $this->options->dateFormat = DATE_FORMAT;

        if(isset($options["dateFormat"]))
            if($this->isValidDateFormat($options["dateFormat"]))
                $this->options->dateFormat = $options["dateFormat"];

        /**
          *  Time format options
          */

        $this->options->timeFormat = "24 Hour";
        if(defined("TIME_FORMAT"))
            $this->options->timeFormat = TIME_FORMAT;

        if(isset($options["timeFormat"]))
            if($this->isValidTimeFormat($options["timeFormat"]))
                $this->options->timeFormat = $options["timeFormat"];

        /**
          *  Id field options
          *
          *  This option dictates whether or not to keep the destination's
          *  id field if "replacing" (via the mysql replace) when there is
          *  no id field set.
          */
        $this->options->keepDestId = true;
        if(isset($options["keepDestId"]))
            $this->options->keepDestId = (bool)$options["keepDestId"];

    }//end method --processOptions--

    /**
    * function process
    * Process request array posted to api
    *
    * The method process() loops through the request array, and attempts to
    * find a corresponding function to run for the request. It first looks for
    * a corresponding api class to load. If it does not find it, it next to
    * see if there is an overriden table class file, and finally if none of
    * these are present, it uses the standard table class.
    *
    */
    function process(){

        $i = 1;
        $tabledefid = null;

        if(!is_array($this->data))
            $this->sendError("Passed data malformed.  Was expecting an array.", $this->data, true);

        foreach($this->data as $request){

            if(!is_array($request))
                $this->sendError("Malformed request number ".$i, $request);

            if(!isset($request["tabledefid"]) || !isset($request["command"]) || !isset($request["data"]))
                $this->sendError("Malformed request number ".$i, $request);

            /**
              *  Process the options and populate the options object.
              */
            if(!isset($request["options"]))
                $request["options"] = NULL;

            $this->processOptions($request["options"]);

            if((int) $request["tabledefid"] !== $tabledefid){

                $tabledefid = mysql_real_escape_string($request["tabledefid"]);

                //First let's get the table information from the tabledef
                $querystatement = "
                    SELECT
                        `maintable`,
                        `deletebutton`,
                        `querytable`,
                        `modules`.`name`,
                        `apiaccessible`
                    FROM
                        `tabledefs` INNER JOIN `modules` ON tabledefs.moduleid = modules.uuid
                    WHERE
                        tabledefs.uuid = '".$tabledefid."'
                ";

                $queryresult = $this->db->query($querystatement);

                if($this->db->numRows($queryresult) == 0){

                    if (!(in_array($request["command"], array("procedure", "getsetting")))){

                        $this->sendError("Invalid tabledefid (".$tabledefid.") from request number ".$i);
                        continue;

                    } else {

                        $deletebutton = "delete";
                        $maintable = "settings";
                        $modulename = "base";

                        $hasAPIOveride = false;
                        $hasTableClassOveride = false;

                    }//endif

                } else {

                    $therecord = $this->db->fetchArray($queryresult);

                    if(!$therecord["apiaccessible"]){
                        $this->sendError("Invalid tabledefid (".$tabledefid.") from request number ".$i.": This table definition is inaccessible via api.");
                        continue;
                    }//endif


                    $deletebutton = $therecord["deletebutton"];

                    $maintable = $therecord["maintable"];

                    $modulename = $therecord["name"];

                    //check for ovridding classes only once.
                    $hasAPIOveride = file_exists("../extendedapi/".$maintable.".php");
                    $hasTableClassOveride = file_exists("../".$modulename."/include/".$maintable.".php");

                }//endif

            }//endif


            /* Order in which to check for processors is as follows:

               If the extendedAPI module is present, look for a file matching the main
               table name of the table def.
               Example: modules/extendedapi/clients.php

               If a table class file exists in the module's include folder
               use that.
               Example: modules/bms/include/clients.php

               Use the standard class module.

            */
            $methodName = "";

            if($hasAPIOveride){
                // Found an API module table php

                @ include_once("modules/extendedapi/".$maintable.".php");

                $className = $className."Api";

                if(class_exists($className)) {

                    $processor = new $className($this->db);
                    $processor->dateFormat =  $this->options->dateFormat;
                    $processor->timeFormat =  $this->options->timeFormat;

                    if(!method_exists($processor, $request["command"])) {

                        $methodName = $request["command"];

                        $this->response[] = $processor->$methodName($request["data"], $this->options->useUuid);

                    }//endif

                }//end if

            }//endif


            /* If the command starts with api_, and there is a request overload, let's assume they
               are trying to call a homeade function in the ovveriden phpBMS table that they created.
            */
            if(!$methodName && substr($request["command"], 0, 4) == "api_" && $hasTableClassOveride){

                include_once("include/tables.php");
                @ include_once("modules/".$modulename."/include/".$maintable.".php");

                if(class_exists($maintable)){
                    $processor = new $maintable($this->db, $tabledefid);
                    $processor->dateFormat =  $this->options->dateFormat;
                    $processor->timeFormat =  $this->options->timeFormat;
                }else{
                    $processor = new phpbmsTable($this->db, $tabledefid);
                    $processor->dateFormat =  $this->options->dateFormat;
                    $processor->timeFormat =  $this->options->timeFormat;
                }

                if(method_exists($processor, $request["command"])){

                    $methodName = $request["command"];

                    $this->response[] = $processor->$methodName($request["data"], $this->options->useUuid);

                }//endif

            }//endif

            if(!$methodName) {
                /* Either using the modules overriden table class or search
                   functions class or the standard one There are several
                   standard commands that can be passed:

                   * insert - calls the tabledefs insertRecord command, the
                                same command that is called on standard
                                phpBMS forms. a variable array should be
                                passed in the request data.

                   * update - calls the tabledefs iupdateRecord command, the
                                same command that is called on standard
                                phpBMS forms. a variable array should be
                                passed in the request data

                   * delete (or the corresponding delete button command)
                            - calls the deleteRecord searchFunctions command
                            data should be an array of ids

                   * procedure - This calls a stored MySQL stored procedure
                                 request data should pass an object with the
                                 (name) and optionally an array of any
                                 (parameters)

                    In addition, you can pass a command that corresponds to
                    any additional commands as defined in the table definition
                    the request data passed should contain an array of ids
                */

                switch($request["command"]){

                    case "insert":
                        //======================================================
                        include_once("include/tables.php");

                        if($hasTableClassOveride){

                            @ include_once("modules/".$modulename."/include/".$maintable.".php");

                            if(class_exists($maintable)){
                                $processor = new $maintable($this->db, $tabledefid);
                                $processor->dateFormat =  $this->options->dateFormat;
                                $processor->timeFormat =  $this->options->timeFormat;
                            }else{
                                $processor = new phpbmsTable($this->db, $tabledefid);
                                $processor->dateFormat =  $this->options->dateFormat;
                                $processor->timeFormat =  $this->options->timeFormat;
                            }//end if

                        } else{
                            $processor = new phpbmsTable($this->db, $tabledefid);
                            $processor->dateFormat =  $this->options->dateFormat;
                            $processor->timeFormat =  $this->options->timeFormat;
                        }//end if

                        $errorArray = $processor->verifyVariables((array) $request["data"]);

                        if(count($errorArray))
                            $this->sendError("Insert failed from request number ".$i, $errorArray);
                        else {

                            $overrideID = false;
                            if(is_array($request["data"]))
                                if(isset($request["data"]["id"])){

                                    if(((int)$request["data"]["id"]) !== 0)
                                        $overrideID = true;
                                    if($this->options->keepDestId && isset($request["data"]["uuid"]) && $this->options->useUuid)
                                        $request["data"]["id"] = getId($this->db, $processor->uuid, $request["data"]["uuid"]);

                                }elseif($this->options->keepDestId && isset($request["data"]["uuid"]) && $this->options->useUuid)
                                    $request["data"]["id"] = getId($this->db, $processor->uuid, $request["data"]["uuid"]);

                            $createUuid = true;
                            if(is_array($request["data"]))
                                if(isset($request["data"]["uuid"]))
                                    if((string)$request["data"]["uuid"] !== ""){
                                        $overrideID = true;
                                        $createUuid = false;
                                    }//end if

                            if(!isset($processor->fields["uuid"]))
                                $createUuid = false;

                            $newid = $processor->insertRecord($request["data"], NULL, $overrideID, true, $createUuid);

                            if($newid){
                                if($createUuid){
                                    $this->_addToResponse("added", "record added to tabledef ".$tabledefid, $newid["uuid"]);
                                }elseif(isset($processor->fields["uuid"])){
                                    $this->_addToResponse("added", "record added to tabledef ".$tabledefid, $request["data"]["uuid"]);
                                }else{
                                    $this->_addToResponse("added", "record added to tabledef ".$tabledefid, $newid);
                                }//end if
                            }else
                                $this->sendError("Insert failed from request number ".$i);

                        }//endif

                        break;

                    case "update":
                        //======================================================
                        include_once("include/tables.php");

                        if($hasTableClassOveride){

                            @ include_once("modules/".$modulename."/include/".$maintable.".php");

                            if(class_exists($maintable)){
                                $processor = new $maintable($this->db, $tabledefid);
                                $processor->dateFormat =  $this->options->dateFormat;
                                $processor->timeFormat =  $this->options->timeFormat;
                            }else{
                                $processor = new phpbmsTable($this->db, $tabledefid);
                                $processor->dateFormat =  $this->options->dateFormat;
                                $processor->timeFormat =  $this->options->timeFormat;
                            }//end if

                        } else {
                            $processor = new phpbmsTable($this->db, $tabledefid);
                            $processor->dateFormat =  $this->options->dateFormat;
                            $processor->timeFormat =  $this->options->timeFormat;
                        }//end if

                        $errorArray = $processor->verifyVariables($request["data"]);

                        if($this->options->useUuid){
                            if(!isset($request["data"]["uuid"]))
                                $errorArray[] = "The `uuid` field must be set.";
                        }else{
                            if(!isset($request["data"]["id"]))
                                $errorArray[] = "The `id` field must be set.";
                        }//end if


                        if(count($errorArray))
                            $this->sendError("Update failed from request number ".$i, $errorArray);
                        else {

                            $processor->updateRecord($request["data"], NULL, (bool)$this->options->useUuid);

                            $this->_addToResponse("updated", "record updated in tabledef ".$tabledefid);

                        }//endif

                        break;

                    case "get":
                        //======================================================

                        include_once("include/tables.php");

                        if($hasTableClassOveride){

                            @ include_once("modules/".$modulename."/include/".$maintable.".php");

                            if(class_exists($maintable)){
                                $processor = new $maintable($this->db, $tabledefid);
                                $processor->dateFormat =  $this->options->dateFormat;
                                $processor->timeFormat =  $this->options->timeFormat;
                            }else{
                                $processor = new phpbmsTable($this->db, $tabledefid);
                                $processor->dateFormat =  $this->options->dateFormat;
                                $processor->timeFormat =  $this->options->timeFormat;
                            }//end if

                        } else {
                            $processor = new phpbmsTable($this->db, $tabledefid);
                            $processor->dateFormat =  $this->options->dateFormat;
                            $processor->timeFormat =  $this->options->timeFormat;
                        }//end if
                        
                        $errorMessage = "";
                        if($this->options->useUuid){
                            if(!isset($request["data"]["uuid"]))
                                $errorMessage = "The `uuid` field must be set.";
                        }else{
                            if(!isset($request["data"]["id"]))
                                $errorMessage = "The `id` field must be set.";
                        }//end if
                        
                        if($errorMessage)
                            $this->sendError("Update failed from request number ".$i, $errorMessage);
                        elseif(!$this->options->useUuid){
                            $therecord = $processor->getRecord((int) $request["data"]["id"], $this->options->useUuid);
                            $thereturn = $therecord["id"];
                            $thevalue = (int)$request["data"]["id"];
                        }else{
                            $therecord = $processor->getRecord(mysql_real_escape_string($request["data"]["uuid"]), $this->options->useUuid);
                            $thereturn = $therecord["uuid"];
                            $thevalue = $request["data"]["uuid"];
                        }

                        if($thereturn == $thevalue)
                            $this->_addToResponse("retrieved", "record (".htmlQuotes($thevalue).") retrieved in tabledef ".$tabledefid, $therecord);
                        else
                            $this->_addToResponse("retrieved", "no record found (".htmlQuotes($thevalue).") in tabledef ".$tabledefid);

                        break;

                    case "delete":
                    case $deletebutton:
                        //======================================================
                        if(!is_array($request["data"]))
                                $this->sendError("Passed data is not array in request number ".$i, $request["data"]);
                        else {

                            include_once("include/search_class.php");

                           //if($useUuid){
                           //     if(!isset($request["data"]->uuid))
                           //         $this->sendError("The `uuid` field must be set in request number ".$i, $request["data"], true);
                           //     else
                           //         $id = $request["data"]->uuid;
                           //
                           // }else{
                           //     if(!isset($request["data"]->id))
                           //         $this->sendError("The `id` field must be set in request number ".$i, $request["data"], true);
                           //     else
                           //         $id = $request["data"]->uuid;
                           // }//end if

                            if($hasTableClassOveride){

                                @ include_once("modules/".$modulename."/include/".$maintable.".php");

                                $className = $maintable."SearchFunctions";

                                if(class_exists($className))
                                    $processor = new $className($this->db, $tabledefid, $request["data"]);
                                else
                                    $processor = new searchFunctions($this->db, $tabledefid, $request["data"]);

                            } else
                                $processor = new searchFunctions($this->db, $tabledefid, $request["data"]);


                            $result = $processor->delete_record($this->options->useUuid);

                            $this->_addToResponse($request["command"], $result);

                        }//endif

                        break;

                    case "procedure":
                        //======================================================
                        if(!is_array($request["data"]))
                            $this->sendError("Wrong passed procedure format, expected object in request number ".$i, $request["data"]);
                        else{

                            if(!isset($request["data"]["name"]))
                                $this->sendError("Wrong passed procedure format, name missing in request number ".$i, $request["data"]);
                            else {

                                //check to see if stored procedure exists
                                $querystatement = "
                                    SHOW PROCEDURE STATUS LIKE '".mysql_real_escape_string($request["data"]["name"])."'
                                ";

                                $queryresult = $this->db->query($querystatement);

                                if($this->db->numRows($queryresult) === 0)
                                    $this->sendError("Procedure '".$request["data"]["name"]."' does not exist in request number ".$i, $request["data"]);
                                else{

                                    $parameterList = "";

                                    if(isset($request["data"]["parameters"]))
                                        foreach($request["data"]["parameters"] as $parameter)
                                            $parameterList .= ", '".mysql_real_escape_string($parameter)."'";

                                    if($parameterList)
                                        $parameterList = substr(1, $parameterList);

                                    $procedurestatement = "
                                        CALL ".$request["data"]["name"]."(".$parameterList.")";

                                    $queryresult = $this->db->query($procedurestatement);

                                    $result = array();
                                    while($therecord = $this->db->fetchArray($queryresult))
                                        $result[] = $therecord;

                                    $this->_addToResponse("result",
                                                          "Procedure '".$request["data"]["name"]."' returned (".$this->db->numRows($queryresult).") in request number ".$i,
                                                          $result);

                                }//endif

                            }//endif

                        }//endif

                        break;

                    case "getsetting":
                        //======================================================
                        if(!is_array($request["data"]))
                            $this->sendError("Wrong passed data format, expected array in request number ".$i, $request["data"]);
                        else{

                            $whereclause = "";
                            foreach($request["data"] as $settingName)
                                $whereclause = "OR `name` = '".mysql_real_escape_string($settingName)."' ";

                            if($whereclause)
                                $whereclause = "WHERE ".substr($whereclause, 2);

                            $querystatement = "
                                SELECT
                                    `name`,
                                    `value`
                                FROM
                                    `settings`
                                ".$whereclause;

                            $queryresult = $this->db->query($querystatement);

                            $settings = array();

                            while($therecord = $this->db->fetchArray($queryresult))
                                $settings[$therecord["name"]] = $therecord["value"];

                            $this->_addToResponse("result",
                                                  "GetSettings returned (".count($settings).") in request number ".$i,
                                                  $settings);

                        }//endif

                        break;

                    default:
                        //======================================================
                        // a catch all for other requests.  This should correspond
                        // to an ovrriden search class function only. Calling
                        // some commands can cause response errors so be careful
                        if(!is_array($request["data"]) && !$hasTableClassOveride)
                                $this->sendError("Passaed data is not array or function (".$request["command"].") does not exist in request number ".$i, $request["data"]);
                        else {

                                @ include_once("modules/".$modulename."/include/".$maintable.".php");

                                $className = $maintable."SearchFunctions";

                                if(!class_exists($className))
                                    $this->sendError("Function (".$request["command"].") does not exist in request number ".$i, $request["data"]);
                                else{

                                    $processor = new $className($this->db, $tabledefid, $request["data"]);
                                    $processor->dateFormat =  $this->options->dateFormat;
                                    $processor->timeFormat =  $this->options->timeFormat;

                                    $methodName = $request["command"];

                                    if(!method_exists($processor, $methodName))
                                        $this->sendError("Function (".$request["command"].") does not exist in request number ".$i, $request["data"]);
                                    else {

                                        $result = $processor->$methodName();
                                        $this->_addToResponse($request["command"], $result);

                                    }//endif method_exists

                                }//endif $className

                        }//endif

                        break;

                }//endswitch $request["command"]

            }//endif $modulename

            $i++;

        }//endforeach

        $this->displayResult();

    }//end function process


    /**
     * function sendError
     * Logs error to response message and optionally exits
     *
     * Adds an entry of type error to the response array. Can also optionally
     * quit processing of the scrpt gracefully, and outputs the response.
     *
     * @param string $errorMessage message to log in response
     * @param var $dump any extra information to send back n the response - usually a dump of the offending variable
     * @param boolean $stop true/false whether to display response and stop exceution of the script
     */
    function sendError($errorMessage = "", $dump = "", $stop = false){

        $this->_addToResponse("error", $errorMessage, $dump);

        if($stop){

            //display the current result and exits;
            $this->displayResult();
            exit;

        }//endif

    }//end function sendError


    /**
     * function _addToResponse
     * adds an entry to the response array
     *
     * Adds a detailed entry to the response array.
     *
     * @param string $type the type of message being sent
     * @param string $message message to send
     * @param var $extras any extra information to send
     */
    function _addToResponse($type = "message", $message ="", $extras = ""){

        $response["type"] = $type;
        $response["messsage"] = $message;
        if($extras)
            $response["extras"] = $extras;

        $this->response[] = $response;

    }//end function _addToResponse


    /**
     * function displayResult
     * outputs formated response array
     *
     * Formats the response array for out put and then echos the result
     */
    function displayResult(){

        $response = $this->encode($this->response);

        switch($this->format){

            default:
                echo $response;
                break;

        }//endswitch

    }//end function displayError

}//end class api

?>
Return current item: PhpBMS