/** * Logs a bounce from bounce_detector.php for POSTed token, marks the send receipt * and the campaign member as bounced. * * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_bounce($handler_id, array $args, array &$data) { if (!array_key_exists('token', $_POST) || empty($_POST['token'])) { throw new midcom_error('Token not present in POST or empty'); } $messages = array(); $campaigns = array(); $this->_request_data['update_status'] = array('receipts' => array(), 'members' => array()); midcom::get('auth')->request_sudo('org.openpsa.directmarketing'); debug_add("Looking for token '{$_POST['token']}' in sent receipts"); $ret = $this->_qb_token_receipts($_POST['token']); debug_print_r("_qb_token_receipts({$_POST['token']}) returned", $ret); if (empty($ret)) { midcom::get('auth')->drop_sudo(); throw new midcom_error_notfound("No receipts with token '{$_POST['token']}' found"); } //While in theory we should have only one token lets use foreach just to be sure foreach ($ret as $receipt) { //Mark receipt as bounced debug_add("Found receipt #{$receipt->id}, marking bounced"); $receipt->bounced = time(); $this->_request_data['update_status']['receipts'][$receipt->guid] = $receipt->update(); //Mark member(s) as bounced (first get campaign trough message) if (!array_key_exists($receipt->message, $campaigns)) { $messages[$receipt->message] = new org_openpsa_directmarketing_campaign_message_dba($receipt->message); } $message =& $messages[$receipt->message]; if (!array_key_exists($message->campaign, $campaigns)) { $campaigns[$message->campaign] = new org_openpsa_directmarketing_campaign_dba($message->campaign); } $campaign =& $campaigns[$message->campaign]; debug_add("Receipt belongs to message '{$message->title}' (#{$message->id}) in campaign '{$campaign->title}' (#{$campaign->id})"); $qb2 = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb2->add_constraint('orgOpenpsaObtype', '=', org_openpsa_directmarketing_campaign_member_dba::NORMAL); //PONDER: or should be just mark the person bounced in ALL campaigns while we're at it ? //Just in case we somehow miss the campaign if (isset($campaign->id)) { $qb2->add_constraint('campaign', '=', $campaign->id); } $qb2->add_constraint('person', '=', $receipt->person); $ret2 = $qb2->execute(); if (empty($ret2)) { continue; } foreach ($ret2 as $member) { debug_add("Found member #{$member->id}, marking bounced"); $member->orgOpenpsaObtype = org_openpsa_directmarketing_campaign_member_dba::BOUNCED; $this->_request_data['update_status']['members'][$member->guid] = $member->update(); } } midcom::get('auth')->drop_sudo(); midcom::get()->skip_page_style = true; midcom::get('cache')->content->content_type('text/plain'); }
/** * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_all($handler_id, array $args, array &$data) { $this->_request_data['campaign'] = new org_openpsa_directmarketing_campaign_dba($args[0]); $this->_prepare_toolbar(); // List members who have been interviewed $qb = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb->add_constraint('campaign', '=', $this->_request_data['campaign']->id); $qb->begin_group("OR"); $qb->add_constraint('orgOpenpsaObtype', '=', org_openpsa_directmarketing_campaign_member_dba::INTERVIEWED); $qb->add_constraint('orgOpenpsaObtype', '=', org_openpsa_directmarketing_campaign_member_dba::UNSUBSCRIBED); $qb->end_group(); $this->_request_data['members_interviewed'] = $qb->execute(); $schemadb = midcom_helper_datamanager2_schema::load_database($this->_config->get('schemadb')); $this->_request_data['datamanager'] = new midcom_helper_datamanager2_datamanager($schemadb); }
/** * Phase for composing a message * * @param String $handler_id Name of the request handler * @param array $args Variable arguments * @param array &$data Public request data, passed by reference */ public function _handler_compose($handler_id, array $args, array &$data) { midcom::get('auth')->request_sudo(); //Load message $data['message'] = new org_openpsa_directmarketing_campaign_message_dba($args[0]); $data['campaign'] = new org_openpsa_directmarketing_campaign_dba($data['message']->campaign); $this->set_active_leaf('campaign_' . $data['campaign']->id); $this->_load_datamanager(); $this->_datamanager->autoset_storage($data['message']); $data['message_obj'] =& $data['message']; $data['message_dm'] =& $this->_datamanager; if ($handler_id === 'compose4person') { $data['person'] = new org_openpsa_contacts_person_dba($args[1]); $qb = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb->add_constraint('person', '=', $this->_request_data['person']->id); $memberships = $qb->execute(); if (empty($memberships)) { $data['member'] = new org_openpsa_directmarketing_campaign_member_dba(); $data['member']->person = $data['person']->id; $data['member']->campaign = $data['message']->campaign; } else { $data['member'] = $memberships[0]; } } $data['message_array'] = $this->_datamanager->get_content_raw(); if (!array_key_exists('content', $data['message_array'])) { throw new midcom_error('"content" not defined in schema'); } //Substyle handling @debug_add("\$data['message_array']['substyle']='{$data['message_array']['substyle']}'"); if (array_key_exists('substyle', $data['message_array']) && !empty($data['message_array']['substyle']) && !preg_match('/^builtin:/', $data['message_array']['substyle'])) { debug_add("Appending substyle {$data['message_array']['substyle']}"); midcom::get('style')->append_substyle($data['message_array']['substyle']); } //This isn't necessary for dynamic-loading, but is nice for "preview". midcom::get()->skip_page_style = true; debug_add('message type: ' . $data['message_obj']->orgOpenpsaObtype); switch ($data['message_obj']->orgOpenpsaObtype) { case org_openpsa_directmarketing_campaign_message_dba::EMAIL_TEXT: case org_openpsa_directmarketing_campaign_message_dba::SMS: debug_add('Forcing content type: text/plain'); midcom::get('cache')->content->content_type('text/plain'); break; //TODO: Other content type overrides ? } midcom::get('auth')->drop_sudo(); }
private function get_campaign_member_qb($kept = false) { $qb = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); if ($kept) { $qb->begin_group('OR'); $qb->add_constraint('metadata.revised', '>=', $this->get_deletion_timestamp()); $qb->add_constraint('orgOpenpsaObtype', '=', org_openpsa_directmarketing_campaign_member_dba::TESTER); $qb->add_constraint('campaign.orgOpenpsaObtype', '=', org_openpsa_directmarketing_campaign_dba::TYPE_SMART); $qb->end_group(); } else { $qb->add_constraint('metadata.revised', '<', $this->get_deletion_timestamp()); // Don't delete testers $qb->add_constraint('orgOpenpsaObtype', '<>', org_openpsa_directmarketing_campaign_member_dba::TESTER); // Don't delete from smart campaigns $qb->add_constraint('campaign.orgOpenpsaObtype', '<>', org_openpsa_directmarketing_campaign_dba::TYPE_SMART); } return $qb; }
/** * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_next($handler_id, array $args, array &$data) { $this->_request_data['campaign'] = new org_openpsa_directmarketing_campaign_dba($args[0]); $this->_prepare_toolbar(); // Figure out next person to call $qb = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb->add_constraint('campaign', '=', $this->_request_data['campaign']->id); $qb->add_constraint('orgOpenpsaObtype', '=', org_openpsa_directmarketing_campaign_member_dba::NORMAL); $qb->add_constraint('suspended', '<', time()); $qb->set_limit(1); $next_contact = $qb->execute(); if (count($next_contact) == 1) { $member =& $next_contact[0]; // Found, lock and redirect $member->orgOpenpsaObtype = org_openpsa_directmarketing_campaign_member_dba::LOCKED; if (!$member->update()) { throw new midcom_error('Failed to lock the interviewee'); } return new midcom_response_relocate(midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX) . "interview/{$member->guid}/"); } }
private function _get_campaign_data($first_send) { $this->_request_data['report']['campaign_data'] = array(); $campaign_data =& $this->_request_data['report']['campaign_data']; $campaign_data['unsubscribed'] = 0; $qb_unsub = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb_unsub->add_constraint('campaign', '=', $this->_request_data['message']->campaign); $qb_unsub->add_constraint('orgOpenpsaObtype', '=', org_openpsa_directmarketing_campaign_member_dba::UNSUBSCRIBED); $qb_unsub->add_constraint('metadata.revised', '>', date('Y-m-d H:i:s', $first_send)); $campaign_data['next_message'] = false; // Find "next message" and if present use its sendStarted as constraint for this query $qb_messages = org_openpsa_directmarketing_campaign_message_dba::new_query_builder(); $qb_messages->add_constraint('campaign', '=', $this->_request_data['message']->campaign); $qb_messages->add_constraint('id', '<>', $this->_request_data['message']->id); $qb_messages->add_constraint('sendStarted', '>', $first_send); $qb_messages->add_order('sendStarted', 'DESC'); $qb_messages->set_limit(1); $messages = $qb_messages->execute_unchecked(); if (is_array($messages) && isset($messages[0])) { $campaign_data['next_message'] = $messages[0]; $qb_unsub->add_constraint('metadata.revised', '<', date('Y-m-d H:i:s', $messages[0]->sendStarted)); } $campaign_data['unsubscribed'] = $qb_unsub->count_unchecked(); }
private function _import_subscribers_campaign_member($subscriber, $person) { // Check if person is already in campaign $member = null; $qb = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb->add_constraint('person', '=', $person->id); $qb->add_constraint('campaign', '=', $this->_request_data['campaign']->id); $qb->add_constraint('orgOpenpsaObtype', '<>', org_openpsa_directmarketing_campaign_member_dba::TESTER); $members = $qb->execute_unchecked(); if (count($members) > 0) { // User is or has been subscriber earlier, update status $member = $members[0]; // Fix http://trac.midgard-project.org/ticket/1112 if ($member->orgOpenpsaObtype == org_openpsa_directmarketing_campaign_member_dba::UNSUBSCRIBED) { // PONDER: Which code to use ?? //$this->_request_data['import_status']['failed_add']++; // PONDER: What is the difference between these two? $this->_request_data['import_status']['already_subscribed']++; $this->_request_data['import_status']['subscribed_existing']++; // PONDER: Should we skip any updates, they're usually redundant but ne never knows.. return $member; } else { if ($member->orgOpenpsaObtype == org_openpsa_directmarketing_campaign_member_dba::NORMAL) { // PONDER: What is the difference between these two? $this->_request_data['import_status']['already_subscribed']++; $this->_request_data['import_status']['subscribed_existing']++; } else { $member->orgOpenpsaObtype = org_openpsa_directmarketing_campaign_member_dba::NORMAL; if ($member->update()) { if (array_key_exists('person', $this->_request_data['new_objects'])) { $this->_request_data['import_status']['subscribed_new']++; } else { $this->_request_data['import_status']['subscribed_existing']++; } } else { $this->_request_data['import_status']['failed_add']++; return false; } } } } if (!$member) { // Not a subscribed member yet, add $member = new org_openpsa_directmarketing_campaign_member_dba(); $member->person = $person->id; $member->campaign = $this->_request_data['campaign']->id; $member->orgOpenpsaObtype = org_openpsa_directmarketing_campaign_member_dba::NORMAL; if (!$member->create()) { $this->_request_data['import_status']['failed_add']++; return false; } $this->_request_data['new_objects']['campaign_member'] =& $member; $this->_request_data['import_status']['subscribed_new']++; } if (!$this->_datamanager_process('campaign_member', $subscriber, $person)) { // Failed to handle campaign member via DM return false; } return $member; }
/** * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_csv($handler_id, array $args, array &$data) { $this->_prepare_handler($args); if (empty($args[1])) { debug_add('Filename part not specified in URL, generating'); //We do not have filename in URL, generate one and redirect $fname = preg_replace('/[^a-z0-9-]/i', '_', strtolower($this->_request_data['campaign']->title)) . '_' . date('Y-m-d') . '.csv'; return new midcom_response_relocate("campaign/export/csv/{$this->_request_data['campaign']->guid}/{$fname}"); } midcom::get()->disable_limits(); $this->_request_data['export_rows'] = array(); $qb_members = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb_members->add_constraint('campaign', '=', $this->_request_data['campaign']->id); $qb_members->add_constraint('orgOpenpsaObtype', '<>', org_openpsa_directmarketing_campaign_member_dba::TESTER); // PONDER: Filter by status (other than tester) ?? $qb_members->add_order('person.lastname', 'ASC'); $qb_members->add_order('person.firstname', 'ASC'); $members = $qb_members->execute_unchecked(); if (!is_array($members)) { throw new midcom_error('Fatal QB error'); } $this->membership_mode = $this->_config->get('csv_export_memberships'); foreach ($members as $k => $member) { if ($row = $this->_process_member($member)) { $this->_request_data['export_rows'][] = $row; } } $this->_load_datamanagers(); $this->_init_csv_variables(); midcom::get()->skip_page_style = true; midcom::get('cache')->content->content_type($this->_config->get('csv_export_content_type')); }
/** * Creates/Removes members for this smart campaign based on the rules array * NOTE: This is highly resource intensive for large campaigns * * @return boolean indicating success/failure */ function update_smart_campaign_members() { midcom::get()->disable_limits(); if (!$this->id) { debug_add('This campaign has no id (maybe not created yet?), aborting', MIDCOM_LOG_ERROR); return false; } if ($this->orgOpenpsaObtype != self::TYPE_SMART) { debug_add("This (id #{$this->id}) is not a smart campaign, aborting", MIDCOM_LOG_ERROR); return false; } midcom::get('auth')->request_sudo('org.openpsa.directmarketing'); $this->parameter('org.openpsa.directmarketing_smart_campaign', 'members_update_failed', ''); $this->parameter('org.openpsa.directmarketing_smart_campaign', 'members_update_started', time()); $solver = new org_openpsa_directmarketing_campaign_ruleresolver(); if (!$solver->resolve($this->rules)) { $this->parameter('org.openpsa.directmarketing_smart_campaign', 'members_update_failed', time()); debug_add('Failed to resolve rules', MIDCOM_LOG_ERROR); debug_print_r("this->rules has value:", $this->rules); midcom::get('auth')->drop_sudo(); return false; } //returns now the result array of collector instead array of objects of query builder $rule_persons = $solver->execute(); if (!is_array($rule_persons)) { $this->parameter('org.openpsa.directmarketing_smart_campaign', 'members_update_failed', time()); debug_add('Failure when executing rules based search', MIDCOM_LOG_ERROR); midcom::get('auth')->drop_sudo(); return false; } //Delete (normal) members that should not be here anymore $qb_unwanted = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb_unwanted->add_constraint('campaign', '=', $this->id); $qb_unwanted->add_constraint('orgOpenpsaObtype', '=', org_openpsa_directmarketing_campaign_member_dba::NORMAL); if (sizeof($rule_persons) > 0) { $qb_unwanted->add_constraint('person', 'NOT IN', array_keys($rule_persons)); } $uwret = $qb_unwanted->execute(); if (is_array($uwret) && !empty($uwret)) { foreach ($uwret as $member) { debug_add("Deleting unwanted member #{$member->id} (linked to person #{$member->person}) in campaign #{$this->id}"); if (!$member->delete()) { debug_add("Failed to delete unwanted member #{$member->id} (linked to person #{$member->person}) in campaign #{$this->id}, reason: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); } } } //List current non-tester members (including unsubscribed etc), and filter those out of rule_persons $qb_current = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb_current->add_constraint('campaign', '=', $this->id); if (sizeof($rule_persons) > 0) { $qb_current->add_constraint('person', 'IN', array_keys($rule_persons)); } $qb_current->add_constraint('orgOpenpsaObtype', '<>', org_openpsa_directmarketing_campaign_member_dba::TESTER); $cret = $qb_current->execute(); if (is_array($cret) && !empty($cret)) { foreach ($cret as $member) { //Filter the existing member from rule_persons (if present, for example unsubscribed members might not be) debug_add("Removing person #{$member->person} ({$rule_persons[$member->person]['lastname']}, {$rule_persons[$member->person]['firstname']}) from rule_persons list, already a member"); unset($rule_persons[$member->person]); } } //Finally, create members of each person matched by rule left reset($rule_persons); foreach ($rule_persons as $id => $person) { debug_add("Creating new member (linked to person #{$id}) to campaign #{$this->id}"); $member = new org_openpsa_directmarketing_campaign_member_dba(); $member->orgOpenpsaObtype = org_openpsa_directmarketing_campaign_member_dba::NORMAL; $member->campaign = $this->id; $member->person = $id; if (!$member->create()) { debug_add("Failed to create new member (linked to person #{$id}) in campaign #{$this->id}, reason: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); } } //All done, set last updated timestamp $this->parameter('org.openpsa.directmarketing_smart_campaign', 'members_updated', time()); midcom::get('auth')->drop_sudo(); return true; }
/** * Support for contacts person merge */ function org_openpsa_contacts_duplicates_merge_person(&$person1, &$person2, $mode) { switch ($mode) { case 'all': break; case 'future': // DirMar does not have future references so we have nothing to transfer... return true; break; default: // Mode not implemented debug_add("mode {$mode} not implemented", MIDCOM_LOG_ERROR); return false; break; } // Transfer links from classes we drive // ** Members ** $qb_member = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb_member->add_constraint('person', '=', $person2->id); $members = $qb_member->execute(); if ($members === false) { // Some error with QB debug_add('QB Error', MIDCOM_LOG_ERROR); return false; } // Transfer memberships foreach ($members as $member) { $member->person = $person1->id; if (!$member->_check_duplicate_membership()) { // This is a duplicate membership, delete it debug_add("Person #{$person1->id} is already member in campaign #{$member->campaign}, removing membership #{$member->id}", MIDCOM_LOG_INFO); if (!$member->delete()) { debug_add("Could not delete campaign member #{$member->id}, errstr: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); return false; } continue; } debug_add("Transferred campaign membership #{$member->id} to person #{$person1->id} (from #{$member->person})", MIDCOM_LOG_INFO); if (!$member->update()) { debug_add("Failed to update campaign member #{$member->id}, errstr: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); return false; } } // ** Receipts ** $qb_receipt = org_openpsa_directmarketing_campaign_messagereceipt_dba::new_query_builder(); $qb_receipt->add_constraint('person', '=', $person2->id); $receipts = $qb_receipt->execute(); if ($receipts === false) { // Some error with QB debug_add('QB Error / receipts', MIDCOM_LOG_ERROR); return false; } foreach ($receipts as $receipt) { debug_add("Transferred message_receipt #{$receipt->id} to person #{$person1->id} (from #{$receipt->person})", MIDCOM_LOG_INFO); $receipt->person = $person1->id; if (!$receipt->update()) { // Error updating debug_add("Failed to update receipt #{$receipt->id}, errstr: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); return false; } } // ** Logs ** $qb_log = org_openpsa_directmarketing_link_log_dba::new_query_builder(); $qb_log->add_constraint('person', '=', $person2->id); $logs = $qb_log->execute(); if ($logs === false) { // Some error with QB debug_add('QB Error / links', MIDCOM_LOG_ERROR); return false; } foreach ($logs as $log) { debug_add("Transferred link_log #{$log->id} to person #{$person1->id} (from #{$log->person})", MIDCOM_LOG_INFO); $log->person = $person1->id; if (!$log->update()) { // Error updating debug_add("Failed to update link #{$log->id}, errstr: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); return false; } } // Transfer metadata dependencies from classes that we drive $classes = array('org_openpsa_directmarketing_campaign_dba', 'org_openpsa_directmarketing_campaign_member_dba', 'org_openpsa_directmarketing_campaign_message_dba', 'org_openpsa_directmarketing_campaign_messagereceipt_dba', 'org_openpsa_directmarketing_link_log_dba'); foreach ($classes as $class) { // TODO: 1.8 metadata format support $ret = org_openpsa_contacts_duplicates_merge::person_metadata_dependencies_helper($class, $person1, $person2, $metadata_fields); if (!$ret) { // Failure updating metadata debug_add("Failed to update metadata dependencies in class {$class}, errsrtr: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); return false; } } // All done return true; }
private function &_callback_get_qb_send_sms() { $qb = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb->add_constraint('person.handphone', '<>', ''); return $qb; }
/** * Handle the request for unsubscribing all subscribers from a campaign * * @param String $handler_id Name of the request handler * @param array $args Variable arguments * @param array &$data Public request data, passed by reference */ public function _handler_unsubscribe_all($handler_id, array $args, array &$data) { midcom::get('auth')->request_sudo(); $this->_request_data['person'] = new org_openpsa_contacts_person_dba($args[0]); if ($handler_id === 'subscriber_unsubscribe_all_future') { $deny_type = strtolower($args[1]); $this->_request_data['person']->set_parameter('org.openpsa.directmarketing', "send_{$deny_type}_denied", '1'); } $this->_request_data['unsubscribe_status'] = true; $qb = org_openpsa_directmarketing_campaign_member_dba::new_query_builder(); $qb->add_constraint('campaign.node', '=', $this->_topic->id); $qb->add_constraint('person', '=', $this->_request_data['person']->id); // FIXME: Use NOT IN $qb->add_constraint('orgOpenpsaObtype', '<>', org_openpsa_directmarketing_campaign_member_dba::UNSUBSCRIBED); $qb->add_constraint('orgOpenpsaObtype', '<>', org_openpsa_directmarketing_campaign_member_dba::TESTER); $memberships = $qb->execute(); if ($memberships === false) { midcom::get('auth')->drop_sudo(); throw new midcom_error('Some error occurred with QB'); } foreach ($memberships as $member) { $member->orgOpenpsaObtype = org_openpsa_directmarketing_campaign_member_dba::UNSUBSCRIBED; $mret = $member->update(); if (!$mret) { //TODO: How to report failures of single rows when other succeed sensibly ?? $this->_request_data['unsubscribe_status'] = false; } } midcom::get('auth')->drop_sudo(); }