2
/* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
3
// +----------------------------------------------------------------------+
4
// | Eventum - Issue Tracking System |
5
// +----------------------------------------------------------------------+
6
// | Copyright (c) 2003 - 2008 MySQL AB |
7
// | Copyright (c) 2008 - 2010 Sun Microsystem Inc. |
8
// | Copyright (c) 2011 - 2013 Eventum Team. |
10
// | This program is free software; you can redistribute it and/or modify |
11
// | it under the terms of the GNU General Public License as published by |
12
// | the Free Software Foundation; either version 2 of the License, or |
13
// | (at your option) any later version. |
15
// | This program is distributed in the hope that it will be useful, |
16
// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18
// | GNU General Public License for more details. |
20
// | You should have received a copy of the GNU General Public License |
21
// | along with this program; if not, write to: |
23
// | Free Software Foundation, Inc. |
24
// | 59 Temple Place - Suite 330 |
25
// | Boston, MA 02111-1307, USA. |
26
// +----------------------------------------------------------------------+
27
// | Authors: Bryan Alsdorf <bryan@mysql.com> |
28
// +----------------------------------------------------------------------+
33
* Example customer backend. This does not cover all functionality, but should provide an idea
34
* on how to implement a backend.
36
* @author Bryan Alsdorf <bryan@mysql.com>
38
class Example_Customer_Backend extends Abstract_Customer_Backend
40
// array of customer data used for this example
44
* Overide the connect method to populate a variable instead of connecting to a database
51
"customer_name" => "Bryan's widget factory",
52
"start_date" => '2004-03-10',
53
"expiration_date" => '2010-03-10',
57
'first_name' => 'Bryan',
58
'last_name' => 'Alsdorf',
59
'email' => 'bryan@example.com',
60
'phone' => '+1 (123) 456-7890'
64
'first_name' => 'Bob',
65
'last_name' => 'Smith',
66
'email' => 'bob@example.com',
67
'phone' => '+1 (123) 456-7890'
70
"address" => "1234 Blah Street,\nHouston, TX 12345",
71
"support_level_id" => 1,
72
"account_manager" => array("Sales guy", "Salesguy@example.com")
76
"customer_name" => "Joao, Inc.",
77
"start_date" => '2004-08-01',
78
"expiration_date" => '2005-08-01',
82
'first_name' => 'Joao',
83
'last_name' => 'Prado Maia',
84
'email' => 'jpm@example.com',
85
'phone' => '+1 (123) 456-7890'
88
"address" => "123 Fake Street,\nSpringfield, USA",
89
"support_level_id" => 3,
90
"account_manager" => array("Sales guy", "Salesguy@example.com")
94
"customer_name" => "Example Corp.",
95
"start_date" => '2002-01-01',
96
"expiration_date" => '2006-01-01',
101
'last_name' => 'Man',
102
'email' => 'j-man@example.com',
103
'phone' => '+1 (123) 456-7890'
107
'first_name' => 'John',
108
'last_name' => 'Doe',
109
'email' => 'John.Doe@example.com',
110
'phone' => '+1 (123) 456-7890'
113
"address" => "56789 Some drive,\nFooo, Foo 12345",
114
"support_level_id" => 4,
115
"account_manager" => array("Sales guy", "Salesguy@example.com")
122
* Returns the name of the backend
124
* @return string The name of the backend
133
* Returns true if the backend uses support levels, false otherwise
136
* @return boolean True if the project uses support levels.
138
function usesSupportLevels()
140
// this example will use support levels so override parent method
146
* Returns the contract status associated with the given customer ID.
147
* Possible return values are 'active', 'in_grace_period' and 'expired'.
150
* @param integer $customer_id The customer ID
151
* @return string The contract status
153
function getContractStatus($customer_id)
155
// active contracts have an expiration date in the future
156
$expiration = strtotime($this->data[$customer_id]['expiration_date']);
157
$now = Date_Helper::getCurrentUnixTimestampGMT();
158
if ($expiration > $now) {
160
} elseif ($expiration > ($now + (DAY * $this->getExpirationOffset()))) {
161
return 'in_grace_period';
169
* Retrieves the customer titles associated with the given list of issues.
172
* @param array $result The list of issues
173
* @see Search::getListing()
175
function getCustomerTitlesByIssues(&$result)
177
if (count($result) > 0) {
178
for ($i = 0; $i < count($result); $i++) {
179
if (!empty($result[$i]["iss_customer_id"])) {
180
$result[$i]["customer_title"] = $this->getTitle($result[$i]["iss_customer_id"]);
188
* Retrieves the support levels associated with the given list of issues.
191
* @param array $result The list of issues
192
* @see Search::getListing()
194
function getSupportLevelsByIssues(&$result)
196
if (count($result) > 0) {
197
$support_levels = $this->getSupportLevelAssocList();
198
for ($i = 0; $i < count($result); $i++) {
199
if (!empty($result[$i]["iss_customer_id"])) {
200
$result[$i]["support_level"] = @$support_levels[$this->getSupportLevelID($result[$i]['iss_customer_id'])];
208
* Method used to get the details of the given customer.
211
* @param integer $customer_id The customer ID
212
* @param boolean $force_refresh If the cache should not be used.
213
* @param integer $contract_id The contract ID
214
* @return array The customer details
216
function getDetails($customer_id, $force_refresh = false, $contract_id = false)
218
$support_levels = $this->getSupportLevelAssocList();
219
$details = $this->data[$customer_id];
220
$details["support_level"] = $support_levels[$details["support_level_id"]];
221
$details["contract_status"] = $this->getContractStatus($customer_id);
222
$details["note"] = Customer::getNoteDetailsByCustomer($customer_id);
228
// This example does not implement per-incident
229
// support so those methods will not be included here
233
* Returns a list of customers (companies) in the customer database.
236
* @return array An associated array of customers.
238
function getAssocList()
241
foreach ($this->data as $id => $details) {
242
$assoc[$id] = $details["customer_name"];
249
* Method used to get the customer names for the given customer id.
252
* @param integer $customer_id The customer ID
253
* @return string The customer name
255
function getTitle($customer_id)
257
return $this->data[$customer_id]["customer_name"];
262
* Method used to get an associative array of the customer names
263
* for the given list of customer ids.
266
* @param array $customer_ids The list of customers
267
* @return array The associative array of customer id => customer name
269
function getTitles($customer_ids)
272
foreach ($this->data as $id => $details) {
273
if (in_array($id, $customer_ids)) {
274
$assoc[$id] = $details["customer_name"];
282
* Method used to get the list of email addresses associated with the
283
* contacts of a given customer.
286
* @param integer $customer_id The customer ID
287
* @return array The list of email addresses
289
function getContactEmailAssocList($customer_id)
292
foreach ($this->data[$customer_id]['contacts'] as $key => $contact) {
293
$assoc[] = $contact["email"];
300
* Method used to get the customer and customer contact IDs associated
301
* with a given list of email addresses.
304
* @param array $emails The list of email addresses
305
* @return array The customer and customer contact ID
307
function getCustomerIDByEmails($emails)
310
foreach ($this->data as $company_id => $details) {
311
foreach ($details['contacts'] as $contact) {
312
// in a perfect world you would want to do partial searches
313
// here, but as an example in_array() will do
314
if (in_array($contact["email"], $emails)) {
315
return array($company_id, $contact['contact_id']);
324
* Method used to get the overall statistics of issues in the system for a
328
* @param integer $customer_id The customer ID
329
* @return array The customer related issue statistics
331
function getOverallStats($customer_id)
333
// don't count customer notes, emails, phone calls
338
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
340
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "status
344
iss_customer_id=$customer_id";
345
$res = DB_Helper::getInstance()->getAssoc($stmt);
346
if ((PEAR::isError($res)) || (empty($res)) || (count($res) == 0)) {
351
'total_persons' => 0,
354
'average_first_response' => 0,
361
foreach ($res as $issue_id => $status) {
362
$issues[] = $issue_id;
363
if (empty($status)) {
371
// get the list of distinct persons from the notification
372
// list, phone support and notes tables
379
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
381
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "note
385
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "phone_support
389
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription
391
sub_iss_id=iss_id AND
393
sub_usr_id IS NOT NULL
395
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "subscription_type
397
sbt_sub_id=sub_id AND
400
iss_customer_id=$customer_id";
401
$res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
403
for ($i = 0; $i < count($res); $i++) {
404
if ((!empty($res[$i]['sub_usr_id'])) && (!in_array($res[$i]['sub_usr_id'], $persons))) {
405
$persons[] = $res[$i]['sub_usr_id'];
407
if ((!empty($res[$i]['not_usr_id'])) && (!in_array($res[$i]['not_usr_id'], $persons))) {
408
$persons[] = $res[$i]['not_usr_id'];
410
if ((!empty($res[$i]['phs_usr_id'])) && (!in_array($res[$i]['phs_usr_id'], $persons))) {
411
$persons[] = $res[$i]['phs_usr_id'];
415
// get list of staff emails
419
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user,
420
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_user,
421
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
423
pru_usr_id=usr_id AND
424
pru_prj_id=iss_prj_id AND
426
pru_role <> " . User::getRoleID('Customer');
427
$staff_emails = DB_Helper::getInstance()->getCol($stmt);
432
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "support_email
434
sup_iss_id IN (" . implode(", ", $issues) . ")";
435
$emails = DB_Helper::getInstance()->getCol($stmt);
437
foreach ($emails as $address) {
438
$email = strtolower(Mail_Helper::getEmailAddress($address));
439
$staff_emails = array_map('strtolower', $staff_emails);
440
if (@in_array($email, $staff_emails)) {
448
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "phone_support
450
phs_iss_id IN (" . implode(", ", $issues) . ")";
451
$calls = DB_Helper::getInstance()->getOne($stmt);
454
AVG(UNIX_TIMESTAMP(iss_first_response_date) - UNIX_TIMESTAMP(iss_created_date)) AS first_response_time
456
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
458
iss_id IN (" . implode(", ", $issues) . ")";
459
$avg_first_response = DB_Helper::getInstance()->getOne($stmt);
460
if (!empty($avg_first_response)) {
461
// format the average into a number of minutes
462
$avg_first_response = $avg_first_response / 60;
466
AVG(UNIX_TIMESTAMP(iss_closed_date) - UNIX_TIMESTAMP(iss_created_date)) AS closed_time
468
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
470
iss_id IN (" . implode(", ", $issues) . ")";
471
$avg_close = DB_Helper::getInstance()->getOne($stmt);
472
if (!empty($avg_close)) {
473
// format the average into a number of minutes
474
$avg_close = $avg_close / 60;
478
'total_issues' => count($issues),
479
'total_open' => $open,
480
'total_closed' => $closed,
481
'total_persons' => count($persons),
482
'total_emails' => $total_emails,
483
'total_calls' => (integer) $calls,
484
'average_first_response' => Misc::getFormattedTime($avg_first_response),
485
'average_close' => Misc::getFormattedTime($avg_close)
491
* Method used to build the overall customer profile from the information
492
* stored in the customer database.
495
* @param integer $usr_id The Eventum user ID
496
* @return array The customer profile information
498
function getProfile($usr_id)
500
// this is used to return all details about the customer/contact in one fell swoop.
501
// for this example it will just return the details
502
return $this->getDetails(User::getCustomerID($usr_id));
507
* Method used to get the details associated with a customer contact.
510
* @param integer $contact_id The customer contact ID
511
* @return array The contact details
513
function getContactDetails($contact_id)
516
foreach ($this->data as $company_id => $details) {
517
foreach ($details['contacts'] as $contact) {
518
if ($contact['contact_id'] == $contact_id) {
519
$contact['customer_id'] = $company_id;
529
* Returns the list of customer IDs that are associated with the given
530
* email value (wildcards welcome). Contrary to the name of the method, this
531
* also works with customer names
534
* @param string $email The email value
535
* @return array The list of customer IDs
537
function getCustomerIDsLikeEmail($email)
540
foreach ($this->data as $customer) {
541
if (stristr($customer['customer_name'], $email) !== false) {
542
$ids[] = $customer['customer_id'];
546
foreach ($customer['contacts'] as $contact) {
547
if (stristr($contact['email'], $email) !== false) {
548
$ids[] = $customer['customer_id'];
557
* Performs a customer lookup and returns the matches, if
561
* @param string $field The field that we are trying to search against
562
* @param string $value The value that we are searching for
563
* @return array The list of customers
565
function lookup($field, $value)
567
if ($field == "email") {
568
$details = $this->getCustomerIDsLikeEmail($value);
569
if (count($details) > 0) {
570
list($id, $contact_id) = $details;
574
} elseif ($field == "customer_id") {
575
if (empty($this->data[$value])) {
582
return array($this->getDetails($id));
588
* Method used to notify the customer contact that a new issue was just
589
* created and associated with his Eventum user.
592
* @param integer $issue_id The issue ID
593
* @param integer $contact_id The customer contact ID
596
function notifyCustomerIssue($issue_id, $contact_id)
598
// send a notification email to your customer here
603
* Method used to get the list of available support levels.
606
* @return array The list of available support levels
608
function getSupportLevelAssocList()
620
* Returns the support level of the current support contract for a given
624
* @param integer $customer_id The customer ID
625
* @return string The support contract level
627
function getSupportLevelID($customer_id)
629
return $this->data[$customer_id]["support_level_id"];
634
* Returns the list of customer IDs for a given support contract level.
637
* @param integer/array $support_level_id The support level ID or an array of support level IDs
638
* @param mixed $support_options An integer or array of integers indicating various options to get customers with.
639
* @return array The list of customer IDs
641
function getListBySupportLevel($support_level_id, $support_options = false)
643
if (!is_array($support_level_id)) {
644
$support_level_id = array($support_level_id);
647
foreach ($this->data as $company_id => $details) {
648
if (in_array($details["support_level_id"], $support_level_id)) {
649
$assoc[] = $company_id;
657
* Returns an array of support levels grouped together.
660
* @return array an array of support levels.
662
function getGroupedSupportLevels()
665
"Normal" => array(1,2),
666
"Enhanced" => array(3),
667
"Ultra-Special" => array(4)
673
* Checks whether the given technical contact ID is allowed in the current
674
* support contract or not.
677
* @param integer $customer_contact_id The customer technical contact ID
680
function isAllowedSupportContact($customer_contact_id)
682
foreach ($this->data as $id => $details) {
683
foreach ($details['contacts'] as $contact) {
684
if ($contact['contact_id'] == $customer_contact_id) {
694
* Method used to get the associated customer and customer contact from
695
* a given set of support emails. This is especially useful to automatically
696
* associate an issue to the appropriate customer contact that sent a
700
* @param array $sup_ids The list of support email IDs
701
* @return array The customer and customer contact ID
703
function getCustomerInfoFromEmails($sup_ids)
705
$senders = Support::getSender($sup_ids);
706
if (count($senders) > 0) {
708
for ($i = 0; $i < count($senders); $i++) {
709
$emails[] = Mail_Helper::getEmailAddress($senders[$i]);
711
list($customer_id, $contact_id) = $this->getCustomerIDByEmails($emails);
712
$company = $this->getDetails($customer_id);
713
$contact = $this->getContactDetails($contact_id);
715
'customer_id' => $customer_id,
716
'customer_name' => $company['customer_name'],
717
'contact_id' => $contact_id,
718
'contact_name' => $contact['first_name'] . " " . $contact['last_name'],
719
'contacts' => $this->getContactEmailAssocList($customer_id)
724
'customer_name' => '',
726
'contact_name' => '',
734
* Method used to get the customer login grace period (number of days).
737
* @return integer The customer login grace period
739
function getExpirationOffset()
741
// customers can log in up to 30 days after the contract expires.
747
* Method used to get the details of the given customer contact.
750
* @param integer $contact_id The customer contact ID
751
* @return array The customer details
753
function getContactLoginDetails($contact_id)
760
" . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "user
762
usr_customer_contact_id = $contact_id";
763
$res = DB_Helper::getInstance()->getRow($stmt);
764
if (PEAR::isError($res)) {
765
Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
778
* Returns the end date of the current support contract for a given
782
* @param integer $customer_id The customer ID
783
* @return string The support contract end date
785
function getContractEndDate($customer_id)
787
return $this->data[$customer_id]['expiration_date'];
792
* Returns the name and email of the sales account manager of the given customer ID.
795
* @param integer $customer_id The customer ID
796
* @return array An array containing the name and email of the sales account manager
798
function getSalesAccountManager($customer_id)
800
return $this->data[$customer_id]['account_manager'];
805
* Returns the start date of the current support contract for a given
809
* @param integer $customer_id The customer ID
810
* @return string The support contract start date
812
function getContractStartDate($customer_id)
814
return $this->data[$customer_id]['start_date'];
819
* Returns a message to be displayed to a customer on the top of the issue creation page.
821
* @param array $customer_id Customer ID.
823
function getNewIssueMessage($customer_id)
825
// we could do anything we wanted in here, but we will just say "hi"
826
return "Hi! Please create a new issue";
831
* Checks whether the given customer has a support contract that
832
* enforces limits for the minimum first response time or not.
835
* @param integer $customer_id The customer ID
838
function hasMinimumResponseTime($customer_id)
840
$support_level = $this->getSupportLevelID($customer_id);
841
if ($support_level == 1 || $support_level == 2) {
848
* Returns the minimum first response time in seconds for the
849
* support level associated with the given customer.
852
* @param integer $customer_id The customer ID
853
* @return integer The minimum first response time
855
function getMinimumResponseTime($customer_id)
857
// normal level customers will not recieve a response for atleast a day
858
$support_level = $this->getSupportLevelID($customer_id);
859
if ($support_level == 1 || $support_level == 2) {
860
return (60 * 60 * 24);
866
* Returns the maximum first response time associated with the
867
* support contract of the given customer.
870
* @param integer $customer_id The customer ID
871
* @return integer The maximum first response time, in seconds
873
function getMaximumFirstResponseTime($customer_id)
875
$support_level = $this->getSupportLevelID($customer_id);
876
if ($support_level == 1 || $support_level == 2) {
878
return (60 * 60 * 24 * 2);
879
} elseif ($support_level == 3) {
881
return (60 * 60 * 24);
882
} elseif ($support_level == 4) {
883
// 30 minutes for special
890
* Method used to send an expiration notice.
893
* @param integer $contact_id The customer contact ID
894
* @param boolean $is_expired Whether this customer is expired or not
897
function sendExpirationNotice($contact_id, $is_expired = FALSE)
899
// send a support expiration notice email to your customer here
904
* Method used to notify the customer contact that an existing issue
905
* associated with him was just marked as closed.
908
* @param integer $issue_id The issue ID
909
* @param integer $contact_id The customer contact ID
912
function notifyIssueClosed($issue_id, $contact_id)
914
// send a notification email to your customer here
919
* Method used to send an email notification to the sender of a
920
* set of email messages that were manually converted into an
924
* @param integer $issue_id The issue ID
925
* @param array $sup_ids The email IDs
926
* @param integer $customer_id The customer ID
927
* @return array The list of recipient emails
929
function notifyEmailConvertedIntoIssue($issue_id, $sup_ids, $customer_id = FALSE)
931
// send a notification email to your customer here
936
* Method used to send an email notification to the sender of an
937
* email message that was automatically converted into an issue.
940
* @param integer $issue_id The issue ID
941
* @param string $sender The sender of the email message (and the recipient of this notification)
942
* @param string $date The arrival date of the email message
943
* @param string $subject The subject line of the email message
946
function notifyAutoCreatedIssue($issue_id, $sender, $date, $subject)
948
// send a notification email to your customer here
953
* Method used to get the contract details for a given customer contact.
956
* @param integer $contact_id The customer contact ID
957
* @return array The customer contract details
959
function getContractDetails($contact_id, $restrict_expiration)
961
$contact = $this->getContactDetails($contact_id);
962
$customer = $this->getDetails($contact['customer_id']);
963
$support_levels = $this->getSupportLevelAssocList();
966
'contact_name' => $contact['first_name'] . ' ' . $contact['last_name'],
967
'company_name' => $customer['customer_name'],
968
'contract_id' => $customer['customer_id'],
969
'support_level' => $support_levels[$customer['support_level_id']],
970
'expiration_date' => $customer['expiration_date']