From 65941f96fdcbe6478f146e0b29d71761654364f5 Mon Sep 17 00:00:00 2001 From: Steve Sutton Date: Tue, 7 Feb 2017 15:20:39 -0500 Subject: [PATCH] Adding library for StreamSend Files from the app.gaslightmedia.com --- lib/StreamSend/DB/AbstractTable.php | 105 + lib/StreamSend/DB/IMapper.php | 7 + lib/StreamSend/DB/Member.php | 40 + lib/StreamSend/DB/MemberContact.php | 39 + lib/StreamSend/DB/MemberContactMapper.php | 159 ++ lib/StreamSend/DB/MemberMapper.php | 100 + lib/StreamSend/Exception.php | 9 + lib/StreamSend/Member.php | 349 ++++ lib/StreamSend/StreamSend.php | 442 +++++ lib/StreamSend/StreamSendAbstract.php | 70 + lib/StreamSend/class_streamsend_api.inc | 2160 +++++++++++++++++++++ models/admin/member/memberInfo.php | 3 + 12 files changed, 3483 insertions(+) create mode 100644 lib/StreamSend/DB/AbstractTable.php create mode 100644 lib/StreamSend/DB/IMapper.php create mode 100644 lib/StreamSend/DB/Member.php create mode 100644 lib/StreamSend/DB/MemberContact.php create mode 100644 lib/StreamSend/DB/MemberContactMapper.php create mode 100644 lib/StreamSend/DB/MemberMapper.php create mode 100644 lib/StreamSend/Exception.php create mode 100644 lib/StreamSend/Member.php create mode 100644 lib/StreamSend/StreamSend.php create mode 100644 lib/StreamSend/StreamSendAbstract.php create mode 100644 lib/StreamSend/class_streamsend_api.inc diff --git a/lib/StreamSend/DB/AbstractTable.php b/lib/StreamSend/DB/AbstractTable.php new file mode 100644 index 00000000..1ba0db96 --- /dev/null +++ b/lib/StreamSend/DB/AbstractTable.php @@ -0,0 +1,105 @@ +email_address; + } + + public function setEmail_address($email_address) { + $this->email_address = $email_address; + return $this; + } + + public function getFirst_name() { + return $this->first_name; + } + + public function setFirst_name($first_name) { + $this->first_name = $first_name; + return $this; + } + + public function getLast_name() { + return $this->last_name; + } + + public function setLast_name($last_name) { + $this->last_name = $last_name; + return $this; + } + + public function getCompany() { + return $this->company; + } + + public function setCompany($company) { + $this->company = $company; + return $this; + } + + public function getAddress1() { + return $this->address1; + } + + public function setAddress1($address1) { + $this->address1 = $address1; + return $this; + } + + public function getAddress2() { + return $this->address2; + } + + public function setAddress2($address2) { + $this->address2 = $address2; + return $this; + } + + public function getCity() { + return $this->city; + } + + public function setCity($city) { + $this->city = $city; + return $this; + } + + public function getStateprovince() { + return $this->stateprovince; + } + + public function setStateprovince($stateprovince) { + $this->stateprovince = $stateprovince; + return $this; + } + + public function getPostal_code() { + return $this->postal_code; + } + + public function setPostal_code($postal_code) { + $this->postal_code = $postal_code; + return $this; + } + + public function getPhone_number() { + return $this->phone_number; + } + + public function setPhone_number($phone_number) { + $this->phone_number = $phone_number; + return $this; + } +} + diff --git a/lib/StreamSend/DB/IMapper.php b/lib/StreamSend/DB/IMapper.php new file mode 100644 index 00000000..da1f8c66 --- /dev/null +++ b/lib/StreamSend/DB/IMapper.php @@ -0,0 +1,7 @@ +member_category; + } + + public function setMember_category($member_category) { + $this->member_category = $member_category; + return $this; + } + + public function getMember_type() { + return $this->member_type; + } + + public function setMember_type($member_type) { + $this->member_type = $member_type; + return $this; + } + + public function getActive() + { + return $this->active; + } + + public function setActive($active) + { + $this->active = $active; + return $this; + } + + +} + diff --git a/lib/StreamSend/DB/MemberContact.php b/lib/StreamSend/DB/MemberContact.php new file mode 100644 index 00000000..f06cbf7a --- /dev/null +++ b/lib/StreamSend/DB/MemberContact.php @@ -0,0 +1,39 @@ +member_category; + } + + public function setMember_category($member_category) { + $this->member_category = $member_category; + return $this; + } + + public function getMember_type() { + return $this->member_type; + } + + public function setMember_type($member_type) { + $this->member_type = $member_type; + return $this; + } + + public function getActive() + { + return $this->active; + } + + public function setActive($active) + { + $this->active = $active; + return $this; + } +} + diff --git a/lib/StreamSend/DB/MemberContactMapper.php b/lib/StreamSend/DB/MemberContactMapper.php new file mode 100644 index 00000000..a5e0c7aa --- /dev/null +++ b/lib/StreamSend/DB/MemberContactMapper.php @@ -0,0 +1,159 @@ +prepare($sql); + $sql = " + SELECT m.member_id, + case + when mc.send_mail <> true then false + when m.active <> true then false + when m.active = true and mc.send_mail = true then true + else false end as active, + m.member_name as company, + mc.email as email_address, + mc.fname as first_name, + mc.lname as last_name, + '' as address1, + '' as city, + '' as stateprovince, + '' as postal_code, + '' as phone_number, + 'Member Contact' as member_type + FROM member m + LEFT OUTER JOIN member_contacts mc + ON (m.member_id = mc.member_id) + WHERE mc.email != '' + AND mc.email is not null + -- AND mc.send_mail is true + AND m.member_id = :member_id"; + $stmt = $dbh->prepare($sql); + $stmt->bindParam( + ':member_id', + $id, + PDO::PARAM_INT + ); + $stmt->execute(); + while ($member = $stmt->fetch(PDO::FETCH_ASSOC)) { + // get member categories + // this will be used also for its member contacts + $memberCatsArray = array(); + $memberCategories->bindParam( + ':member_id', + $member['member_id'], + PDO::PARAM_INT + ); + $memberCategories->execute(); + while ($mcdRow = $memberCategories->fetch(PDO::FETCH_ASSOC)) { + if ($catNameString = $this->memberCategories[$mcdRow['category_id']]) { + $memberCatsArray[] = $catNameString; + } + } + + $member['member_category'] = implode('|', $memberCatsArray); + $memberContacts[] = $this->createFromArray($member); + } + return $memberContacts; + } catch(PDOException $e) { + Toolkit_Common::handleError($e); + } + return false; + } + public function findByEmail(PDO $dbh, $email) { + try { + $sql = " + SELECT category_id + FROM member_category + WHERE member_id = :member_id"; + $memberCategories = $dbh->prepare($sql); + $sql = " + SELECT m.member_id, + case + when mc.send_mail <> true then false + when m.active <> true then false + when m.active = true and mc.send_mail = true then true + else true end + as active, + m.member_name as company, + mc.email as email_address, + mc.fname as first_name, + mc.lname as last_name, + '' as address1, + '' as city, + '' as stateprovince, + '' as postal_code, + '' as phone_number, + 'Member Contact' as member_type + FROM member m + LEFT OUTER JOIN member_contacts mc + ON (m.member_id = mc.member_id) + WHERE mc.email != '' + AND mc.email is not null + AND m.process_email = :process_email"; + $stmt = $dbh->prepare($sql); + $stmt->bindParam( + ':process_email', + $email, + PDO::PARAM_INT + ); + $stmt->execute(); + $member = $stmt->fetch(PDO::FETCH_ASSOC); + // get member categories + // this will be used also for its member contacts + $memberCatsArray = array(); + $memberCategories->bindParam( + ':member_id', + $member['member_id'], + PDO::PARAM_INT + ); + $memberCategories->execute(); + while ($mcdRow = $memberCategories->fetch(PDO::FETCH_ASSOC)) { + if ($catNameString = $this->memberCategories[$mcdRow['category_id']]) { + $memberCatsArray[] = $catNameString; + } + } + + $member['member_category'] = implode('|', $memberCatsArray); + return $this->createFromArray($member); + } catch(PDOException $e) { + Toolkit_Common::handleError($e); + } + return false; + } + public function createFromArray($contact) { + $member = new StreamSend_DB_MemberContact(); + $member->setAddress1($contact['address1']) + ->setAddress2($contact['address2']) + ->setCity($contact['city']) + ->setCompany($contact['company']) + ->setEmail_address($contact['email_address']) + ->setFirst_name($contact['first_name']) + ->setLast_name($contact['last_name']) + ->setPhone_number($contact['phone_number']) + ->setPostal_code($contact['postal_code']) + ->setStateprovince($contact['stateprovince']) + ->setMember_category($contact['member_category']) + ->setMember_type($contact['member_type']) + ->setActive($contact['active']); + return $member; + } + public function setMemberCategories($categories) + { + $this->memberCategories = $categories; + } +} diff --git a/lib/StreamSend/DB/MemberMapper.php b/lib/StreamSend/DB/MemberMapper.php new file mode 100644 index 00000000..4a388800 --- /dev/null +++ b/lib/StreamSend/DB/MemberMapper.php @@ -0,0 +1,100 @@ + + */ +class StreamSend_DB_MemberMapper + implements IMapper +{ + protected $memberCategories = array(); + public function findByIdOrEmail( $args ) + { + if ( $args['email'] ) { + $email = $args['email']; + } + if ( $args['id'] ) { + $id = $args['id']; + } + if ( $email || $id ) { + throw new StreamSend_Exception( + 'Name or Id not given findByIdOrEmail' + ); + } + try { + $sql = " + SELECT category_id + FROM member_category + WHERE member_id = :member_id"; + $memberCategories = $dbh->prepare($sql); + $sql = " + SELECT member_id,active, + member_name as company, + process_email as email_address, + primary_contact_fname as first_name, + primary_contact_lname as last_name, + street as address1, + city.city_name as city, + state.state_abb as stateprovince, + zip as postal_code, + phone as phone_number, + 'Member' as member_type + FROM member + LEFT OUTER JOIN state + ON (member.state_id = state.state_id) + LEFT OUTER JOIN city + ON (member.city_id = city.city_id) + WHERE process_email != '' + AND process_email is not null + AND member_id = :member_id"; + $stmt = $dbh->prepare($sql); + $stmt->bindParam( ':member_id', $id, PDO::PARAM_INT ); + $stmt->execute(); + $member = $stmt->fetch(PDO::FETCH_ASSOC); + // get member categories + // this will be used also for its member contacts + $memberCatsArray = array(); + $memberCategories->bindParam( ':member_id', $member['member_id'], PDO::PARAM_INT ); + $memberCategories->execute(); + while ($mcdRow = $memberCategories->fetch(PDO::FETCH_ASSOC)) { + if ($catNameString = $this->memberCategories[$mcdRow['category_id']]) { + $memberCatsArray[] = $catNameString; + } + } + + $member['member_category'] = implode('|', $memberCatsArray); + return $this->createFromArray($member); + } catch(PDOException $e) { + Toolkit_Common::handleError($e); + } + return false; + } + public function createFromArray($contact) { + $member = new StreamSend_DB_Member(); + $member->setAddress1($contact['address1']) + ->setAddress2($contact['address2']) + ->setCity($contact['city']) + ->setCompany($contact['company']) + ->setEmail_address($contact['email_address']) + ->setFirst_name($contact['first_name']) + ->setLast_name($contact['last_name']) + ->setPhone_number($contact['phone_number']) + ->setPostal_code($contact['postal_code']) + ->setStateprovince($contact['stateprovince']) + ->setMember_category($contact['member_category']) + ->setMember_type($contact['member_type']) + ->setActive($contact['active']); + return $member; + } + public function setMemberCategories($categories) + { + $this->memberCategories = $categories; + } + public function setMemberCategoriesForImport() + { + foreach ($this->memberCategories as $catId => &$catName) { + $this->memberCategories[$catId] = str_replace("&", "&", $catName); + } + } +} diff --git a/lib/StreamSend/Exception.php b/lib/StreamSend/Exception.php new file mode 100644 index 00000000..dfd96231 --- /dev/null +++ b/lib/StreamSend/Exception.php @@ -0,0 +1,9 @@ +streamSend->contactSearch( + $contact->getEmail_address() + ); + $contactData = array( + 'email-address' => $contact->getEmail_address(), + 'first-name' => $contact->getFirst_name(), + 'last-name' => $contact->getLast_name(), + 'company' => $contact->getCompany(), + 'address1' => $contact->getAddress1(), + 'address2' => $contact->getAddress2(), + 'city' => $contact->getCity(), + 'stateprovince' => $contact->getStateprovince(), + 'postal-code' => $contact->getPostal_code(), + 'phone-number' => $contact->getPhone_number(), + 'member-category' => $contact->getMember_category(), + 'member-type' => $contact->getMember_type() + ); + if ($ret->contact) { + // if the contact is active then update it + if ($contact->getActive()) { + $contacts = $this->streamSend->contactUpdate( + $ret->contact->id, + $contactData + ); + } else { + $contacts = $this->streamSend->contactDelete( + $contact->getEmail_address() + ); + } + } else { + if ($contact->getActive()) { + $contacts = $this->streamSend->contactCreate( + $contactData, + STREAMSEND_DEFAULT_ACTIVATE, + STREAMSEND_DEFAULT_DELIVER_ACTIVATION, + STREAMSEND_DEFAULT_DELIVER_WELCOME + ); + } + } + if ($contact->getActive() && !$contacts) { + // show errors if on development server + switch ($_ENV['GLM_HOST_ID']) { + case "DEVELOPMENT": + echo "

A total and complete failure occured."; + break; + case "PRODUCTION": + break; + } + } + } + + public function cleanName($name) + { + $name = str_replace("&", "&", $name); + $name = str_replace("'", "'", $name); + $name = str_replace("\"", """, $name); + $name = str_replace("<", "<", $name); + $name = str_replace(">", ">", $name); + return $name; + } + + public function createFieldOption($fieldId, $name) + { + $name = $this->cleanName($name); + $ret = $this->streamSend->fieldOptionCreate($fieldId, $name); + if ($this->debug) { + echo $this->streamSend->debugBuffer; + } + return $ret->responseData->optionID; + } + + public function createMemberCategories(PDO $dbh) + { + $this->setMemberCategories($dbh); + if (is_array($this->memberCategories) + && is_array($this->memberCategories) + && !empty($this->memberCategories) + ) { + $this->streamSendDbMapper->setUpMemberCategory( + $this->memberCategories + ); + } + } + + public function createMemberTypes() + { + if (is_array($this->memberTypes) + && is_array($this->memberTypes) + && !empty($this->memberTypes) + ) { + $this->streamSendDbMapper->setUpMemberType( + $this->memberTypes + ); + } + } + + public function deleteFieldOption($fieldId, $optionId) + { + $this->streamSend->fieldOptionDelete($fieldId, $optionId); + if ($this->debug) { + echo $this->streamSend->debugBuffer; + } + } + + public function getMemberCategoryFieldId() + { + $fieldId = false; + foreach ($this->fieldsData as $id => $field) { + if ($field['name'] == 'Member Category') { + $fieldId = $id; + } + } + return $fieldId; + } + + public function getMemberTypeFieldId() + { + $fieldId = false; + foreach ($this->fieldsData as $id => $field) { + if ($field['name'] == 'Member Type') { + $fieldId = $id; + } + } + return $fieldId; + } + + public function importMembers($contacts) + { + $contactData = array(); + if (!is_array($contacts) || empty($contacts)) { + // nothing to do + return false; + } + foreach ($contacts as $contact) { + if (is_subclass_of($contact, 'StreamSend_DB_AbstractTable')) { + $contactData[] = array( + 'email_address' => $contact->getEmail_address(), + 'first_name' => $contact->getFirst_name(), + 'last_name' => $contact->getLast_name(), + 'company' => $contact->getCompany(), + 'address1' => $contact->getAddress1(), + 'address2' => $contact->getAddress2(), + 'city' => $contact->getCity(), + 'stateprovince' => $contact->getStateprovince(), + 'postal_code' => $contact->getPostal_code(), + 'phone_number' => $contact->getPhone_number(), + 'member_category' => $contact->getMember_category(), + 'member_type' => $contact->getMember_type() + ); + } + } + var_dump($contactData); + $ret = $this->streamSend->uploadContacts($contactData); + if ($this->debug) { + echo $this->streamSend->debugBuffer; + } + return $ret; + } + + public function initializeMemberStreamSend(PDO $dbh) + { + $this->setStreamSend(); + $this->getStreamSendFields(); + $this->setMemberCategories($dbh); + $this->setUpMemberCategory($this->memberCategories); + $this->setUpMemberType( + array( + 'Member', + 'Member Contact' + ) + ); + $fData = $this->getAllStreamSendFieldData(); + $fieldData = $fData->fieldsData; + $sql = " + SELECT parent_id + FROM category + WHERE category_id = :category_id"; + $getParentId = $dbh->prepare($sql); + $sql = " + INSERT INTO streamsend + (field_id, field_name, option_id, option_name, category_id, parent) + VALUES + (:field_id, :field_name, :option_id, :option_name, :category_id, :parent)"; + $stmt = $dbh->prepare($sql); + foreach ($fieldData as $fieldId => $field) { + if ( $field['name'] == 'Member Category' + && is_array($field['options']) + ) { + foreach ($field['options'] as $optionId => $fieldName) { + $fieldName = $this->cleanName($fieldName); + if ($categoryId = array_search($fieldName, $this->memberCategories)) { + // first get the parent id for the category + $getParentId->bindParam( + ':category_id', + $categoryId, + PDO::PARAM_INT + ); + $getParentId->execute(); + $parentId = $getParentId->fetchColumn(); + + // now insert into the streamsend table + $stmt->bindParam( ':field_id', $fieldId, PDO::PARAM_INT ); + $stmt->bindParam( ':field_name', $field['name'], PDO::PARAM_STR ); + $stmt->bindParam( ':option_id', $optionId, PDO::PARAM_INT ); + $stmt->bindParam( ':category_id', $categoryId, PDO::PARAM_INT ); + $stmt->bindParam( ':option_name', $fieldName, PDO::PARAM_STR ); + $stmt->bindParam( ':parent', $parentId, PDO::PARAM_INT ); + $stmt->execute(); + } else { + var_dump($fieldName); + throw new StreamSend_Exception( + 'Name not found in member categories' + ); + } + } + } + } + } + + public function setUpMemberCategory($options) + { + $fieldId = $this->getMemberCategoryFieldId(); + if (!$fieldId) { + $ret = $this->streamSend->fieldCreate( + 'Member Category', + STREAMSEND_API_FIELD_TYPE_CHECKBOX, + $options + ); + if (!$ret) { + echo $this->streamSend->debugBuffer; + } + } + } + + public function setUpMemberType($options) + { + $fieldId = $this->getMemberTypeFieldId(); + if (!$fieldId) { + $ret = $this->streamSend->fieldCreate( + 'Member Type', + STREAMSEND_API_FIELD_TYPE_CHECKBOX, + $options + ); + if ($this->debug) { + echo $this->streamSend->debugBuffer; + } + } + } + + public function setMemberCategories(PDO $dbh, $unClean = false) + { + static $categories = array(); + if (empty($categories)) { + // first get all member categories + // Members cannot belong to a main category so they have to be subs + try { + $catTree = Toolkit_Common::getHierarchicalTreeStructure( + $dbh, + 'category', + 'category_id', + 'parent_id', + 'name', + 0, + 3, + true + ); + if ($catTree && is_array($catTree) && !empty($catTree)) { + $sql = " + SELECT category_id,name + FROM category + WHERE category_id = :category_id"; + $stmt = $dbh->prepare($sql); + $baseName = ''; + foreach ($catTree as $catid => $level) { + if ($level == 1) { + $baseName = ''; + $stmt->bindParam(':category_id', $catid); + $stmt->execute(); + $row = $stmt->fetch(); + $baseName + = ($unClean) + ? $row['name'] + : $this->cleanName($row['name']); + } + if ($level == 2) { + $stmt->bindParam(':category_id', $catid); + $stmt->execute(); + $row = $stmt->fetch(); + $categoryName = $row['name']; + $categories[$row['category_id']] + = ($unClean) + ? $baseName . '/' . $categoryName + : $baseName . '/' . + $this->cleanName($categoryName); + } + if ($level == 3) { + $stmt->bindParam(':category_id', $catid); + $stmt->execute(); + $row = $stmt->fetch(); + $categoryName = $row['name']; + $categories[$row['category_id']] + = ($unClean) + ? $baseName . '/' . $categoryName + : $baseName . '/' . + $this->cleanName($categoryName); + } + } + } + } catch(PDOException $e) { + Toolkit_Common::handle_error($e); + } + } + $this->memberCategories = $categories; + } + + public function updateFieldOption($fieldId, $optionId, $name) + { + $name = $this->cleanName($name); + $this->streamSend->fieldOptionUpdate($fieldId, $optionId, $name); + if ($this->debug) { + echo $this->streamSend->debugBuffer; + } + } +} diff --git a/lib/StreamSend/StreamSend.php b/lib/StreamSend/StreamSend.php new file mode 100644 index 00000000..5c7eb6fb --- /dev/null +++ b/lib/StreamSend/StreamSend.php @@ -0,0 +1,442 @@ + + * @license Gaslight Media + * @link <> + */ + +/** + * Default parameters for contact create operations. + * Note that these are strings for use in XML data not true/false values. + * If false, the person will be created with a status of pending + */ +define('STREAMSEND_DEFAULT_ACTIVATE', 'true'); +/** + * If activate is false, setting this to true will trigger the sending of the built-in + * activation notification; if activate is true, this setting has no effect + */ +define('STREAMSEND_DEFAULT_DELIVER_ACTIVATION', 'false'); +/** + * If activate is true, setting this to true will trigger the sending of the built-in + * welcome notification; if activate is false, this setting has no effect + */ +define('STREAMSEND_DEFAULT_DELIVER_WELCOME', 'false'); + +require_once 'StreamSend/Member.php'; + +/** + * Toolkit_Members_StreamSend + * + * Description for Toolkit_Members_StreamSend + * + * @category Toolkit + * @package Members + * @author Jamie Kahgee + * @license Gaslight Media + * @link <> + */ +class Toolkit_Members_StreamSend +{ + /** + * Description of $streamSendMember + * @var StreamSend_Member() + * @access protected + */ + protected $streamSendMember; + + /** + * Class constructor + * + * @access public + */ + public function __construct() + { + $this->streamSendMember = new StreamSend_Member(); + } + + public function sendMember($member) + { + $this->streamSendMember->setStreamSend(); + //$this->streamSendMember->setMemberCategories($this->dbh); + $memberMapper = new StreamSend_DB_MemberMapper(); + $member = $memberMapper->createFromArray( + array( + 'address1' => $member['addr1'], + 'address2' => $member['addr2'], + 'city' => $member['city']['name'], + 'stateprovince' => $member['state']['value'], + 'postal_code' => $member['zip'], + 'email_address' => $member['email'], + 'phone_number' => $member['phone'], + 'active' => (($member['active']['value'] == 10)?'1':'0'), + 'member_type' => 'Member' + ) + ); + echo '

$member: ' . print_r( $member, true ) . '
'; + + } + /** + * Description for SendMemberById + * + * @param integer $memberId Member ID + * + * @return void + * @access public + */ + public function sendMemberById($memberId) + { + $this->streamSendMember->setStreamSend(); + $this->streamSendMember->setMemberCategories($this->dbh); + + $memberMapper = new StreamSend_DB_MemberMapper(); + $memberMapper->setMemberCategories( + $this->streamSendMember->memberCategories + ); + + $member = $memberMapper->findById($this->dbh, $memberId); + if ($member->getEmail_address()) { + $this->streamSendMember->addContactToStreamSend($member); + } + } + + /** + * Description for SendMemberContactsByMemberId + * + * @param int $memberId Member ID + * + * @return void + * @access public + */ + public function sendMemberContactsByMemberId($memberId) + { + $this->streamSendMember->debug = true; + $this->streamSendMember->setStreamSend(); + $this->streamSendMember->setMemberCategories($this->dbh); + + $memberContactMapper = new StreamSend_DB_MemberContactMapper(); + $memberContactMapper->setMemberCategories( + $this->streamSendMember->memberCategories + ); + $memberContacts = $memberContactMapper->findById( + $this->dbh, + $memberId + ); + foreach ($memberContacts as $contact) { + $this->streamSendMember->addContactToStreamSend($contact); + } + } + + /** + * Given a Member Category id and name update the streamsend option + * for the field 'Member Category' + * This also update the streamsend table with correct data + * if the category parent is 0 or the category is deleted streamsend table + * is updated and the field option is destroyed + * + * @param integer $categoryId category_id field table category + * + * @return void + */ + public function updateOptionByCategoryId($categoryId) + { + $this->streamSendMember->setStreamSend(); + $this->streamSendMember->setMemberCategories($this->dbh); + if (!is_numeric($categoryId)) { + throw new InvalidArgumentException( + 'CategoryId supplied must be numeric' + ); + } + try { + // for getting the categories parent name (needed for option name) + $sql = " + SELECT name + FROM category + WHERE category_id = :parent_id"; + $getParentId = $this->dbh->prepare($sql); + // get data from our cache table of member category options + $sql = " + SELECT * + FROM streamsend + WHERE category_id = :category_id"; + $stmt = $this->dbh->prepare($sql); + $stmt->bindParam( + ':category_id', + $categoryId, + PDO::PARAM_INT + ); + $stmt->execute(); + // $currentFieldData is from the streamsend table + $currentFieldData = $stmt->fetch(PDO::FETCH_ASSOC); + $sql = " + SELECT * + FROM category + WHERE category_id = :category_id"; + $stmt = $this->dbh->prepare($sql); + $stmt->bindParam( + ':category_id', + $categoryId, + PDO::PARAM_INT + ); + $stmt->execute(); + // $data will be the array from category table + $data = $stmt->fetch(PDO::FETCH_ASSOC); + if ($data['parent_id']) { + $getParentId->bindParam( + ':parent_id', + $data['parent_id'], + PDO::PARAM_INT + ); + $getParentId->execute(); + $parentName = $getParentId->fetchColumn(); + $newOptionName = $parentName . '/' . $data['name']; + $newOptionName = $this->streamSendMember->cleanName( + $newOptionName + ); + } + if ($currentFieldData) { + if (!$data['parent_id'] || $_REQUEST['delete']) { + $this->streamSendFieldOptionDelete( + $currentFieldData, + $categoryId + ); + return true; + } + if ($newOptionName != $currentFieldData['option_name']) { + // we'll need to see if the category is going to parent 0 + // if it is then it'll need to be deleted + // if not then it'll be updated + if ($data['parent_id']) { + $this->streamSendFieldOptionUpdate( + $currentFieldData['field_id'], + $currentFieldData['option_id'], + $newOptionName, + $categoryId + ); + } + } + } else { + if (!$_REQUEST['delete'] && $data['parent_id']) { + $this->streamSendFieldOptionCreate( + $data, + $newOptionName + ); + } + } + return true; + } catch(PDOException $e) { + Toolkit_Common::handleError($e); + } + } + + /** + * Create a new stream send field option + * gets the fieldId from feild_id in 'Member Category' (streamsend table) + * function must setup the dabase entry for field option and submit to + * streamsend the new field option + * + * @param array $data Array from the category table + * @param string $newOptionName option name 'parent/subcategory' + * + * @return boolean|void + * @throws InvalidArgumentException + * @access protected + */ + protected function streamSendFieldOptionCreate($data, $newOptionName) + { + if (!is_numeric($data['category_id'])) { + throw new InvalidArgumentException( + '$data["category_id"] must be numeric' + ); + } + if (!is_numeric($data['parent_id'])) { + throw new InvalidArgumentException( + '$data["parent_id"] must be numeric' + ); + } + if (!isset($newOptionName) || !strstr($newOptionName, '/')) { + throw new InvalidArgumentException( + 'Must have option name with a slash in it' + ); + } + $sql = " + SELECT field_id + FROM streamsend + WHERE field_name = 'Member Category' + LIMIT 1 + OFFSET 0"; + $fieldId = $this->dbh->query($sql)->fetchColumn(); + $fieldName = 'Member Category'; + // for a new field option create a record in cache table + $sql = " + INSERT INTO streamsend + (field_id, field_name, option_id, option_name, category_id, parent) + VALUES + (:field_id, :field_name, :option_id, :option_name, :category_id, :parent)"; + $stmt = $this->dbh->prepare($sql); + $optionId = $this->streamSendMember->createFieldOption( + $fieldId, + $newOptionName + ); + if (!$optionId) { + // if we can't find it here then look into the member category array + if (is_array($this->streamSendMember->memberCategories)) { + foreach ($this->streamSendMember->memberCategories as $optId => $name) { + if ($newOptionName == $name) { + $optionId = $optId; + continue; + } + } + } + } + if (!$optionId) { + return true; + } + // now insert into the streamsend table + $stmt->bindParam( + ':field_id', + $fieldId, + PDO::PARAM_INT + ); + $stmt->bindParam( + ':field_name', + $fieldName, + PDO::PARAM_STR + ); + $stmt->bindParam( + ':option_id', + $optionId, + PDO::PARAM_INT + ); + $stmt->bindParam( + ':category_id', + $data['category_id'], + PDO::PARAM_INT + ); + $stmt->bindParam( + ':option_name', + $newOptionName, + PDO::PARAM_STR + ); + $stmt->bindParam( + ':parent', + $data['parent_id'], + PDO::PARAM_INT + ); + $stmt->execute(); + } + + /** + * delete the field option from streamsend + * and delete the streamsend record for it + * This has to be done before the actual delete of the category + * or it won't find the field_id and option_id so it can be deleted + * from streamsend + * + * reamsend the new field option + * + * @param array $data Array from the category table + * @param string $categoryId Category ID + * + * @return void + * @throws InvalidArgumentException + * @access protected + */ + protected function streamSendFieldOptionDelete($data, $categoryId) + { + if (!is_numeric($data['field_id'])) { + throw new InvalidArgumentException( + '$data["field_id"] must be numeric' + ); + } + if (!is_numeric($data['option_id'])) { + throw new InvalidArgumentException( + '$data["option_id"] must be numeric' + ); + } + if (!is_numeric($categoryId)) { + throw new InvalidArgumentException( + '$categoryId must be numeric' + ); + } + $this->streamSendMember->deleteFieldOption( + $data['field_id'], + $data['option_id'] + ); + try { + $sql = " + DELETE FROM streamsend + WHERE category_id = :category_id"; + $stmt = $this->dbh->prepare($sql); + $stmt->bindParam( + ':category_id', + $categoryId, + PDO::PARAM_INT + ); + $stmt->execute(); + } catch(PDOException $e) { + Toolkit_Common::handleError($e); + } + } + + /** + * update the streamsend account with new name for an option + * and update our streamsend table with new name + * + * @param integer $fieldId field_id of streamsend field + * @param integer $optionId option_id of streamsend field + * @param string $newOptionName new name + * @param integer $categoryId category_id for streamsend table + * + * @return void + * @access protected + * @throws InvalidArgumentException + */ + protected function streamSendFieldOptionUpdate( $fieldId, $optionId, $newOptionName, $categoryId ) { + if (!is_numeric($fieldId)) { + throw new InvalidArgumentException( + '$fieldId must be numeric' + ); + } + if (!is_numeric($optionId)) { + throw new InvalidArgumentException( + '$optionId must be numeric' + ); + } + if (!isset($newOptionName) || !strstr($newOptionName, '/')) { + throw new InvalidArgumentException( + 'Must have option name with a slash in it' + ); + } + $this->streamSendMember->updateFieldOption( + $fieldId, + $optionId, + $newOptionName + ); + try { + $sql = " + UPDATE streamsend + SET option_name = :option_name + WHERE category_id = :category_id"; + $stmt = $this->dbh->prepare($sql); + $stmt->bindParam( + ':category_id', + $categoryId, + PDO::PARAM_INT + ); + $stmt->bindParam( + ':option_name', + $newOptionName, + PDO::PARAM_STR + ); + $stmt->execute(); + } catch(PDOException $e) { + Toolkit_Common::handleError($e); + } + } +} diff --git a/lib/StreamSend/StreamSendAbstract.php b/lib/StreamSend/StreamSendAbstract.php new file mode 100644 index 00000000..722d7198 --- /dev/null +++ b/lib/StreamSend/StreamSendAbstract.php @@ -0,0 +1,70 @@ + + * @license PHP Version 5.0 {@link http://www.php.net/license/3_0.txt} + */ +abstract class StreamSendAbstract +{ + public $debug = false; + + protected $fieldsData = array(); + protected $streamSend; + protected $streamSendDbMapper; + + const streamSendLoginId = STREAMSEND_LOGIN_ID; + const streamSendKey = STREAMSEND_KEY; + + abstract public function addContactToStreamSend( StreamSend_DB_AbstractTable $contact ); + + public function getStreamSendFields() + { + $fields = $this->streamSend->fieldsList(); + if (!$fields || $fields->responseStatus) { + return false; + } + foreach ($fields->responseData->field as $field) { + + $f_id = (int) $field->id; + + /** Add the field data to the plain fieldsData array */ + $this->fieldsData[$f_id] = array( + 'name' => ( (string) $field->name ), + 'data-type' => ( (string) $field->{'data-type'} ), + 'include-blank' => ( (string) $field->{'include-blank'} ), + 'allow-other' => ( (string) $field->{'allow-other'} ), + 'slug' => ( (string) $field->slug ) + ); + } + } + + public function getAllStreamSendFieldData() + { + return $this->streamSend->fieldsGetAll(); + } + + public function setStreamSend() + { + $this->streamSend = new streamSend( + STREAMSEND_BASE_URL, + self::streamSendLoginId, + self::streamSendKey + ); + $this->streamSend->debug = $this->debug; + } + + public function setStreamSendDBMapper($mapper) + { + $this->streamSendDbMapper = $mapper; + } +} diff --git a/lib/StreamSend/class_streamsend_api.inc b/lib/StreamSend/class_streamsend_api.inc new file mode 100644 index 00000000..7e4dd1e3 --- /dev/null +++ b/lib/StreamSend/class_streamsend_api.inc @@ -0,0 +1,2160 @@ + + * @copyright 2008 Gaslight Media - All rights Reserved + * @license Trade Secret and Proprietary Materials - No License + * @version CVS: $Id: class_streamsend_api.inc,v 1.2 2010/03/23 14:28:23 matrix Exp $ + * @link (none) + * @see + * @since File available since Release 1.0 + * @deprecated + */ + +/** +* Require DocBlock +* +* No includes required. +* +* PHP libcurl support required. +*/ + +/** + * ---------------------------------------------------------- + * Definitions + */ +define ("STREAMSEND_MAX_PAGE_SIZE", 10000); // This must be set to the maximum page size for the StreamSend system + +/** + * API Interface Error Codes + * + * Also add an error message for each error code + */ + /** Errors related to responses from StreamSend */ +define ("STREAMSEND_API_ERROR_NONE", 0); +define ("STREAMSEND_API_ERROR_NO_RESPONSE", 1); +define ("STREAMSEND_API_ERROR_HTTP_RESPONSE", 2); +define ("STREAMSEND_API_ERROR_BAD_XML", 3); + + /** Errors related to use of the GLM StreamSend methods and functions */ +define ("STREAMSEND_API_ERROR_POSTDATA_NOT_SUPPLIED", 100); + +/** + * API Interface Error Messages + * + * Requires an error code for each error message + */ +$streamsendErrorMessage = array ( + + /** Errors related to responses from StreamSend */ + STREAMSEND_API_ERROR_NONE => 'No error reported.', + STREAMSEND_API_ERROR_NO_RESPONSE => 'No response received from server.', + STREAMSEND_API_ERROR_HTTP_RESPONSE => 'Server replied with an unexpected HTTP response code.', + STREAMSEND_API_ERROR_BAD_XML => 'The XML response was malformed.', + + /** Errors related to use of the GLM StreamSend methods and functions */ + STREAMSEND_API_ERROR_POSTDATA_NOT_SUPPLIED => 'No POST data was supplied to send to StreamSend.', +); + +/** + * Field Types + */ +define("STREAMSEND_API_FIELD_TYPE_TEXT", 1); +define("STREAMSEND_API_FIELD_TYPE_TEXTAREA", 2); +define("STREAMSEND_API_FIELD_TYPE_DATETIME", 3); +define("STREAMSEND_API_FIELD_TYPE_DATE", 4); +define("STREAMSEND_API_FIELD_TYPE_TIME", 5); +define("STREAMSEND_API_FIELD_TYPE_SELECT", 6); +define("STREAMSEND_API_FIELD_TYPE_RADIO", 7); +define("STREAMSEND_API_FIELD_TYPE_CHECKBOX", 8); + +$streamsendFieldTypes = array ( + STREAMSEND_API_FIELD_TYPE_TEXT => array( 'Name' => 'Text Field', 'Options' => false ), + STREAMSEND_API_FIELD_TYPE_TEXTAREA => array( 'Name' => 'Text Area', 'Options' => false ), + STREAMSEND_API_FIELD_TYPE_DATETIME => array( 'Name' => 'Date/Time', 'Options' => false ), + STREAMSEND_API_FIELD_TYPE_DATE => array( 'Name' => 'Date', 'Options' => false ), + STREAMSEND_API_FIELD_TYPE_TIME => array( 'Name' => 'Time', 'Options' => false ), + STREAMSEND_API_FIELD_TYPE_SELECT => array( 'Name' => 'Select', 'Options' => true ), + STREAMSEND_API_FIELD_TYPE_RADIO => array( 'Name' => 'Radio', 'Options' => true ), + STREAMSEND_API_FIELD_TYPE_CHECKBOX => array( 'Name' => 'Checkbox', 'Options' => true ) +); + +/** + * Other General Setup Data + */ + +$HTTPResponseCodeText = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Permanently Moved', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Tempoorary Redirect', + 400 => 'Your browser sent a request that this server could not understand. XML included with a request may be malformed', + 401 => 'Authorization Required or StreamSend authentication error', + 402 => 'Payment Required, plan upgrade required, number of recipients for blast may exceed available quota for account', + 403 => 'Forbidden or permission not granted by StreamSend - Contact support staff', + 404 => 'Not Found - Possible reasons: The resource never existed, The resource was destroyed, The requested URI is invalid.', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'No code', + 426 => 'Upgrade Required', + 500 => 'Internal Server Error', + 501 => 'Method Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Temporarily Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 510 => 'Not Extended' +); + + +/** + * End of Definitions + * ---------------------------------------------------------- + */ + + + +/** + * StreamSend Class + */ + +class StreamSend { + + /** + * Field types in StreamSend + * + * This var is assigned the field type array in the constructor + * and is available for reference. + */ + var $streamsendFieldTypes; + + /** + * Clase Debug Status + * + * Set to true to enable debug output + */ + var $debug = false; + + /** + * Base URL at which the StreamSend API is located + */ + var $baseURL; + + /** + * Complete URL at which the specific StreamSend API method is located + */ + var $completeURL; + + /** + * Setting for CURLOPT_SSL_VERIFYPEER + * + * Defines whether we should verify the peer's certificate + * + * Normally should be set to "true". + */ + var $verifyPeer; + + /** + * Setting for CURLOPT_RETURNTRANSFER + * + * Defines if PHP curl_exec() should return a string or + * display the response directly to the user's browser. + * + * In all cases this should be "true" to return rather + * than display the response. + */ + var $returnTransfer; + + /** + * User name to use for authentication into StreamSend API + * + * The user name and password for the StreamSend API is generated + * via the StreamSend partner Web site. + */ + var $userName; + + /** + * User password to use for authentication into StreamSend API + */ + var $userPassword; + + /** + * XML Request + * + * This is detail of the request sent to the StreamSend API. + */ + var $xmlRequest = ''; + + /** + * Default Setting for CURLOPT_HTTPHEADER + * + * An array of HTTP header fields that is included in all requests. + * This is set in the GET, POST, MULTIPART case options in sendRequest(). + */ + var $httpHeader = false; + + /** + * Setting for CURLOPT_CUSTOMREQUEST + * + * The HTTP method to use for a request. + * For StreamSend API may be: "GET", "POST", "PUT", or "DELETE" + */ + var $requestMethod; + + /** + * Curl POST data + * + * Text to supply to StreamSend in a POST operation + * Set to false by default to detect when data is not supplied. + */ + var $postData = false; + + /** + * Currently select StreamSend Account + * + * A StreamSend Account equates to a Gaslight Media customer. + * All customer lists and blasts are contained within their account. + */ + var $currentAccount = NULL; + + /** + * Int - Last Curl Return Code + */ + var $curlError = NULL; + + /** + * String - Last Curl Return String + */ + var $curlErrorString = NULL; + + /** + * Boolean - Was last API request successful + */ + var $responseStatus = NULL; + + /** + * Numeric Error Code we assign to API failures + */ + var $responseError = NULL; + + /** + * Text error message describing $responseError + */ + var $responseErrorText = NULL; + + /** + * API Server HTTP Response Code + * + * The response HTTP response code from the last request to the StreamSend server. + * (i.e. 200, 404, ...) + */ + var $responseHTTPStatus = NULL; + + /** + * Text describing HTTP Response code + */ + var $responseHTTPStatusText = NULL; + + /** + * API XML Response + * + * The raw XML response from the last API request to the StreamSend server. + * $noXMLExpected tells the sendRequest() method to no bother trying to + * parse the response from StreamSend as XML. + */ + var $response = NULL; + var $noXMLExpected = false; + + /** + * Return headers in response from curl. + * + * If this is true, curl will include the response headers in the response. + */ + var $returnHeaders = false; + + /** + * Destination for parsed header data returned in a response when $returnHeaders is true + */ + var $parsedHeaders = false; + + /** + * Parsed API Response Data + * + * This is an array containing the parsed data from the XML response. + */ + var $responseData = NULL; + + /** + * Display a debug message if debug is enabled + */ + var $debugBuffer = ''; + + function debug($message) + { + if ($this->debug) + $this->debugBuffer .= '

StreamSend API Debug: '.$message.'
'; + } + + /** + * Parse headers into a key/value array + * + */ + function parseHeaders( $header ) + { + $retVal = array(); + + $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header)); + + foreach( $fields as $field ) { + + if( preg_match('/([^:]+): (.+)/m', $field, $match) ) { + + $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1]))); + + if( isset($retVal[$match[1]]) ) { + $retVal[$match[1]] = array($retVal[$match[1]], $match[2]); + } else { + $retVal[$match[1]] = trim($match[2]); + } + } + } + return $retVal; + } + + /** StreamSend + * Class construtor. Set up the default variables for the class. + * + * May be called with optional $debug parameter to set debug right away. + */ + function StreamSend($base_url, $user_name, $user_password, $debug = false ) + { + global $streamsendFieldTypes; + + $this->baseURL = $base_url; + $this->verifyPeer = true; + $this->returnTransfer = true; + $this->userName = $user_name; + $this->userPassword = $user_password; + $this->debug = $debug; + $this->streamsendFieldTypes = $streamsendFieldTypes; + + $this->debug("StreamSend() Constructor called with:
+ URL: $base_url
+ User Name: $user_name
+ User Password: $user_password
"); + } + + /** + * Sets and API error code along with the corresponding error description. + * + * If the error code is anything but 0 (successful), it must be a failure + */ + function setAPIError($error) + { + global $streamsendErrorMessage; + + $this->responseError = $error; + $this->responseErrorText = $streamsendErrorMessage[$error]; + $this->responseStatus = ( $error > 0 ); + } + + function sendRequest($method, $uri ) + { + global $HTTPResponseCodeText; + + $this->debug("sendRequest() called with:
$uri"); + + /** Build complete URL to specific StreamSend API method */ + $this->completeURL = $this->baseURL.'/'.$uri; + + /** Create curl object */ + $c = curl_init( ); + + /** Setup Curl Parameters */ + curl_setopt($c, CURLOPT_URL, $this->completeURL); + curl_setopt($c, CURLOPT_SSL_VERIFYPEER, $this->verifyPeer); + curl_setopt($c, CURLOPT_RETURNTRANSFER, $this->returnTransfer); + curl_setopt($c, CURLOPT_USERPWD, $this->userName.':'.$this->userPassword); + curl_setopt($c, CURLOPT_POST, false); + curl_setopt($c, CURLOPT_POSTFIELDS, NULL); + + /** set request method */ + switch ($method) { + case 'PUT' : + $this->httpHeader = array ( + 'Accept: application/xml', # any data returned should be XML + 'Content-Type: application/xml' # any data we send will be XML + ); + curl_setopt($c, CURLOPT_HTTPHEADER, $this->httpHeader); + curl_setopt($c, CURLOPT_CUSTOMREQUEST, 'PUT'); + curl_setopt($c, CURLOPT_POST, true); + + /** Check for post data */ + if( !$this->postData ) { + $this->setAPIError(STREAMSEND_API_ERROR_POSTDATA_NOT_SUPPLIED); + return(false); + } + + /** Provide the post data */ + curl_setopt($c, CURLOPT_POSTFIELDS, $this->postData); + break; + case 'GET': + $this->httpHeader = array ( + 'Accept: application/xml', # any data returned should be XML + 'Content-Type: application/xml' # any data we send will be XML + ); + curl_setopt($c, CURLOPT_HTTPHEADER, $this->httpHeader); + curl_setopt($c, CURLOPT_HTTPGET, true); + + break; + + case 'DELETE': + $this->httpHeader = array ( + 'Accept: application/xml' # any data returned should be XML + ); + curl_setopt($c, CURLOPT_HTTPHEADER, $this->httpHeader); + curl_setopt($c, CURLOPT_CUSTOMREQUEST, 'DELETE'); + + break; + + case 'POST': + + $this->httpHeader = array ( + 'Accept: application/xml', # any data returned should be XML + 'Content-Type: application/xml' # any data we send will be XML + ); + curl_setopt($c, CURLOPT_HTTPHEADER, $this->httpHeader); + curl_setopt($c, CURLOPT_POST, true); + + /** Check for post data */ + if( !$this->postData ) { + $this->setAPIError(STREAMSEND_API_ERROR_POSTDATA_NOT_SUPPLIED); + return(false); + } + + /** Provide the post data */ + curl_setopt($c, CURLOPT_POSTFIELDS, $this->postData); + break; + + case 'MULTIPART': + /** This method sends multipart/form-data for uploading files */ + + $this->httpHeader = array ( + 'Accept: application/xml', # any data returned should be XML + ); + curl_setopt($c, CURLOPT_HTTPHEADER, $this->httpHeader); + curl_setopt($c, CURLOPT_POST, true); + + /** Check for post data */ + if( !$this->postData ) { + $this->setAPIError(STREAMSEND_API_ERROR_POSTDATA_NOT_SUPPLIED); + return(false); + } + + /** Provide the post data */ + curl_setopt($c, CURLOPT_POSTFIELDS, $this->postData); + break; + + default: + curl_setopt($c, CURLOPT_CUSTOMREQUEST, $method); + break; + } + + /** Clear all response data */ + $this->setAPIError(STREAMSEND_API_ERROR_NONE); + $this->responseHTTPStatus = NULL; + $this->response = NULL; + $this->responseData = NULL; + + /** If requested, include the returned headers in the respone output */ + if( $this->returnHeaders ) + curl_setopt($c, CURLOPT_HEADER, true); + + /** + * Send the request to the StreamSend server. + * + * If there's a complete failure to talk with it, return false. + */ + if (!($this->response = curl_exec($c))) { + $this->setAPIError(STREAMSEND_API_ERROR_NO_RESPONSE); + $this->curlError = curl_errno($c); + $this->curlErrorString = curl_error($c); + $this->responseErrorText = curl_error($c); + $this->debug('sendRequest() CURL Error:
'.$this->responseErrorText); + return(clone $this); + } + + $this->debug('sendRequest() Call to curl_exec() completed
Full Response
'
+                    ."\n".htmlentities($this->response)."\n"
+                    .'
'); + + + /** + * We received some kind of response from the StreamSend server. + * + * Save the returned HTTP result status (i.e. 200, 404, etc) + */ + $this->responseHTTPStatus = curl_getinfo($c, CURLINFO_HTTP_CODE); + $this->debug('sendRequest() HTTP response code returned: '.$this->responseHTTPStatus); + + /** + * Check for if the HTTP result status indicates a problem + */ + $this->responseHTTPStatusText = $HTTPResponseCodeText[$this->responseHTTPStatus]; + if ($this->responseHTTPStatus > 299) { + $this->debug('sendRequest() Received unexpected HTTP response status ('.$this->responseHTTPStatus.').
'.$this->responseHTTPStatusText); + $this->setAPIError(STREAMSEND_API_ERROR_HTTP_RESPONSE); + } + + /** + * If we have a failed HTTP reponse then return now + */ + if ($this->responseError > 0) { + return( $this->responseError ); + } + + /** + * If there's no data returned, assume that's what was expected + * and return. + */ + $response = trim($this->response); + if (empty($response)) { + $this->debug('sendRequest() No response provided. Assuming it was not expected. Returning.'); + $this->setAPIError(STREAMSEND_API_ERROR_NONE); + return( $this->responseError ); + } + + /** + * If return headers were requested, parse them out into an array + */ + if( $this->returnHeaders ) + $this->parsedHeaders = $this->parseHeaders($response); + + /** If no XML is expected, return now */ + if( $this->noXMLExpected ) + return $this->responseError; + + /** + * Parse the XML response to a data array. + */ + $this->debug('sendRequest() Have text of a response. Trying to parse XML.'); + try { + $this->responseData = new SimpleXMLElement($response); + } catch (Exception $e) { + $this->setAPIError(STREAMSEND_API_ERROR_BAD_XML); + return( $this->responseError ); + } + $this->setAPIError(STREAMSEND_API_ERROR_NONE); + $this->debug('sendRequest() Result of parsed XML
'.htmlentities(print_r($this->responseData,true)).'

'); + + return( $this->responseError ); + } + + /***************************************************************************************************** + * + * STREAMSEND RESOURCE CATEGORIES + * + * The StreamSend API is divided into general categories of functionality. Below are groupings of + * methods intended to address GLM needs to interact with the StreamSend API under each category. + * + * System Configuration + * Audiences Top level grouping - There is an Audience ID for each account that doesn't change + * Fields Attributes that describe each contact who receives E-Mail from a blast + * Fields Options Available options for contact picklist fields + * Accounts Customer level functionality + * Users Users who have access to specific accounts + * + * Contacts and Contact Groupings + * People Individual contacts + * Lists Groups of contacts + * Memberships Links contacts to lists (subscriptions) + * Filters Means of grouping lists and filtering contacts for use in a blast + * + * E-Mail Blasts + * Emails E-Mails (newsletters) that may be sent + * Blasts An individual instance of E-Mail distribution + * Bounces Bounce reporting for a specific Blast + * Unsubscribes Unsubscribe requests resulting from a specific blast + * Clicks Instances of contacts clicking on a link in an E-Mail + * Views Instances of a contact viewing an E-Mail + * + *****************************************************************************************************/ + + + /** ---------------------------------- System Configuration Methods -------------------------------- */ + + + /** + * Audience Methods + * + * Audiences form the highest level grouping of people. Within each audience, people may be grouped + * into any number of lists, but audiences themselves are self-contained and mutually exclusive. + * Each StreamSend account has one audience, as StreamSend does not currently provide support for + * multiple audiences per account. This resource is provided for conceptual consistency and is + * limited to the index, show, and update actions. + * + * Each account had an Audience ID that doesn't change, but it's not the same for all accounts. This + * method can be used to determine the correct ID for an account. + * another request to the StreamSend API. + */ + + function audienceList() + { + $this->debug('audienceList() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences') === false) { + $this->debug('audienceList() Call to sendRequest() failed.'); + return( false ); + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('audienceList() Call to sendRequest() returned an error code.'); + } else { + $this->debug('audienceList() Received good response from sendRequest().'); + } + + return(clone $this); + } + + /** + * Fields Methods + * + * Fields are the various attributes that describe each person, e.g. first name, last name. + * Each account may have a unique set of fields. Be sure to maintain a standard set of + * fields to ensure compatibility with Gaslight Media applications. + */ + + function fieldCreate( $name, $type, $options = false, $include_blank = false, $allow_other = false ) + { + global $streamsendFieldTypes; + + $this->debug('fieldCreate() called to create field type: '.$streamsendFieldTypes[$type]['Name']); + + /** Make sure a valid name is supplied */ + if( ($name = trim($name)) == '') { + $this->debug('fieldCreate() Name not provided or invalid.'); + return false; + } + + /** Force type to be numerit and make sure the specified field type is valid. */ + $type = ($type + 0); + if( $type == 0 || !isset($streamsendFieldTypes[$type]) ) { + $this->debug('fieldCreate() Field type not provided or not a valid type number.'); + return false; + } + + /** If the field type requires options then make sure at least one was provided */ + if( $streamsendFieldTypes[$type]['Options'] && ( !is_array($options) || count($options) == 0 ) ) { + $this->debug('fieldCreate() Options not provided for field type that requires options.'); + return false; + } + + /** + * Build XML POST data string for supplied field + */ + $post_data = "\n"; + $post_data .= " $name\n" + ." ".$streamsendFieldTypes[$type]['Name']."\n" + ." ".($include_blank?'true':'false')."\n" + ." ".($allow_other?'true':'false')."\n"; + if( $streamsendFieldTypes[$type]['Options'] ) { + $post_data .= " \n"; + foreach( $options as $opt ) + $post_data .= " \n"; + $post_data .= " \n"; + } + $post_data .= "\n"; + + $this->postData = $post_data; + $this->debug('fieldCreate() XML POST request data
'
+                    ."\n".htmlentities($this->postData)."\n"
+                    .'
'); + + /** Tell sendRequest() to not bother parsing response as XML and to return response headers for parsing */ + $this->noXMLExpected = true; + $this->returnHeaders = true; + + /** This operation requires the "POST" Method */ + if ($this->sendRequest('POST', 'audiences/'.STREAMSEND_AUDIENCE.'/fields') === false) { + $this->debug('fieldCreate() Call to sendRequest() failed.'); + return false; + } + + /** Be sure to reset these parameters or other functions may have trouble */ + $this->noXMLExpected = false; + $this->returnHeaders = false; + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('fieldCreate() Call to sendRequest() returned an error code.'); + } else { + $this->debug('fieldCreate() Received good response from sendRequest().'); + } + + /** Did we get a 422 response? If so, it could be that the field already exists */ + if ($this->responseHTTPStatus != '201' ) { + $this->debug('fieldCreate() StreamSend returned a "422 Unprocessable Entry" response. Does the field already exist?'); + return false; + } + + /** We should have received a 201 response */ + if ($this->responseHTTPStatus != '201' ) { + $this->debug('fieldCreate() StreamSend did not return a "201 Created" response.'); + return false; + } + + /** Check for the field ID in the "Location:" header line */ + if( !isset($this->parsedHeaders['Location']) ) { + $this->debug('fieldCreate() StreamSend did not return a "Location:" header with the field ID.'); + return false; + } + + /** Get field ID from "Location" header and force it to a number, then check it's not 0 */ + if( ($field_id = substr( strrchr( $this->parsedHeaders['Location'], '/' ), 1 ) + 0) == 0 ) { + $this->debug('fieldCreate() StreamSend did not return a valid field ID. ('.$id.')'); + return false; + } + + $this->debug('fieldCreate() Have a valid field ID. ('.$field_id.')'); + + $this->responseData->fieldID = $field_id; + + return clone $this; + + } + + + function fieldsList() + { + $this->debug('fieldsList() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/fields') === false) { + $this->debug('fieldsList() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('fieldsList() Call to sendRequest() returned an error code.'); + } else { + $this->debug('fieldsList() Received good response from sendRequest().'); + } + + return(clone $this); + } + + /** + * Field Get + * + * Fields are the various attributes that describe each person, e.g. first name, last name. + * Each account may have a unique set of fields. Be sure to maintain a standard set of + * fields to ensure compatibility with Gaslight Media applications. + */ + + function fieldGet( $id ) + { + $this->debug('fieldGet() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/fields/'.$id) === false) { + $this->debug('fieldGet() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('fieldGet() Call to sendRequest() returned an error code.'); + } else { + $this->debug('fieldGet() Received good response from sendRequest().'); + } + + return(clone $this); + } + + function fieldOptionCreate($fieldId, $name) + { + global $streamsendFieldTypes; + + $this->debug('fieldOptionCreate() called to create field type: '.$streamsendFieldTypes[$type]['Name']); + + /** Make sure a valid name is supplied */ + if( ($name = trim($name)) == '') { + $this->debug('fieldOptionCreate() Name not provided or invalid.'); + return false; + } + + /** Force fieldId to be numeric and make sure the specified field type is valid. */ + $fieldId = ($fieldId + 0); + if( $fieldId == 0 || !is_numeric($fieldId) ) { + $this->debug('fieldOptionCreate() FieldId not provided or not a number.'); + return false; + } + + /** + * Build XML POST data string for supplied field + */ + $post_data = "\n"; + + $this->postData = $post_data; + $this->debug('fieldOptionCreate() XML POST request data
'
+                    ."\n".htmlentities($this->postData)."\n"
+                    .'
'); + + /** Tell sendRequest() to not bother parsing response as XML and to return response headers for parsing */ + $this->noXMLExpected = true; + $this->returnHeaders = true; + + /** This operation requires the "POST" Method */ + $postUrl = "audiences/".STREAMSEND_AUDIENCE."/fields/{$fieldId}/options"; + if ($this->sendRequest('POST', $postUrl) === false) { + $this->debug('fieldOptionCreate() Call to sendRequest() failed.'); + return false; + } + + /** Be sure to reset these parameters or other functions may have trouble */ + $this->noXMLExpected = false; + $this->returnHeaders = false; + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('fieldOptionCreate() Call to sendRequest() returned an error code.'); + } else { + $this->debug('fieldOptionCreate() Received good response from sendRequest().'); + } + + /** Did we get a 404 response? */ + if ($this->responseHTTPStatus == '404' ) { + $this->debug('fieldOptionCreate() StreamSend returned a "404 Page Not Found" response.'); + return false; + } + + /** Did we get a 422 response? If so, it could be that the field already exists */ + if ($this->responseHTTPStatus == '422' ) { + $this->debug('fieldOptionCreate() StreamSend returned a "422 Unprocessable Entry" response. Does the field already exist?'); + return false; + } + + /** We should have received a 201 response */ + if ($this->responseHTTPStatus != '201' ) { + $this->debug('fieldOptionCreate() StreamSend did not return a "201 Created" response.'); + return false; + } + + /** Check for the field ID in the "Location:" header line */ + if( !isset($this->parsedHeaders['Location']) ) { + $this->debug('fieldOptionCreate() StreamSend did not return a "Location:" header with the field ID.'); + return false; + } + + /** Get field ID from "Location" header and force it to a number, then check it's not 0 */ + if( ($optionId = substr( strrchr( $this->parsedHeaders['Location'], '/' ), 1 ) + 0) == 0 ) { + $this->debug('fieldOptionCreate() StreamSend did not return a valid option ID. ('.$id.')'); + return false; + } + + $this->debug('fieldOptionCreate() Have a valid field ID. ('.$optionId.')'); + + $this->responseData->optionID = $optionId; + + return clone $this; + } + + function fieldOptionDelete($fieldId, $optionId) + { + $this->debug('fieldOptionDelete() Called to delete Field Option '.$optionId ); + + /** Force fieldId to be numeric and make sure the specified field type is valid. */ + $fieldId = ($fieldId + 0); + if( $fieldId == 0 || !is_numeric($fieldId) ) { + $this->debug('fieldOptionDelete() FieldId not provided or not a number.'); + return false; + } + + /** Force optionId to be numeric and make sure the specified field type is valid. */ + $optionId = ($optionId + 0); + if( $optionId == 0 || !is_numeric($optionId) ) { + $this->debug('fieldOptionDelete() OptionId not provided or not a number.'); + return false; + } + + /** Expecting only an HTTP response code, so no XML parsing is required */ + $this->noXMLExpected = true; + + /** This operation requires the "DELETE" Method */ + $deleteUrl = "audiences/" + . STREAMSEND_AUDIENCE + . "/fields/{$fieldId}/options/{$optionId}/"; + if ($this->sendRequest('DELETE', $deleteUrl) === false) { + $this->debug('fieldOptionDelete() Call to sendRequest() failed.'); + return false; + } + + /** Be sure to reset these parameters or other functions may have trouble */ + $this->noXMLExpected = false; + + /** Process the two normally expected response codes related to deleting a contact */ + switch( $this->responseHTTPStatus ) { + + case '200': + $this->debug('fieldOptionDelete() Call to sendRequest() returned a 200 OK. Contact should be deleted.'); + return clone $this; + break; + + case '423': + $this->debug('fieldOptionDelete() Call to sendRequest() returned a 423 LOCKED. Contact cannot be deleted at this time.'); + return clone $this; + break; + + } + + /** We get here because an unexpected HTTP response code was recieved */ + $this->debug('fieldOptionDelete() Call to sendRequest() returned an unexpected code. Contact may not be deleted.'); + return false; + } + + /** + * Fields Options Methods + * + * Those fields that utilize enumerated values, e.g. Select, Radio, Checkbox, will present + * each person with a series of options from which they may choose. The available options + * for a given field constitute an ordered set onto which new options will be appended. + */ + + function fieldOptionsGet( $id ) + { + $this->debug('fieldOptionsGet() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/fields/'.$id.'/options') === false) { + $this->debug('fieldOptionsGet() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('fieldOptionsGet() Call to sendRequest() returned an error code.'); + } else { + $this->debug('fieldOptionsGet() Received good response from sendRequest().'); + } + + return(clone $this); + } + + /** + * To update a given option for a field. + * + * @global type $streamsendFieldTypes (why we still using globals?) + * + * @param type $fieldId + * @param type $optionId + * @param type $name + * + * @return type + */ + function fieldOptionUpdate($fieldId, $optionId, $name) + { + global $streamsendFieldTypes; + + $this->debug('fieldOptionUpdate() called to create field type: '.$streamsendFieldTypes[$type]['Name']); + + /** Make sure a valid name is supplied */ + if( ($name = trim($name)) == '') { + $this->debug('fieldOptionUpdate() Name not provided or invalid.'); + return false; + } + + /** Force fieldId to be numeric and make sure the specified field type is valid. */ + $fieldId = ($fieldId + 0); + if( $fieldId == 0 || !is_numeric($fieldId) ) { + $this->debug('fieldOptionUpdate() FieldId not provided or not a number.'); + return false; + } + + /** Force optionId to be numeric and make sure the specified field type is valid. */ + $optionId = ($optionId + 0); + if( $optionId == 0 || !is_numeric($optionId) ) { + $this->debug('fieldOptionUpdate() OptionId not provided or not a number.'); + return false; + } + + /** + * Build XML POST data string for supplied field + */ + $post_data = "\n"; + + $this->postData = $post_data; + $this->debug('fieldOptionUpdate() XML POST request data
'
+                    ."\n".htmlentities($this->postData)."\n"
+                    .'
'); + + /** This operation requires the "POST" Method */ + $putUrl = "audiences/".STREAMSEND_AUDIENCE."/fields/{$fieldId}/options/{$optionId}/"; + if ($this->sendRequest('PUT', $putUrl) === false) { + $this->debug('fieldOptionUpdate() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('fieldOptionUpdate() Call to sendRequest() returned an error code.'); + } else { + $this->debug('fieldOptionUpdate() Received good response from sendRequest().'); + } + + return clone $this; + } + + function fieldsGetAll() + { + $this->debug('fieldsGetAll() called'); + + if (!($fields = $this->fieldsList($field_id)) || $fields->responseStatus) { + $this->debug('fieldsGetAll() Call to fieldGet() failed.'); + return false; + } + + $this->fieldsData = array(); + + foreach ($fields->responseData->field as $field) { + + $f_id = (int) $field->id; + + /** Add the field data to the plain fieldsData array */ + $this->fieldsData[$f_id] = array( + 'name' => ( (string) $field->name ), + 'data-type' => ( (string) $field->{'data-type'} ), + 'include-blank' => ( (string) $field->{'include-blank'} ), + 'allow-other' => ( (string) $field->{'allow-other'} ), + 'slug' => ( (string) $field->slug ) + ); + + + switch( $field->{'data-type'} ) { + + /** These fields have no additional data */ + case 'Text Field': + case 'Text Area': + case 'Date/Time': + case 'Date': + case 'Time': + $this->fieldsData[$f_id]['options'] = false; + break; + + /** These fields have options associated with them */ + case 'Select': + case 'Radio': + case 'Checkbox': + + /** Get the options for this field - Don't fail completely here */ + if (!($options = $this->fieldOptionsGet($f_id)) || $options->responseStatus) { + $this->debug('fieldsGetAll() Call to fieldOptionsGet() failed.'); + $fields_data[$f_id]['Options'] = false; + break; + } + + + //$fields_data + foreach( $options->responseData->option as $option ) { + $this->fieldsData[$f_id]['options'][(int) $option->id] = (string) $option->name; + } + + break; + } + + } + + return(clone $this); + } + + /** + * Accounts Methods + * + * Resellers of StreamSend may create additional accounts underneath their parent account. + * The quota for child accounts constitutes a subset of the quota for the parent reseller account. + * + * Gaslight Media will have one account per customer or Web site. In some cases, customers may + * want to conduct E-Mail blasts for multiple related Web sites. + */ + function accountList() + { + $this->debug('accountList() called'); + + /** + * This operation requires the "GET" Method + * A false indicates that the server didn't respond at all. + */ + if ($this->sendRequest('GET', 'accounts') === false) { + $this->debug('accountList() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('accountList() Call to sendRequest() returned an error code.'); + } else { + $this->debug('accountList() Received good response from sendRequest().'); + } + + return(clone $this); + } + + function accountGet( $id ) + { + $this->debug('acccountGet() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'accounts/'.$id) === false) { + $this->debug('accountGet() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('accountGet() Call to sendRequest() returned an error code.'); + } else { + $this->debug('accoun tGet() Received good response from sendRequest().'); + } + + /** Stuff the results into a logical and consistent place to use by the calling program */ + $this->contact = $this->responseData; + + return clone $this; + } + + + + /** + * Destroy Account - USE WITH CAUTION!!! + * Seems to take the StreamSend servers some time to complete and may + * cause their systems to be unresponsive or require killing the cookie for + * the Web login to their account management interface and loging in again. + * + * This may have to be used to remove an account since it's not possible as far + * as I can tell to change the name on a sub-account and no way to delete it from + * the Web interface. This does totally remove it. + * + * @return unknown_type + */ + function accountDestroy( $id ) + { + $this->debug('accountDestroy() called'); + + /** + * This operation requires the "GET" Method + * A false indicates that the server didn't respond at all. + */ + if ($this->sendRequest('DELETE', 'accounts/'.$id) === false) { + $this->debug('accountDestroy() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('accountDestroy() Call to sendRequest() returned an error code.'); + } else { + $this->debug('accountDestroy() Received good response from sendRequest().'); + } + + return(clone $this); + } + + + + + + /** + * User Methods + * + * One may manage the users that have access to one�s account or, if one is a + * reseller, the users that have access to one�s resold accounts. + * + * Gaslight Media will normally have at least one user per account to permit + * the customer access to the StreamSend interface. + */ + + function userList() + { + $this->debug('userList() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'users') === false) { + $this->debug('userList() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('userList() Call to sendRequest() returned an error code.'); + } else { + $this->debug('userList() Received good response from sendRequest().'); + } + + return(clone $this); + } + + + /** ------------------------------ Contacts and Contact Grouping Methods --------------------------- */ + + /** + * People (contacts) Methods + * + * People are the recipients of mailings. + */ + + function contactList( $page = 1, $pagesize = 100 ) + { + $this->debug('contactList() called'); + + /** Check for and limit to the maximum page size **/ + if ($pagesize > STREAMSEND_MAX_PAGE_SIZE) { + $pagesize = STREAMSEND_MAX_PAGE_SIZE; + $this->debug('contactList() Maximum page size exceeded. Page size set to StreamSend maximum.'); + } + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/people?page='.$page.'&per_page='.$pagesize) === false) { + $this->debug('contactList() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('contactList() Call to sendRequest() returned an error code.'); + } else { + $this->debug('contactList() Received good response from sendRequest().'); + } + + /** Place list of "persons" in results as "contacts" */ + $this->contacts = $contacts->responseData->person; + + return clone $this; + } + + function contactSearch( $email ) + { + $this->debug('contactSearch() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/people?email_address='.$email) === false) { + $this->debug('contactSearch() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('contactSearch() Call to sendRequest() returned an error code.'); + } else { + $this->debug('contactSearch() Received good response from sendRequest().'); + } + + /** + * We now need to see if we actually got any contact data back + */ + $this->contact = false; + foreach ($this->responseData->person as $contact) { + + /** Check to see if there's more than one contact returned, which would be an error */ + if( $this->contact != false ) { + $this->debug('contactSearch() Received mulitple contact results. This should not happen.'); + return false; + } + + /** Stuff the results into a logical and consistent place to use by the calling program */ + $this->contact = $contact; + } + + return clone $this; + } + + function contactGet( $id ) + { + $this->debug('contactGet() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/people/'.$id) === false) { + $this->debug('contactGet() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('contactGet() Call to sendRequest() returned an error code.'); + } else { + $this->debug('contactGet() Received good response from sendRequest().'); + } + + /** Stuff the results into a logical and consistent place to use by the calling program */ + $this->contact = $this->responseData; + + return(clone $this); + } + + /** + * Create a Contact + * + * $contact is field/value array. + * + * The index of contactData is the field name slug (i.e. field name = "First Name", slug = "first_name" ) + * Slugs may only contain lower-case alphanumeric characters and "_". + * + * Minimum required fields include: email_address + * + * See setup file for explanation on other default values. + */ + + function contactCreate( $contact, + $activate = STREAMSEND_DEFAULT_ACTIVATE, + $deliver = STREAMSEND_DEFAULT_DELIVER_ACTIVATION, + $welcome = STREAMSEND_DEFAULT_DELIVER_WELCOME ) + { + $this->debug('contactCreate() called'); + + /** + * Check for minimum contact data + */ + if( !isset($contact['email-address']) || preg_match( "/^[A-Z0-9._%-]+@[A-Z0-9._%-]+\.[A-Z]{2,4}$/i", $contact['email-address'] ) == 0 ) { + $this->debug('contactCreate() E-Mail address not provided or not a valid E-Mail address.'); + return false; + } + + /** + * Build XML POST data string for supplied contact + */ + $post_data = "\n"; + while( list($k, $v) = each($contact) ) { + $v = str_replace("&", "&", $v); + $v = str_replace("'", "'", $v); + if (strstr($v, "|")) { + $mSect = explode("|", $v); + foreach ($mSect as $multiples) { + $post_data .= ' <'.trim($k).'>'.trim($multiples).'\n"; + } + } else { + $post_data .= ' <'.trim($k).'>'.trim($v).'\n"; + } + } + $post_data .= " $activate\n" + ." $deliver\n" + ." $welcome\n"; + $post_data .= "\n"; + $this->postData = $post_data; + $this->debug('contactCreate() XML POST request data
'
+                    ."\n".htmlentities($this->postData)."\n"
+                    .'
'); + + /** This operation requires the "POST" Method */ + if ($this->sendRequest('POST', 'audiences/'.STREAMSEND_AUDIENCE.'/people') === false) { + $this->debug('contactCreate() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('contactCreate() Call to sendRequest() returned an error code.'); + } else { + $this->debug('contactCreate() Received good response from sendRequest().'); + } + + return clone $this; + } + + function contactUpdate($id, $contact) + { + $this->debug('contactUpdate() called'); + + /** + * Check for minimum contact data + */ + if( !isset($contact['email-address']) || preg_match( "/^[A-Z0-9._%-]+@[A-Z0-9._%-]+\.[A-Z]{2,4}$/i", $contact['email-address'] ) == 0 ) { + $this->debug('contactUpdate() E-Mail address not provided or not a valid E-Mail address.'); + return false; + } + + /** + * Build XML POST data string for supplied contact + */ + $post_data = "\n"; + while( list($k, $v) = each($contact) ) { + $v = str_replace("&", "&", $v); + $v = str_replace("'", "'", $v); + if (strstr($v, "|")) { + $mSect = explode("|", $v); + foreach ($mSect as $multiples) { + $post_data .= ' <'.trim($k).'>'.trim($multiples).'\n"; + } + } else { + $post_data .= ' <'.trim($k).'>'.trim($v).'\n"; + } + } + $post_data .= "\n"; + $this->postData = $post_data; + $this->debug('contactUpdate() XML POST request data
'
+                    ."\n".htmlentities($this->postData)."\n"
+                    .'
'); + + /** This operation requires the "POST" Method */ + if ($this->sendRequest('PUT', 'audiences/'.STREAMSEND_AUDIENCE.'/people/'.$id) === false) { + $this->debug('contactUpdate() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + var_dump($this->responseError); + $this->debug('contactUpdate() Call to sendRequest() returned an error code.'); + } else { + $this->debug('contactUpdate() Received good response from sendRequest().'); + } + + return clone $this; + } + + /** + * Delete a Contact + * + * Requires a valid contact E-Mail address + */ + function contactDelete( $email ) + { + $this->debug('contactDelete() Called to delete E-Mail address: '.$email ); + + /** + * First find the contact to get the contact ID + */ + if (!($c = $this->contactSearch( $email ))) { + $this->debug('contactDelete() Call to sendRequest() failed.'); + return false; + } elseif ($c->responseStatus){ + $this->debug('contactDelete() Call to sendRequest returned an error code.'); + return false; + } + + if( $c->contact == false ) { + $this->debug('contactDelete() Search for specified E-Mail address failed. Does Contact exist?'); + return false; + } + + $id = $c->contact->id; + $this->debug('contactDelete() Have an ID for this contact: '.$id); + + /** Expecting only an HTTP response code, so no XML parsing is required */ + $this->noXMLExpected = true; + + /** This operation requires the "DELETE" Method */ + if ($this->sendRequest('DELETE', 'audiences/'.STREAMSEND_AUDIENCE.'/people/'.$id) === false) { + $this->debug('contactDelete() Call to sendRequest() failed.'); + return false; + } + + /** Be sure to reset these parameters or other functions may have trouble */ + $this->noXMLExpected = false; + + /** Process the two normally expected response codes related to deleting a contact */ + switch( $this->responseHTTPStatus ) { + + case '200': + $this->debug('uploadContacts() Call to sendRequest() returned a 200 OK. Contact should be deleted.'); + return clone $this; + break; + + case '423': + $this->debug('uploadContacts() Call to sendRequest() returned a 423 LOCKED. Contact cannot be deleted at this time.'); + return clone $this; + break; + + } + + /** We get here because an unexpected HTTP response code was recieved */ + $this->debug('uploadContacts() Call to sendRequest() returned an unexpected code. Contact may not be deleted.'); + return false; + } + + + /** + * Lists Methods + * + * Lists are groups of contacts. People (contacts) may be subscribed to multiple lists. + */ + + function listList() + { + $this->debug('listList() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/lists') === false) { + $this->debug('listList() Call to sendRequest() failed.'); + return( false ); + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('listList() Call to sendRequest() returned an error code.'); + } else { + $this->debug('listsList() Received good response from sendRequest().'); + } + + return(clone $this); + } + + + + /** + * Memberships (subscription) Methods + * + * Membership records track to which lists each person belongs. For any given list to + * which a person is subscribed, a membership record exists. The creation of a membership + * record linking a person and a list represents the act of joining that list. The + * destruction of a membership record represents the act of leaving that list. + */ + + /** Not written yet */ + + + /** + * Filters Methods + * + * Filters, along with lists, are one of two ways that the people in your audience + * can be grouped and manipulated. While lists are fairly static and unchanging, + * filters are completely dynamic. That is, no person ever "belongs" to a filter, + * as they might a list. Instead they are said to "match" a filter, and only when + * the filter is used does a clear group of people emerge. + */ + + /** Not written yet */ + + + /** -------------------------------------- E-Mail Blast Methods ------------------------------------ */ + + + /** + * Emails Methods + * + * Emails in StreamSend are those that have been saved for later use. They consist + * of some combination of HTML and plain text content and may be modified at any + * time without affecting any blasts that have used or will be using them. + */ + + /** Not written yet */ + + + /** + * Blasts Methods + * + * A blast is the act of sending an email to one or more people in your audience. Blasts + * may be sent immediately or at some specified date in the future. + */ + + function blastList() + { + $this->debug('blastList() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/blasts') === false) { + $this->debug('blastList() Call to sendRequest() failed.'); + return( false ); + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('blastList() Call to sendRequest() returned an error code.'); + } else { + $this->debug('blastList() Received good response from sendRequest().'); + } + + return(clone $this); + } + + function blastGet( $id ) + { + $this->debug('blastGet() called'); + + /** This operation requires the "GET" Method */ + if ($this->sendRequest('GET', 'audiences/'.STREAMSEND_AUDIENCE.'/blasts/'.$id) === false) { + $this->debug('blastGet() Call to sendRequest() failed.'); + return false; + } + + /** + * Check if the response request was successful + */ + if ($this->responseError > 0) { + $this->debug('blastGet() Call to sendRequest() returned an error code.'); + } else { + $this->debug('blastGet() Received good response from sendRequest().'); + } + + /** Stuff the results into a logical and consistent place to use by the calling program */ + $this->contact = $this->responseData; + + return clone $this; + } + + function monthlyStats( $month, $year ) + { + global $StreamSendAccounts; + + $this->debug('monthlyStats() called'); + + $total_accounts = $total_blasts = $total_sent = $total_delivered = $total_undelivered = $total_bounced = + $total_views = $total_clicks = $total_unsubscribes = $total_complaints = 0; + + /** + * Get list of all accounts + */ + if ( !($accounts = $this->accountList()) || $accounts->responseStatus ) { + $this->debug('monthlyStats() Unable to get account list'); + return false; + } + + /** Array that will contact all resulting data */ + $account_data = array(); + + /** + * For each account + */ + foreach ($accounts->responseData->account as $account) { + + $a_id = (int) $account->id; + $account_data[$a_id] = array( + 'name' => (string) $account->name, + 'quota' => (int) $account->quota, + 'soft-bounce-tolerance' => (int) $account->{'soft-bounce-tolerance'}, + 'automated-email-address' => (string) $account->{'automated-email-address'}, + 'owner' => (string) $account->owner->{'last-name'}.', '.$account->owner->{'first-name'}, + 'email-address' => (string) $account->owner->{'email-address'}, + 'blasts' => 0, + 'sent' => 0, + 'delivered' => 0, + 'undelivered' => 0, + 'bounced' => 0, + 'views' => 0, + 'clicks' => 0, + 'unsubscribes' => 0, + 'complaints' => 0 + ); + + $this->debug('monthlyStats() Processing account: '.$a_id); + + $total_accounts++; + + /** + * Get all blasts for the specified month + */ + + $account_data[$a_id]['blast_detail'] = array(); + + /** Set the account id */ + $this->currentAccount = $a_id; + $this->userName = $StreamSendAccounts[$a_id]['id']; + $this->userPassword = $StreamSendAccounts[$a_id]['key']; + + if (!($blasts = $this->blastList()) || $blasts->responseStatus ) { + $this->debug('monthlyStats() Unable to get blast list for account: '.$a_id); + $account_data[$a_id][$b_id] = false; + } else { + + foreach ($blasts->responseData->blast as $blast) { + + /** Get the scheduled date for this blast and break it up into parts */ + $date = getdate(strtotime( $blast->{'scheduled-for'} )); + + /** Is this blast scheduled for the requested month */ + if( $date['mon'] == $month && $date['year'] == $year ) { + + $account_data[$a_id]['blasts']++; + + $b_id = (int) $blast->id; + + $account_data[$a_id]['blast_detail'][$b_id] = array( + 'subject' => (string) $blast->subject, + 'email-address' => (string) $blast->from->{'email-address'}, + 'scheduled-for' => (string) $blast->{'scheduled-for'}, + 'completed-at' => (string) $blast->{'completed-at'}, + 'status' => (string) $blast->status, + 'sent' => 0 + ); + + /** + * Get blast detail + */ + + if (!($b = $this->blastGet($b_id)) || $b->responseStatus ){ + $this->debug('monthlyStats() Unable to get blast list for account: '.$a_id); + } else { + + $b_delivery = $b->responseData->statistics->delivery; + $b_activity = $b->responseData->statistics->activity; + + $account_data[$a_id]['blast_detail'][$b_id]['delivered'] = (int) $b_delivery->delivered; + $account_data[$a_id]['blast_detail'][$b_id]['undelivered'] = (int) $b_delivery->undelivered; + $account_data[$a_id]['blast_detail'][$b_id]['bounced'] = (int) $b_delivery->bounced; + $account_data[$a_id]['blast_detail'][$b_id]['views'] = (int) $b_activity->views; + $account_data[$a_id]['blast_detail'][$b_id]['clicks'] = (int) $b_activity->clicks; + $account_data[$a_id]['blast_detail'][$b_id]['unsubscribes'] = (int) $b_activity->unsubscribes; + $account_data[$a_id]['blast_detail'][$b_id]['complaints'] = (int) $b_activity->complaints; + + /** Calculate total number of E-Mails sent for this blast */ + $sent = (int) $b_delivery->delivered + (int) $b_delivery->undelivered + (int) $b_delivery->bounced; + $account_data[$a_id]['blast_detail'][$b_id]['sent'] = $sent; + + /** Add into account stats */ + $account_data[$a_id]['blasts']++; + $account_data[$a_id]['sent'] += $sent; + $account_data[$a_id]['delivered'] += (int) $b_delivery->delivered; + $account_data[$a_id]['undelivered'] += (int) $b_delivery->undelivered; + $account_data[$a_id]['bounced'] += (int) $b_delivery->bounced; + $account_data[$a_id]['views'] += (int) $b_activity->views; + $account_data[$a_id]['clicks'] += (int) $b_activity->clicks; + $account_data[$a_id]['unsubscribes'] += (int) $b_activity->unsubscribes; + $account_data[$a_id]['complaints'] += (int) $b_activity->complaints; + + /** Add into overall stats */ + $total_blasts++; + $total_sent += $sent; + $total_delivered += (int) $b_delivery->delivered; + $total_undelivered += (int) $b_delivery->undelivered; + $total_bounced += (int) $b_delivery->bounced; + $total_views += (int) $b_activity->views; + $total_clicks += (int) $b_activity->clicks; + $total_unsubscribes += (int) $b_activity->unsubscribes; + $total_complaints += (int) $b_activity->complaints; + + } // Have good blast detail + + } // Check date + + } // for each blast + + } // Have good list of blasts for account + + } // For each account + + /** Stuff the results into a logical and consistent place to use by the calling program */ + $this->stats = array( + 'total_blasts' => $total_blasts, + 'total_sent' => $total_sent, + 'total_delivered' => $total_delivered, + 'total_undelivered' => $total_undelivered, + 'total_bounced' => $total_bounced, + 'total_views' => $total_views, + 'total_clicks' => $total_clicks, + 'total_unsubscribes' => $total_unsubscribes, + 'total_complaints' => $total_complaints, + 'account_detail' => $account_data + ); + + return clone $this; + } + + + + /** + * Bounces Methods + * + * Bounces are those instances wherein certain people do not receive a particular blast. + * There are many reasons for this including an invalid email address, a full inbox, a + * problem with the receiving email server, etc. + */ + + /** Not written yet */ + + + /** + * Unsubcribes Methods + * + * Unsubscribes track two instances of people unsubscribing from one�s audience: manual + * and complaint-based. + */ + + /** Not written yet */ + + + /** + * Clicks Methods + * + * Clicks are those instances in which a person clicks on an external link in a particular + * message. Because clicks are tracked by modifying hyperlink URLs in the body of the message, + * clicks can only be registered for those viewing the HTML portion of the message and only + * for those URLs that are linked via an HTML hyperlink tag. + */ + + /** Not written yet */ + + + /** + * Views Methods + * + * Views are those instances in which a person views a particular message. Because views are + * tracked by way of a tracking image embedded in the message, views can only be registered + * for those viewing the HTML portion of the message and for whom the viewing of images is + * currently enabled in his or her email client. + */ + + /** Not written yet */ + + /** -------------------------------------- Upload and Import Methods ------------------------------------ */ + + + /** + * Upload Method + * + * Uploads a list of contacts and import into the contact list. + * + * $contacts is an array of contacts to upload. The array should be formatted as... + * + * $contacts = array( + * 0 => array( + * 'email_address' => '{email}', + * '{field_name}' => '{field_value}', + * '{field_name}' => '{field_value}', + * ... + * ), + * 1 => array( + * 'email_address' => '{email}', + * '{field_name}' => '{field_value}', + * '{field_name}' => '{field_value}', + * ... + * ), + * ... + * ); + * + * The contact data must have at least 'email_address', but can have additional + * fields that are setup for the account on the StreamSend side. The {field_name} + * for the addional field names must a "slug" for the actual name. Slugs are the + * field name but converted to only contain lower-case alphanumeric characters and "_". + * + * Also, all contact sub arrays must have exactly the same field names or the upload + * will be rejected. + * + * $reactivate re-starts the double opt-in process for the uploaded contacts that + * already exist in StreamSend. This is an optional parameter that defaults to false. + * + * $lists is a simple array of List ID's for Lists to which uploaded contacts should + * be added. This is an optional parameter. + * + */ + + function uploadContacts( $contacts, $reactivate = false, $lists = '' ) + { + $this->debug('uploadContacts() called'); + + /** + * Compile the contacts array into a text file for upload and send it to StreamSend. + * + * This is the first step in the import process. It should result in a valid + * upload ID that will then be used to import the contacts. + */ + + /** + * Check for minimum required data + * + * Was there a list of contacts supplied? + */ + if( !isset($contacts) || !is_array($contacts) || count($contacts) == 0 ) { + $this->debug('uploadContacts() A valid list of contacts was not provided.'); + return false; + } + + $this->debug('uploadContacts() '.count($contacts).' contacts provided.'); + + /** + * Get the list of valid fields for this account and convert to array with key = slug and value = id + */ + if( !($fields_list = $this->fieldsList()) ) { + $this->debug('uploadContacts() Call to fieldsList() failed.'); + return false; + } + + /** Email_address is not returned in fieldsList() but is required. Curiously it doesn't have an ID like the other fields */ + $available_fields = array( 'email_address' => 'email_address' ); + foreach( $fields_list->responseData->field as $field ) { + $available_fields[(string) $field->slug] = (string) $field->id; + } + + /** + * Build the text file for submission to StreamSend, "|" delimited, one contact per line. + * Also get the list of fields from the first contact and verify that all the rest are the same. + */ + $field_names = array(); + $upload_data = $contact_fields = $contact_data_header = $contact_data_header_sep = ''; + $numb_contacts = $numb_fields = 0; + $have_email_field = false; + + foreach( $contacts as $fields ) { + + $field = 0; + $contact_data = ''; + + /** If this is not the first contact, make sure we have the same number of fields as the first. */ + if( $numb_contacts > 0 && count($fields) != $numb_fields ) { + $this->debug('uploadContacts() Contact # '.($numb_contacts+1).' does not have the same number of fields as the first.'); + return false; + } + + /** For each field in this contact */ + while( list($k, $v) = each($fields) ) { + + /** If this is the first contact in the supplied array, save the field name */ + if( $numb_contacts == 0 ) { + /** Check for proper field name Slug */ + if( !isset($available_fields[$k]) ) { + $this->debug('uploadContacts() Improper field name Slug provided: '.$k); + return false; + } + + /** Add this field to our array of field names */ + $field_names[$field] = $k; + + /** Add this field to a header that will be sent as the first line in the contact data file */ + $contact_data_header .= $contact_data_header_sep.$k; + + /** Check if this is the email_address field. If so, say that we have it */ + if( $k == 'email_address' ) + $have_email_field = true; + + /** Compile list of contact fields for debug output */ + $contact_fields .= $k."\n"; + + } + else + /** Otherwise check to see if this is the expected field name */ + if( $field_names[$field] != $k ) { + $this->debug('uploadContacts() Fields for contact # '.($numb_contacts+1).' did not match first contact.'); + return false; + } + + /** Add field to contact, use "|" separator if it's not the first field */ + $contact_data .= ($field>0?"\t":'').$v; + + $contact_data_header_sep = "\t"; + + $field++; + + } // for each field + + /** If this is the first contact, save the number of expected fields, and set the header line for the file */ + if( $numb_contacts == 0 ) { + $numb_fields = $field; + $upload_data = "$contact_data_header\n"; + } + + /** Add this contact to the upload data */ + $upload_data .= "$contact_data\n"; + + $numb_contacts++; + + } // For each Contact + + /** Was the required email_address field supplied? */ + if( !$have_email_field ) { + $this->debug('uploadContacts() Required "email_address" field not supplied.'); + return false; + } + $this->debug('uploadContacts() Have required "email_address" field.'); + + $this->debug('uploadContacts() Contacts fields supplied...
'
+                    ."\n".htmlentities($contact_fields)
+                    .'
'); + + $this->debug('uploadContacts() Contacts data supplied...
'
+                    ."\n".htmlentities($upload_data)
+                    .'
'); + + /** Write the upload data to a file for Curl to send using multipart/form-data */ + $tmpname = tempnam("/tmp", "ss_"); + $h = fopen($tmpname, "w"); + fwrite($h, $upload_data); + fclose($h); + + /** Add file name of upload data to Post array */ + $this->postData = array( 'data' => "@$tmpname" ); + + /** Tell sendRequest() to not bother parsing response as XML and to return response headers for parsing */ + $this->noXMLExpected = true; + $this->returnHeaders = true; + + /** This operation requires the "MULTIPART" Method */ + if ($this->sendRequest('MULTIPART', 'uploads') === false) { + $this->debug('uploadContacts() Contacts upload call to sendRequest() failed.'); + return false; + } + + /** Be sure to reset these parameters or other functions may have trouble */ + $this->noXMLExpected = false; + $this->returnHeaders = false; + + /** Delete the temporary file */ + unlink($tmpname); + + /** We should have received a 201 response */ + if ($this->responseHTTPStatus != '201' ) { + $this->debug('uploadContacts() StreamSend did not return a "201 Created" response.'); + return false; + } + + /** Check for the upload ID in the "Location:" header line */ + if( !isset($this->parsedHeaders['Location']) ) { + $this->debug('uploadContacts() StreamSend did not return a "Location:" header with the upload ID.'); + return false; + } + + /** Get upload ID from "Location" header and force it to a number, then check it's not 0 */ + if( ($upload_id = substr( strrchr( $this->parsedHeaders['Location'], '/' ), 1 ) + 0) == 0 ) { + $this->debug('uploadContacts() StreamSend did not return a valid upload ID. ('.$id.')'); + return false; + } + $this->debug('uploadContacts() Have a valid upload ID. ('.$upload_id.')'); + + $this->responseData->uploadID = $upload_id; + + /** + * Tell StreamSend to import the contacts supplied in the upload above. + * + * This is the second step of the process. + */ + + /** Build XML POST data string for import - Note that fields other than email_address use the field IDs here. */ + $post_data = "\n" + ." ".($reactivate?'true':'false')."\n" + ." $lists\n" + ." $upload_id\n" + ." Tab\n" + ." \n"; + reset( $field_names ); + foreach( $field_names as $f ) { + $post_data .= " ".$available_fields[$f]."\n"; + } + $post_data .= " \n" + .""; + + $this->postData = $post_data; + $this->debug('uploadContacts() XML POST request data
'
+                    ."\n".htmlentities($this->postData)."\n"
+                    .'
'); + + /** This operation requires the "POST" Method */ + if ($this->sendRequest('POST', 'audiences/'.STREAMSEND_AUDIENCE.'/imports') === false) { + $this->debug('uploadContacts() Contact import call to sendRequest() failed.'); + return false; + } + + /** We should have received a 201 response */ + if ($this->responseHTTPStatus != '201' ) { + $this->debug('uploadContacts() StreamSend did not return a "201 Created" response.'); + return false; + } + + /** Check for the upload ID in the "Location:" header line */ + if( !isset($this->parsedHeaders['Location']) ) { + $this->debug('uploadContacts() StreamSend did not return a "Location:" header with import ID.'); + return false; + } + + /** Get upload ID from "Location" header and force it to a number, then check it's not 0 */ + if( ($import_id = substr( strrchr( $this->parsedHeaders['Location'], '/' ), 1 ) + 0) == 0 ) { + $this->debug('uploadContacts() StreamSend did not return a valid upload ID. ('.$id.')'); + return false; + } + $this->debug('uploadContacts() Have a valid import ID. ('.$import_id.')'); + + $this->responseData->importID = $import_id; + + return clone $this; + } + + +} + +?> diff --git a/models/admin/member/memberInfo.php b/models/admin/member/memberInfo.php index 3d30e939..5dbfa864 100644 --- a/models/admin/member/memberInfo.php +++ b/models/admin/member/memberInfo.php @@ -296,6 +296,9 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo // Process submission of a member information record update case 'submit': + $thisEntry = $this->getEntry( $this->memberInfoID ); + echo '

$thisEntry: ' . print_r( $thisEntry, true ) . '
'; + exit; // Check for new cities being submitted $this->checkNewCities(); -- 2.17.1