Location: PHPKode > projects > PhpBMS > phpbms/modules/mailchimp/include/list_sync.php
<?php
/*
 $Rev: 267 $ | $LastChangedBy: brieb $
 $LastChangedDate: 2007-08-14 13:08:27 -0600 (Tue, 14 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.    |
 |                                                                         |
 +-------------------------------------------------------------------------+
*/
class listSync{

    /**
      *  $lastSyncDate
      *  @var string The last date the list has been synced
      */
    var $lastSyncDate = NULL;
    /**
      *  $batchLimit
      *  @var int The size of individual "batches" of clients to be
      *  pulled/pushed.
      */
    var $batchLimit = 500;
    /**
      *   $listId
      *   @var string The list id of the MailChimp mailing list.
      */
    var $listId;

    /**
      *  $api
      *  @var object The MCAPI object.
      */
    var $api;
    /**
      *  $db
      *  @var object The phpbms database object.
      */
    var $db;
    /**
      *  $errors
      *  @var array An array of errors.  Each containing the 'message' and
      *  'code' fields.
      */
    var $errors = array();
    /**
      *  $stopScript
      *  @var bool Whether or not to contiune the process function's internal
      *  function calls (excepting error reporting)
      */
    var $stopScript = false;


    /**
      *  function listSync
      *  Instaciation function
      *  @param object $db The phpbms database object
      *  @param string $apiKey The MailChimp api key
      *  @param string $listId The MailChimp list id
      *  @param string $lastSyncDate The last date that the bms has synchronized
      *  with the MailChimp list
      *  @param integer $batchlimit The size of the individual batches
      *  @param bool $secure Whether to send info over ssl
      */

    function listSync($db, $apiKey, $listId, $lastSyncDate = NULL, $batchlimit = NULL, $secure = false){

        $this->db = $db;
        $this->listId = $listId;

        /**
          *   Check for a valid datetime format?
          */
        if($lastSyncDate)
            $this->lastSyncDate = $lastSyncDate;

        if((int)$batchlimit > 0)
           $this->batchlimit = (int)$batchlimit;

        $this->api = new MCAPI($apiKey, NULL, $secure);
        $this->api->ping();
        if($this->api->errorCode){
            $this->_addError("Unable to successfully ping(): ".$this->api->errorMessage, $this->api->errorCode, true);
            return false;
        }//end if

        /**
          *   check to see if there is a uuid field
          */
        $hasUuid = false;
        $mergeVars = $this->api->listMergeVars($this->listId);
        if($this->api->errorCode){
            $this->_addError("Unable to access list: ".$this->api->errorMessage, $this->api->errorCode, true);
            return false;
        }//end if

        foreach($mergeVars as $mergeVar){

            if($mergeVar["tag"] == "UUID"){
                $hasUuid = true;
                break;
            }//end if

        }//end foreach

        if(!$hasUuid){
            $this->_addError("The list does not have a merge variable with tag of 'UUID'.", NULL, true);
            return false;
        }

    }//end function

    /**
      *   function _addError
      *
      *   Add an error to the class' error array.
      *
      *   @param string $message Text of error message
      *   @param int $errorCode Error number
      *   @param bool $fatal Whether or not the error is fatal, and if the process
      *   function needs to be stopped.
      */

    function _addError($message, $errorCode = NULL, $fatal = false){

        $tempArray["message"] = $message;
        $tempArray["code"] = $errorCode;

        if($fatal){
            $this->stopScript = true;
            $tempArray["errorType"] = "error";
        }else{
            $tempArray["errorType"] = "warning";
        }//end if

        $this->errors[] = $tempArray;

    }//end function


    /**
      *   function _reportResult
      *
      *   Reports the result of the process function.
      *   @return array Result of the process function.
      *   @returnf string type Type of result (either 'error' or 'success')
      *   @returnf array details The details of the result.  Only relevant for
      *   returns of type 'error'.  It is an array of error message and error
      *   code pairs.
      */

    function _reportResult(){

        $return = array();

        if(count($this->errors)){

            if($this->stopScript)
                $return["type"] = "error";
            else
                $return["type"] = "warning";

            $return["details"] = $this->errors;

        }else{

            $return["type"] = "success";
            $return["details"] = array();

        }//end if

        return $return;

    }//end foreach


    /**
      *  function pullChanges
      *
      *  Pull any changes from the Mailchimp side.  Usually this means people
      *  who have unsubscribed and people who cannot be reached via the email
      *  address.
      */

