Overview

Packages

  • Horde
    • Form
    • MIME
      • Viewer
    • Scheduler
  • None
  • Whups
    • UnitTests

Classes

  • Horde_Core_Ui_VarRenderer_whups
  • Whups
  • Whups_Ajax_Imple_ContactAutoCompleter
  • Whups_Api
  • Whups_Driver
  • Whups_Driver_Sql
  • Whups_Form_AddComment
  • Whups_Form_Admin_AddAttribute
  • Whups_Form_Admin_AddPriority
  • Whups_Form_Admin_AddQueue
  • Whups_Form_Admin_AddReply
  • Whups_Form_Admin_AddState
  • Whups_Form_Admin_AddType
  • Whups_Form_Admin_AddUser
  • Whups_Form_Admin_AddVersion
  • Whups_Form_Admin_CloneType
  • Whups_Form_Admin_DefaultPriority
  • Whups_Form_Admin_DefaultState
  • Whups_Form_Admin_DeleteAttribute
  • Whups_Form_Admin_DeletePriority
  • Whups_Form_Admin_DeleteQueue
  • Whups_Form_Admin_DeleteReply
  • Whups_Form_Admin_DeleteState
  • Whups_Form_Admin_DeleteType
  • Whups_Form_Admin_DeleteVersion
  • Whups_Form_Admin_EditAttributeStepOne
  • Whups_Form_Admin_EditAttributeStepTwo
  • Whups_Form_Admin_EditPriorityStepOne
  • Whups_Form_Admin_EditPriorityStepTwo
  • Whups_Form_Admin_EditQueueStepOne
  • Whups_Form_Admin_EditQueueStepTwo
  • Whups_Form_Admin_EditReplyStepOne
  • Whups_Form_Admin_EditReplyStepTwo
  • Whups_Form_Admin_EditStateStepOne
  • Whups_Form_Admin_EditStateStepTwo
  • Whups_Form_Admin_EditTypeStepOne
  • Whups_Form_Admin_EditTypeStepTwo
  • Whups_Form_Admin_EditUser
  • Whups_Form_Admin_EditVersionStepOne
  • Whups_Form_Admin_EditVersionStepTwo
  • Whups_Form_InsertBranch
  • Whups_Form_Query_AttributeCriterion
  • Whups_Form_Query_ChooseNameForLoad
  • Whups_Form_Query_ChooseNameForSave
  • Whups_Form_Query_DateCriterion
  • Whups_Form_Query_Delete
  • Whups_Form_Query_GroupCriterion
  • Whups_Form_Query_Parameter
  • Whups_Form_Query_PropertyCriterion
  • Whups_Form_Query_TextCriterion
  • Whups_Form_Query_UserCriterion
  • Whups_Form_Renderer_Comment
  • Whups_Form_Search
  • Whups_Form_SendReminder
  • Whups_Form_Ticket_CreateStepFour
  • Whups_Form_Ticket_CreateStepOne
  • Whups_Form_Ticket_CreateStepThree
  • Whups_Form_Ticket_CreateStepTwo
  • Whups_Form_Ticket_Edit
  • Whups_Form_TicketDetails
  • Whups_LoginTasks_SystemTask_Upgrade
  • Whups_Mail
  • Whups_Query
  • Whups_Query_Manager
  • Whups_Reports
  • Whups_Ticket
  • Whups_View_Base
  • Whups_View_Results
  • Whups_View_SavedQueries
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Base class for Whups' storage backend.
  4:  *
  5:  * Copyright 2001-2002 Robert E. Coyle <robertecoyle@hotmail.com>
  6:  * Copyright 2001-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file LICENSE for license information (BSD). If you
  9:  * did not receive this file, see http://www.horde.org/licenses/bsdl.php.
 10:  *
 11:  * @todo    Needs updating to include method stubs for all required methods, to
 12:  *          indicate what methods need to be implemented by other backends.
 13:  *
 14:  * @author  Robert E. Coyle <robertecoyle@hotmail.com>
 15:  * @author  Jan Schneider <jan@horde.org>
 16:  * @package Whups
 17:  */
 18: abstract class Whups_Driver
 19: {
 20:     /**
 21:      * @var array
 22:      */
 23:     protected $_params;
 24: 
 25:     /**
 26:      * Constructor
 27:      *
 28:      * @param array $params  Parameter array.
 29:      *
 30:      * @return Whups_Driver_Base
 31:      */
 32:     public function __construct(array $params)
 33:     {
 34:         $this->_params = $params;
 35:     }
 36: 
 37:     /**
 38:      * Set ticket attributes
 39:      *
 40:      * @param array $info           Attributes to set
 41:      * @param Whups_Ticket $ticket  The ticket which attributes to set.
 42:      */
 43:     public function setAttributes(array $info, Whups_Ticket &$ticket)
 44:     {
 45:         $ticket_id = $ticket->getId();
 46: 
 47:         foreach ($info as $name => $value) {
 48:             if (substr($name, 0, 10) == 'attribute_' &&
 49:                 $ticket->get($name) != $value) {
 50:                 $attribute_id = (int)substr($name, 10);
 51:                 $serialized = $this->_serializeAttribute($value);
 52:                 $ticket->change($name, $value);
 53:                 $this->_setAttributeValue(
 54:                     $ticket_id,
 55:                     $attribute_id,
 56:                     $serialized);
 57:                 $this->updateLog($ticket_id, $GLOBALS['registry']->getAuth(), array('attribute' => $attribute_id . ':' . $serialized));
 58:             }
 59:         }
 60:     }
 61: 
 62:     /**
 63:      * Returns a serialized value, if necessary.
 64:      *
 65:      * @param mixed  The original value.
 66:      *
 67:      * @return string  The JSON encoded value if not already a string.
 68:      */
 69:     protected function _serializeAttribute($value)
 70:     {
 71:         if (!is_string($value)) {
 72:             return Horde_Serialize::serialize($value, Horde_Serialize::JSON);
 73:         }
 74:         return $value;
 75:     }
 76: 
 77:     /**
 78:      * Fetch ticket history
 79:      *
 80:      * @param integer $ticket_id  The ticket to fetch history for.
 81:      *
 82:      * @return array
 83:      */
 84:     public function getHistory($ticket_id, Horde_Form $form = null)
 85:     {
 86:         $rows = $this->_getHistory($ticket_id);
 87:         $attributes = $attributeDetails = array();
 88:         foreach ($rows as $row) {
 89:             if ($row['log_type'] == 'attribute' &&
 90:                 strpos($row['log_value'], ':')) {
 91:                 $attributes[(int)$row['log_value']] = $row['attribute_name'];
 92:             }
 93:             if ($row['log_type'] == 'type') {
 94:                 $attributeDetails += $this->getAttributesForType($row['log_value']);
 95:             }
 96:         }
 97: 
 98:         $renderer = new Horde_Core_Ui_VarRenderer_Html();
 99:         $history = array();
100:         foreach ($rows as $row) {
101:             $label = null;
102:             $human = $value = $row['log_value'];
103:             $type = $row['log_type'];
104:             $transaction = $row['transaction_id'];
105: 
106:             $history[$transaction]['timestamp'] = $row['timestamp'];
107:             $history[$transaction]['user_id'] = $row['user_id'];
108:             $history[$transaction]['ticket_id'] = $row['ticket_id'];
109: 
110:             switch ($type) {
111:             case 'comment':
112:                 $history[$transaction]['comment'] = $row['comment_text'];
113:                 $history[$transaction]['changes'][] = array(
114:                     'type' => 'comment',
115:                     'value' => $row['log_value'],
116:                     'comment' => $row['comment_text']);
117:                 continue 2;
118: 
119:             case 'queue':
120:                 $label = $row['queue_name'];
121:                 break;
122: 
123:             case 'version':
124:                 $label = $row['version_name'];
125:                 break;
126: 
127:             case 'type':
128:                 $label = $row['type_name'];
129:                 break;
130: 
131:             case 'state':
132:                 $label = $row['state_name'];
133:                 break;
134: 
135:             case 'priority':
136:                 $label = $row['priority_name'];
137:                 break;
138: 
139:             case 'attribute':
140:                 continue 2;
141: 
142:             case 'due':
143:                 $label = $row['log_value_num'];
144:                 break;
145: 
146:             default:
147:                 if (strpos($type, 'attribute_') === 0) {
148:                     try {
149:                         $value = Horde_Serialize::unserialize(
150:                             $value, Horde_Serialize::JSON);
151:                     } catch (Horde_Serialize_Exception $e) {
152:                     }
153:                     $attribute = substr($type, 10);
154:                     if (isset($attributes[$attribute])) {
155:                         $label = $attributes[$attribute];
156:                         if ($form) {
157:                             if (isset($form->attributes[$attribute])) {
158:                                 /* Attribute is part of the current type, so we
159:                                  * have the form field in the current form. */
160:                                 $field = $form->attributes[$attribute];
161:                             } else {
162:                                 /* Attribute is from a different type, create
163:                                  * the form field manually. */
164:                                 $detail = $attributeDetails[$attribute];
165:                                 $field = new Horde_Form_Variable(
166:                                     $detail['human_name'],
167:                                     $type,
168:                                     $form->getType($detail['type'],
169:                                                    $detail['params']),
170:                                     $detail['required'],
171:                                     $detail['readonly'],
172:                                     $detail['desc']);
173:                             }
174:                             $human = $renderer->render(
175:                                 $form,
176:                                 $field,
177:                                 new Horde_Variables(array($type => $value)));
178:                         }
179:                         $type = 'attribute';
180:                     } else {
181:                         $label = sprintf(_("Attribute %d"), $attribute);
182:                     }
183:                 }
184:                 break;
185:             }
186: 
187:             $history[$transaction]['changes'][] = array(
188:                 'type' => $type,
189:                 'value' => $value,
190:                 'human' => $human,
191:                 'label' => $label);
192:         }
193: 
194:         return $history;
195:     }
196: 
197:     /**
198:      */
199:     public function getQueue($queueId)
200:     {
201:         return $GLOBALS['registry']->call('tickets/getQueueDetails',
202:                                           array($queueId));
203:     }
204: 
205:     /**
206:      */
207:     public function getQueues()
208:     {
209:         return $GLOBALS['registry']->call('tickets/listQueues');
210:     }
211: 
212:     /**
213:      */
214:     public function getVersionInfo($queue)
215:     {
216:         return $GLOBALS['registry']->call('tickets/listVersions',
217:                                           array($queue));
218:     }
219: 
220:     /**
221:      * Returns a hash of versions suitable for select lists.
222:      */
223:     public function getVersions($queue, $all = false)
224:     {
225:         if (empty($queue)) {
226:             return array();
227:         }
228: 
229:         $versioninfo = $this->getVersionInfo($queue);
230:         $versions = array();
231:         $old_versions = array();
232:         foreach ($versioninfo as $vinfo) {
233:             $name = $vinfo['name'];
234:             if (!empty($vinfo['description'])) {
235:                 $name .= ': ' . $vinfo['description'];
236:             }
237:             if ($all && !$vinfo['active']) {
238:                 $name .= ' ' . _("(inactive)");
239:             }
240:             if ($vinfo['active']) {
241:                 $versions[$vinfo['id']] = $name;
242:             } else {
243:                 $old_versions[$vinfo['id']] = $name;
244:             }
245:         }
246: 
247:         if ($old_versions && !$all) {
248:             $versions[key($old_versions)] = _("Older? Please update first!");
249:         } else {
250:             $versions += $old_versions;
251:         }
252: 
253:         return $versions;
254:     }
255: 
256:     /**
257:      */
258:     public function getVersion($version)
259:     {
260:         return $GLOBALS['registry']->call('tickets/getVersionDetails',
261:                                           array($version));
262:     }
263: 
264:     /**
265:      */
266:     public function getCategories()
267:     {
268:         return array('unconfirmed' => _("Unconfirmed"),
269:                      'new' => _("New"),
270:                      'assigned' => _("Assigned"),
271:                      'resolved' => _("Resolved"));
272:     }
273: 
274:     /**
275:      * Returns the attributes for a ticket type.
276:      *
277:      * @params integer $type  A ticket type ID.
278:      *
279:      * @return array  A list of attributes.
280:      */
281:     public function getAttributesForType($type = null)
282:     {
283:         $attributes = $this->_getAttributesForType($type);
284:         foreach ($attributes as $id => $attribute) {
285:             $attributes[$id] = array(
286:                 'human_name' => $attribute['attribute_name'],
287:                 'type'       => $attribute['attribute_type'],
288:                 'required'   => $attribute['attribute_required'],
289:                 'readonly'   => false,
290:                 'desc'       => $attribute['attribute_description'],
291:                 'params'     => $attribute['attribute_params']);
292:         }
293:         return $attributes;
294:     }
295: 
296:     /**
297:      * Returns the attributes for a specific ticket.
298:      *
299:      * This method will check if external attributes need to be fetched from
300:      * hooks or whether to use the standard ones defined within Whups.
301:      *
302:      * @params integer $ticket_id  The ticket ID.
303:      *
304:      * @return array  List of attributes.
305:      */
306:     public function getAllTicketAttributesWithNames($ticket_id)
307:     {
308:         $ta = $this->_getAllTicketAttributesWithNames($ticket_id);
309: 
310:         $attributes = array();
311:         foreach ($ta as $id => $attribute) {
312:             try {
313:                 $value = Horde_Serialize::unserialize(
314:                     $attribute['attribute_value'],
315:                     Horde_Serialize::JSON);
316:             } catch (Horde_Serialize_Exception $e) {
317:                 $value = $attribute['attribute_value'];
318:             }
319:             $attributes[$attribute['attribute_id']] = array(
320:                 'id'         => $attribute['attribute_id'],
321:                 'human_name' => $attribute['attribute_name'],
322:                 'type'       => $attribute['attribute_type'],
323:                 'required'   => $attribute['attribute_required'],
324:                 'readonly'   => false,
325:                 'desc'       => $attribute['attribute_description'],
326:                 'params'     => $attribute['attribute_params'],
327:                 'value'      => $value);
328:         }
329:         return $attributes;
330:     }
331: 
332:     /**
333:      * Deletes a queue.
334:      *
335:      * Should be called by driver subclasses after successful removal from the
336:      * backend. Takes only care of cleaning up queue permissions.
337:      *
338:      * @param integer $queueId  The id of the queue being deleted.
339:      */
340:     public function deleteQueue($queueId)
341:     {
342:         $perms = $GLOBALS['injector']->getInstance('Horde_Perms');
343:         try {
344:             $perm = $perms->getPermission("whups:queues:$queueId");
345:             return $perms->removePermission($perm, true);
346:         } catch (Horde_Perms_Exception $e) {}
347: 
348:         return true;
349:     }
350: 
351:     /**
352:      * Deletes a form reply.
353:      *
354:      * Should be called by driver subclasses after successful removal from the
355:      * backend. Takes only care of cleaning up reply permissions.
356:      *
357:      * @param integer $reply  The id of the form reply being deleted.
358:      */
359:     public function deleteReply($reply)
360:     {
361:         $perms = $GLOBALS['injector']->getInstance('Horde_Perms');
362:         try {
363:             $perm = $perms->getPermission("whups:replies:$reply");
364:             return $perms->removePermission($perm, true);
365:         } catch (Horde_Perms_Exception $e) {}
366: 
367:         return true;
368:     }
369: 
370:     /**
371:      */
372:     public function filterTicketsByState($tickets, $state_category = array())
373:     {
374:         /* Take a list of tickets and return only those of the specified
375:          * state_category. */
376:         $tickets_filtered = array();
377:         foreach ($tickets as $ticket) {
378:             foreach ($state_category as $state) {
379:                 if ($ticket['state_category'] == $state) {
380:                     $tickets_filtered[] = $ticket;
381:                 }
382:             }
383:         }
384: 
385:         return $tickets_filtered;
386:     }
387: 
388:     /**
389:      * Sends email notifications to a list of recipients.
390:      *
391:      * We do some ugly work in here to make sure that no one gets comments
392:      * mailed to them that they shouldn't see (because of group permissions).
393:      *
394:      * @param array $opts  Option hash with notification information.
395:      *                     Possible values:
396:      *                     - ticket:     (Whups_Ticket) A ticket. If not set,
397:      *                                   this is assumed to be a reminder
398:      *                                   message.
399:      *                     - recipients: (array|string) The list of recipients,
400:      *                                   with user names as keys and user roles
401:      *                                   as values.
402:      *                     - subject:    (string) The email subject.
403:      *                     - view:       (Horde_View) The view object for the
404:      *                                   message text.
405:      *                     - template:   (string) The template file for the
406:      *                                   message text.
407:      *                     - from:       (string) The email sender.
408:      *                     - new:        (boolean, optional) Whether the passed
409:      *                                   ticket was just created.
410:      */
411:     public function mail(array $opts)
412:     {
413:         global $conf, $registry, $prefs;
414: 
415:         $opts = array_merge(array('ticket' => false, 'new' => false), $opts);
416: 
417:         /* Set up recipients and message headers. */
418:         $mail = new Horde_Mime_Mail(array(
419:             'X-Whups-Generated' => 1,
420:             'User-Agent' => 'Whups ' . $registry->getVersion(),
421:             'Precedence' => 'bulk',
422:             'Auto-Submitted' => $opts['ticket'] ? 'auto-replied' : 'auto-generated'));
423: 
424:         $mail_always = null;
425:         if ($opts['ticket'] && !empty($conf['mail']['always_copy'])) {
426:             $mail_always = $conf['mail']['always_copy'];
427:             if (strpos($mail_always, '<@>') !== false) {
428:                 try {
429:                     $mail_always = str_replace('<@>', $opts['ticket']->get('queue_name'), $mail_always);
430:                 } catch (Whups_Exception $e) {
431:                     $mail_always = null;
432:                 }
433:             }
434:             if ($mail_always && !isset($opts['recipients'][$mail_always])) {
435:                 $opts['recipients'][$mail_always] = 'always';
436:             }
437:         }
438: 
439:         if ($opts['ticket'] &&
440:             ($queue = $this->getQueue($opts['ticket']->get('queue'))) &&
441:              !empty($queue['email'])) {
442:             $mail->addHeader('From', $queue['email']);
443:         } elseif (!empty($conf['mail']['from_addr'])) {
444:             $mail->addHeader('From', $conf['mail']['from_addr']);
445:         } else {
446:             $mail->addHeader('From', Whups::formatUser($opts['from']));
447:         }
448:         if (!empty($conf['mail']['return_path'])) {
449:             $mail->addHeader('Return-Path', $conf['mail']['return_path']);
450:         }
451: 
452:         if ($opts['ticket']) {
453:             $opts['subject'] = '[' . $registry->get('name') . ' #'
454:                 . $opts['ticket']->getId() . '] ' . $opts['subject'];
455:         }
456:         $mail->addHeader('Subject', $opts['subject']);
457: 
458:         /* Get our array of comments, sorted in the appropriate order. */
459:         if ($opts['ticket']) {
460:             $comments = $this->getHistory($opts['ticket']->getId());
461:             if ($conf['mail']['commenthistory'] == 'new' && count($comments)) {
462:                 $comments = array_pop($comments);
463:                 $comments = array($comments);
464:             } elseif ($conf['mail']['commenthistory'] != 'chronological') {
465:                 $comments = array_reverse($comments);
466:             }
467:         } else {
468:             $comments = array();
469:         }
470: 
471:         /* Don't notify any email address more than once. */
472:         $seen_email_addresses = array();
473: 
474:         /* Get VFS handle for attachments. */
475:         if ($opts['ticket']) {
476:             $vfs = $GLOBALS['injector']
477:                 ->getInstance('Horde_Core_Factory_Vfs')
478:                 ->create();
479:             try {
480:                 $attachments = Whups::getAttachments($opts['ticket']->getId());
481:             } catch (Whups_Exception $e) {
482:                 $attachments = array();
483:                 Horde::logMessage($e);
484:             }
485:         }
486: 
487:         foreach ($opts['recipients'] as $user => $role) {
488:             if ($user == $opts['from'] &&
489:                 $user == $GLOBALS['registry']->getAuth() &&
490:                 $prefs->getValue('email_others_only')) {
491:                 continue;
492:             }
493: 
494:             /* Make sure to check permissions as a guest for the 'always_copy'
495:              * address, and as the recipient for all others. */
496:             $to = $full_name = '';
497:             if (!empty($mail_always) && $user == $mail_always) {
498:                 $details = null;
499:                 $mycomments = Whups::permissionsFilter(
500:                     $comments, 'comment', Horde_Perms::READ, '');
501:                 $to = $mail_always;
502:             } else {
503:                 $details = Whups::getUserAttributes($user);
504:                 if (!empty($details['email'])) {
505:                     $to = Whups::formatUser($details);
506:                     $mycomments = Whups::permissionsFilter(
507:                         $comments, 'comment', Horde_Perms::READ, $details['user']);
508:                 }
509:                 $full_name = $details['name'];
510:             }
511: 
512:             /* We may have no recipients due to users excluding themselves
513:              * from self notifies. */
514:             if (!$to) {
515:                 continue;
516:             }
517: 
518:             if ($opts['ticket']) {
519:                 /* Add attachments. */
520:                 $attachmentAdded = false;
521:                 if (empty($GLOBALS['conf']['mail']['link_attach'])) {
522:                     /* We need to remove all attachments because the attachment
523:                      * list is potentially limited by permissions. */
524:                     $mail->clearParts();
525:                     foreach ($mycomments as $comment) {
526:                         foreach ($comment['changes'] as $change) {
527:                             if ($change['type'] == 'attachment') {
528:                                 foreach ($attachments as $attachment) {
529:                                     if ($attachment['name'] == $change['value']) {
530:                                         if (!isset($attachment['part'])) {
531:                                             $attachment['part'] = new Horde_Mime_Part();
532:                                             $attachment['part']->setType(Horde_Mime_Magic::filenameToMime($change['value'], false));
533:                                             $attachment['part']->setDisposition('attachment');
534:                                             $attachment['part']->setContents($vfs->read(Whups::VFS_ATTACH_PATH . '/' . $opts['ticket']->getId(), $change['value']));
535:                                             $attachment['part']->setName($change['value']);
536:                                         }
537:                                         $mail->addMimePart($attachment['part']);
538:                                         $attachmentAdded = true;
539:                                         break;
540:                                     }
541:                                 }
542:                             }
543:                         }
544:                     }
545:                 }
546: 
547:                 $formattedComment = $this->formatComments($mycomments, $opts['ticket']->getId());
548: 
549:                 if (isset($details['type']) && $details['type'] == 'user') {
550:                     $user_prefs = $GLOBALS['injector']
551:                         ->getInstance('Horde_Core_Factory_Prefs')
552:                         ->create('whups', array('user' => $details['user']));
553:                     if (!$attachmentAdded &&
554:                         empty($formattedComment) &&
555:                         $user_prefs->getValue('email_comments_only')) {
556:                         continue;
557:                     }
558:                 }
559: 
560:                 $opts['view']->comment = $formattedComment;
561:             }
562: 
563:             try {
564:                 $addr_arr = Horde_Mime_Address::parseAddressList($to);
565:                 if (isset($addr_arr[0])) {
566:                     $bare_address = strtolower($addr_arr[0]['mailbox'] . '@' . $addr_arr[0]['host']);
567:                     if (!empty($seen_email_addresses[$bare_address])) {
568:                         continue;
569:                     }
570:                     $seen_email_addresses[$bare_address] = true;
571: 
572:                     if (empty($full_name) && isset($addr_arr[0]['personal'])) {
573:                         $full_name = $addr_arr[0]['personal'];
574:                     }
575:                 }
576:             } catch (Horde_Mime_Exception $e) {}
577: 
578:             // use email address as fallback
579:             if (empty($full_name)) {
580:                 $full_name = $to;
581:             }
582: 
583:             $opts['view']->full_name = $full_name;
584:             $opts['view']->role = $role;
585:             $mail->setBody($opts['view']->render($opts['template']));
586: 
587:             $mail->addHeader('Message-ID', Horde_Mime::generateMessageId());
588:             if ($opts['ticket']) {
589:                 $message_id = '<whups-' . $opts['ticket']->getId() . '-'
590:                     . md5($user) . '@' . $conf['server']['name'] . '>';
591:                 if ($opts['new']) {
592:                     $mail->addHeader('Message-ID', $message_id);
593:                 } else {
594:                     $mail->addHeader('In-Reply-To', $message_id);
595:                     $mail->addHeader('References', $message_id);
596:                 }
597:             }
598: 
599:             $mail->clearRecipients();
600:             $mail->addHeader('To', $to);
601: 
602:             try {
603:                 $mail->send($GLOBALS['injector']->getInstance('Horde_Mail'), true);
604:                 $entry = sprintf('%s Message sent to %s from "%s"',
605:                                  $_SERVER['REMOTE_ADDR'], $to,
606:                                  $GLOBALS['registry']->getAuth());
607:                 Horde::logMessage($entry, 'INFO');
608:             } catch (Horde_Mime_Exception $e) {
609:                 Horde::logMessage($e, 'ERR');
610:             }
611:         }
612:     }
613: 
614:     /**
615:      * Converts a changeset array to a plain text comment snippet.
616:      *
617:      * @param array $comments  A changeset list.
618:      * @param integer $ticket  A ticket ID.
619:      *
620:      * @return string  The formatted comment text, if any.
621:      */
622:     public function formatComments($comments, $ticket)
623:     {
624:         $text = '';
625:         foreach ($comments as $comment) {
626:             if (!empty($comment['comment_text'])) {
627:                 $text .= "\n"
628:                     . sprintf(_("%s (%s) wrote:"),
629:                               Whups::formatUser($comment['user_id']),
630:                               strftime('%Y-%m-%d %H:%M', $comment['timestamp']))
631:                     . "\n\n" . $comment['comment_text'] . "\n\n\n";
632:             }
633: 
634:             /* Add attachment links. */
635:             if (empty($GLOBALS['conf']['mail']['link_attach'])) {
636:                 continue;
637:             }
638:             foreach ($comment['changes'] as $change) {
639:                 if ($change['type'] != 'attachment') {
640:                     continue;
641:                 }
642:                 $url_params = array('actionID' => 'download_file',
643:                                     'file' => $change['value'],
644:                                     'ticket' => $ticket);
645:                 $text .= "\n"
646:                     . sprintf(_("%s (%s) uploaded: %s"),
647:                               Whups::formatUser($comment['user_id']),
648:                               strftime('%Y-%m-%d %H:%M', $comment['timestamp']),
649:                               $change['value'])
650:                     . "\n\n"
651:                     . Horde::url(Horde::downloadUrl($change['value'], $url_params), true)
652:                     . "\n\n\n";
653:             }
654:         }
655: 
656:         return $text;
657:     }
658: 
659: }
660: 
API documentation generated by ApiGen