    function pullChanges(){

        /**
          *  pull all the unsubscribed
          */
        $unsubscribed = array();
        $start = 0;
        do{

            $members = $this->api->listMembers($this->listId, 'unsubscribed', $this->lastSyncDate, $start, $this->batchLimit);
            if($this->api->errorCode){
                $this->_addError("Unable to load listMembers(): ".$this->api->errorMessage, $this->api->errorCode, true);
                return false;
            }//end if

            foreach($members as $member){

                $info = $this->api->listMemberInfo($this->listId, $member["email"]);
                if($this->api->errorCode){
                    $this->_addError("Unable to load listMemberInfo(): ".$this->api->errorMessage, $this->api->errorCode, true);
                    return false;
                }//end if

                $unsubscribed[] = $info["merges"]["UUID"];

            }//end foreach

            $start++;
        }while(count($members) == $this->batchLimit);

        /**
          *  pull all the cleaned
          */
        $start = 0;
        do{

            $members = $this->api->listMembers($this->listId, 'cleaned', $this->lastSyncDate, $start, $this->batchLimit);
            if($this->api->errorCode){
                $this->_addError("Unable to load listMembers(): ".$this->api->errorMessage, $this->api->errorCode, true);
                return false;
            }//end ifd

            foreach($members as $member){

                $info = $this->api->listMemberInfo($this->listId, $member["email"]);
                if($this->api->errorCode){
                    $this->_addError("Unable to load listMemberInfo(): ".$this->api->errorMessage, $this->api->errorCode, true);
                    return false;
                }//end if
                $unsubscribed[] = $info["merges"]["UUID"];

            }//end foreach

            $start++;
        }while(count($members) == $this->batchLimit);


        /**
          *  If there are records to unsubscribe, set their `canemail` to '0'
          */
        if(count($unsubscribed)){

            /**
              *  construct the in statement
              */
            $inValues = "";
            foreach($unsubscribed as $uuid)
                $inValues .= ",'".$uuid."'";

            $inValues = substr($inValues, 1);

            /**
              *  set the cleaned/unsubscribed to canemail = 0
              */
            $querystatement = "
                UPDATE
                    `clients`
                SET
                    `canemail` = '0'
                WHERE
                    `uuid` IN (".$inValues.")
            ";

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

        }//end if

    }//end function

    /**
      *   function unsubscribeInvalid
      *
      *   Unsubscribe the emails that are related to
      *   client records those that don't have an email (or don't exist),
      *   or that can't be emailed anymore.
      */

    function unsubscribeInvalid(){

        /**
          *   Get rid of the temorary table.
          */
        $dropTableStatement = "
            DROP TABLE IF EXISTS
                `tempEmail`
        ";
        $this->db->query($dropTableStatement);

        /**
          *  Create a temporary table
          */
        $createTableStatement = "
            CREATE TEMPORARY TABLE
                `tempEmail`
            (
                `email` varchar(128) default NULL
            ) ENGINE=INNODB";

        $this->db->query($createTableStatement);

        /**
          *  pull all the subscribed
          */
        $start = 0;
        do{
            $valuesClause = "";

            $members = $this->api->listMembers($this->listId, 'subscribed', NULL, $start, $this->batchLimit);

            if($this->api->errorCode){
                $this->_addError("Unable to load listMemberInfo():".$this->api->errorMessage, $this->api->errorCode, true);
                return false;
            }//end if

            foreach($members as $member)
                $valuesClause .= ",('".$member["email"]."')";

            $valuesClause = substr($valuesClause, 1);


            /**
              *  Put the subscribed into a temporary table
              */
            if($valuesClause){
                $insertStatement = "
                    INSERT INTO
                        `tempEmail`
                    (`email`)
                        VALUES
                    ".$valuesClause;

                $this->db->query($insertStatement);
            }//end if

            $start++;
        }while(count($members) == $this->batchLimit);


        /**
          *  Get all the emails of client records that are subscribed
          *  but should not be subscribed.
          */
        $selectStatement = "
            SELECT DISTINCT
                `tempEmail`.`email`
            FROM
                (`tempEmail` LEFT JOIN `clients` ON `tempEmail`.`email` = `clients`.`email`)
            WHERE
                `clients`.`canemail` = '0'
                OR
                `clients`.`email` IS NULL
        ";

        $selectresult = $this->db->query($selectStatement);

        /**
          *   Unsubscribe them from the mailchimp list
          */
        $unsubscribeList = array();
        while($therecord = $this->db->fetchArray($selectresult))
            $unsubscribeList[] = $therecord["email"];

        /**
          *  If there are records to unsubscribe (deleted), do so.
          */
        if(count($unsubscribeList)){

            $return = $this->api->listBatchUnsubscribe($this->listId, $unsubscribeList, true, false);
            if ($this->api->errorCode){
                $this->_addError("Batch Unsubscibe Failed!: ".$this->api->errorMessage, $this->api->errorCode, true);
                return false;
            }else
                foreach($return['errors'] as $val)
                    $this->_addError("Unsubscribing email ".$val["email"]." failed: ".$val["message"], $val["code"]);

        }//end if

        /**
          *   Get rid of the temorary table.
          */
        $dropTableStatement = "
            DROP TABLE IF EXISTS
                `tempEmail`
        ";
        $this->db->query($dropTableStatement);

    }//end function


    /**
      *  function pushChanges
      *
      *  Push changes from any client record that has been changed
      *  since the last sync date.
      */
    function pushChanges(){

        /**
          *   Get the changed records / fields
          */
        $querystatement = "
            SELECT
                `email`,
                `uuid`,
                `firstname`,
                `lastname`,
                `company`,
                `type`
            FROM
                `clients`
            WHERE
                `email` IS NOT NULL
                AND
                `email` != ''
                AND
                `canemail` != '0'
        ";

        /**
          *  If there is a last sync *date*, limit the records by their modified
          *  by date.
          */
        if($this->lastSyncDate)
            $querystatement .= " AND `modifieddate` > '".$this->lastSyncDate."'";


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

        /**
          *   Format the variables to be interpreted by Mailchimp.
          */
        $batchVars = array();
        while($therecord = $this->db->fetchArray($queryresult)){

            $tempArray["EMAIL"] = $therecord["email"];
            $tempArray["FNAME"] = $therecord["firstname"];
            $tempArray["LNAME"] = $therecord["lastname"];
            $tempArray["COMPANY"] = $therecord["company"];
            $tempArray["TYPE"] = $therecord["type"];
            $tempArray["UUID"] = $therecord["uuid"];

            $batchVars[] = $tempArray;
        }//end while

        /**
          *  Update / Insert the changes
          */
        if(count($batchVars)){

            $return = $this->api->listBatchSubscribe($this->listId, $batchVars, false, true);
            if($this->api->errorCode){
                $this->_addError("Batch Subscribe failed: ".$this->api->errorMessage, $this->api->errorCode, true);
                return false;
            }else{

                $instatement = "";

                foreach($return['errors'] as $val){

                    $this->_addError("Subscribing or updating uuid '".$val["row"]["UUID"]."' failed: ".$val["message"], $val["code"]);

                    $memberInfo = $this->api->listMemberInfo($this->listId, $val["row"]["EMAIL"]);

                    if($memberInfo["status"] != "subcribed")
                        $instatement .= ", '".$val["row"]["UUID"]."'";

                }//end foreach

                if($instatement){

                    $instatement = substr($instatement, 2);
                    $instatement = "(".$instatement.")";

                    $updatestatement = "
                        UPDATE
                            `clients`
                        SET
                            `canemail` = '0',
                            `comments` = CONCAT(IF(`comments` IS NOT NULL, `comments`, ''), '\n[', NOW(), '] The `canemail` field of this record has been unchecked by the mailchimp list sync')
                        WHERE
                            `uuid` IN ".$instatement;

                    $this->db->query($updatestatement);

                }//end if

            }//end if

        }//end if

    }//end function


    /*
     * function resetSyncDate
     *
     * Set the last sync date in the settings table to be NOW()
     */

    function resetSyncDate() {

        $querystatement = "
            UPDATE
                `settings`
            SET
                `value` = NOW()
            WHERE
                `name` = 'mailchimp_last_sync_date'
        ";

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

    }//end if


    /**
      *  function process
      *
      *  Perform the list sync
      */
    function process(){

        if(!$this->stopScript)
            $this->pullChanges();
        if(!$this->stopScript)
            $this->unsubscribeInvalid();
        if(!$this->stopScript)
            $this->pushChanges();
        if(!$this->stopScript)
            $this->resetSyncDate();

        return $this->_reportResult();

    }//end function

}//end class
?>
Return current item: PhpBMS