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:  * The Whups_Ticket class encapsulates some logic relating to tickets, sending
  4:  * updates, etc.
  5:  *
  6:  * Copyright 2004-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:  * @author  Chuck Hagenbuch <chuck@horde.org>
 12:  * @author  Jan Schneider <jan@horde.org>
 13:  * @package Whups
 14:  */
 15: class Whups_Ticket
 16: {
 17:     /**
 18:      * The id of the ticket this object wraps.
 19:      *
 20:      * @var integer
 21:      */
 22:     protected $_id;
 23: 
 24:     /**
 25:      * The current values of the ticket.
 26:      *
 27:      * @var array
 28:      */
 29:     protected $_details;
 30: 
 31:     /**
 32:      * Array of changes to make to the ticket.
 33:      *
 34:      * @var array
 35:      */
 36:     protected $_changes = array();
 37: 
 38:     /**
 39:      * Returns a ticket object for an id.
 40:      *
 41:      * @param integer $id  The ticket id.
 42:      *
 43:      * @return Whups_Ticket Whups_Ticket object
 44:      */
 45:     static public function makeTicket($id)
 46:     {
 47:         global $whups_driver;
 48: 
 49:         $details = $whups_driver->getTicketDetails($id);
 50:         $ticket = new Whups_Ticket($id, $details);
 51: 
 52:         return $ticket;
 53:     }
 54: 
 55:     /**
 56:      * Creates a new ticket.
 57:      *
 58:      * Pretty bare wrapper around Whups_Driver::addTicket().
 59:      *
 60:      * @static
 61:      *
 62:      * @param array $info  A hash with ticket information.
 63:      *
 64:      * @return Whups_Ticket  Whups_Ticket object.
 65:      */
 66:     static public function newTicket($info, $requester)
 67:     {
 68:         global $whups_driver;
 69: 
 70:         if (!isset($info['type'])) {
 71:             $info['type'] = $whups_driver->getDefaultType($info['queue']);
 72:             if (!$info['type']) {
 73:                 $queue = $whups_driver->getQueue($info['queue']);
 74:                 throw new Whups_Exception(
 75:                     sprintf(
 76:                         _("No type for this ticket and no default type for queue \"%s\" specified."),
 77:                         $queue['name']));
 78:             }
 79:         }
 80:         if (!isset($info['state'])) {
 81:             $info['state'] = $whups_driver->getDefaultState($info['type']);
 82:             if (!$info['state']) {
 83:                 throw new Whups_Exception(
 84:                     sprintf(
 85:                         _("No state for this ticket and no default state for ticket type \"%s\" specified."),
 86:                         $whups_driver->getTypeName($info['type'])));
 87:             }
 88:         }
 89:         if (!isset($info['priority'])) {
 90:             $info['priority'] = $whups_driver->getDefaultPriority($info['type']);
 91:             if (!$info['priority']) {
 92:                 throw new Whups_Exception(
 93:                     sprintf(
 94:                         _("No priority for this ticket and no default priority for ticket type \"%s\" specified."),
 95:                         $whups_driver->getTypeName($info['type'])));
 96:             }
 97:         }
 98: 
 99:         $id = $whups_driver->addTicket($info, $requester);
100:         $details = $whups_driver->getTicketDetails($id, false);
101:         $ticket = new Whups_Ticket($id, $details);
102: 
103:         // Add attachment if one was uploaded.
104:         if (!empty($info['newattachment']['name'])) {
105:             $ticket->change(
106:                 'attachment',
107:                 array(
108:                     'name' => $info['newattachment']['name'],
109:                     'tmp_name' => $info['newattachment']['tmp_name']));
110:         }
111: 
112:         // Check for a deferred attachment upload.
113:         if (!empty($info['deferred_attachment']) &&
114:             ($a_name = $GLOBALS['session']->get('whups', 'deferred_attachment/' . $info['deferred_attachment']))) {
115:             $ticket->change(
116:                 'attachment',
117:                 array(
118:                     'name' => $info['deferred_attachment'],
119:                     'tmp_name' => $a_name));
120:             unlink($a_name);
121:         }
122: 
123:         // Check for manually added attachments.
124:         if (!empty($info['attachments'])) {
125:             $ticket->change('attachments', $info['attachments']);
126:         }
127: 
128:         // Commit any changes (new attachments, etc.)
129:         $ticket->commit(
130:             $ticket->get('user_id_requester'),
131:             $info['last-transaction'],
132:             false);
133: 
134:         // Send email notifications.
135:         $ticket->notify($ticket->get('user_id_requester'), true);
136: 
137:         return $ticket;
138:     }
139: 
140:     /**
141:      * Constructor.
142:      *
143:      * @param integer $id     The ticket id.
144:      * @param array $details  The hash of ticket information.
145:      *
146:      * @return Whups_Ticket
147:      */
148:     public function __construct($id, array $details)
149:     {
150:         $this->_id = $id;
151:         $this->_details = $details;
152:     }
153: 
154:     /**
155:      * Returns all ticket information.
156:      *
157:      * @return array  The ticket information.
158:      */
159:     public function getDetails()
160:     {
161:         return $this->_details;
162:     }
163: 
164:     /**
165:      * Returns the ticket id.
166:      *
167:      * @return integer  The ticket id.
168:      */
169:     public function getId()
170:     {
171:         return $this->_id;
172:     }
173: 
174:     /**
175:      * Returns a piece of information from this ticket.
176:      *
177:      * @param string $detail  The detail to return.
178:      *
179:      * @return mixed  The detail value.
180:      */
181:     public function get($detail)
182:     {
183:         return isset($this->_details[$detail])
184:             ? $this->_details[$detail]
185:             : null;
186:     }
187: 
188:     /**
189:      * Changes a detail of the ticket to a new value.
190:      *
191:      * Never touches the backend; do not use for changes that you want to
192:      * persist.
193:      *
194:      * @param string $detail  The detail to change.
195:      * @param string $value   The new detail value.
196:      */
197:     public function set($detail, $value)
198:     {
199:         $this->_details[$detail] = $value;
200:     }
201: 
202:     /**
203:      * Tracks that a detail of the ticket should change, but does not actually
204:      * make the change until commit() is called.
205:      *
206:      * @see commit()
207:      *
208:      * @param string $detail  The detail to change.
209:      * @param string $value   The new detail value.
210:      */
211:     public function change($detail, $value)
212:     {
213:         $previous_value = isset($this->_details[$detail])
214:             ? $this->_details[$detail]
215:             : '';
216:         if ($previous_value != $value) {
217:             $this->_changes[$detail] = array(
218:                 'from' => $this->get($detail),
219:                 'from_name' => $this->get($detail . '_name'),
220:                 'to' => $value);
221:         }
222:     }
223: 
224:     /**
225:      * Goes through a list of built-up changes and commits them to the
226:      * backend.
227:      *
228:      * This will send email updates by default, update the ticket log, etc.
229:      *
230:      * @see change()
231:      *
232:      * @param string  $user         The Horde user of the changes to be made.
233:      *                              Defaults to the current user.
234:      * @param integer $transaction  The transaction these changes are part of.
235:      *                              Defaults to a new transaction.
236:      * @param boolean $notify       Send ticket notifications?
237:      */
238:     public function commit($user = null, $transaction = null, $notify = true)
239:     {
240:         global $whups_driver;
241: 
242:         if (!count($this->_changes)) {
243:             return;
244:         }
245: 
246:         if (is_null($user)) {
247:             $user = $GLOBALS['registry']->getAuth();
248:         }
249:         $author_email = isset($this->_changes['comment-email']['to'])
250:             ? $this->_changes['comment-email']['to']
251:             : null;
252: 
253:         if (is_null($transaction)) {
254:             // Get a new transaction id from the backend.
255:             $transaction = $whups_driver->newTransaction($user, $author_email);
256:         }
257: 
258:         // If this is a guest update, the comment id is going to map to the
259:         // requester pseudo-username.
260:         if ($user === false) {
261:             $user = '-' . $transaction . '_transaction';
262:         }
263: 
264:         // Run hook before setting the dates.
265:         try {
266:             $this->_changes = Horde::callHook('ticket_update', array($this, $this->_changes), 'whups');
267:         } catch (Horde_Exception_HookNotSet $e) {
268:         }
269: 
270:         // Update cached dates.
271:         $timestamp = time();
272:         $this->_changes['date_updated'] = array('to' => $timestamp);
273:         if (isset($this->_changes['state'])) {
274:             $state = $whups_driver->getState($this->_changes['state']['to']);
275:             if ($state['category'] == 'assigned') {
276:                 $this->_changes['date_assigned'] = array('to' => $timestamp);
277:                 $this->_changes['date_resolved'] = array('to' => null);
278:             } elseif ($state['category'] == 'resolved') {
279:                 $this->_changes['date_resolved'] = array('to' => $timestamp);
280:             } else {
281:                 $this->_changes['date_resolved'] = array('to' => null);
282:             }
283:         }
284: 
285:         $updates = array();
286:         foreach ($this->_changes as $detail => $values) {
287:             $value = $values['to'];
288:             switch ($detail) {
289:             case 'owners':
290:                 // Fetch $oldOwners list; then loop through $value adding and
291:                 // deleting as needed.
292:                 $oldOwners = current($whups_driver->getOwners($this->_id));
293:                 $this->_changes['oldowners'] = $oldOwners;
294:                 foreach ($value as $owner) {
295:                     if (!$oldOwners ||
296:                         array_search($owner, $oldOwners) === false) {
297:                         $whups_driver->addTicketOwner($this->_id, $owner);
298:                         $whups_driver->updateLog(
299:                             $this->_id, $user,
300:                             array('assign' => $owner),
301:                             $transaction);
302:                     } else {
303:                         // Remove $owner from the old owners list; anyone left
304:                         // in $oldOwners will be removed.
305:                         unset($oldOwners[array_search($owner, $oldOwners)]);
306:                     }
307:                 }
308: 
309:                 // Delete removed owners and log the removals.
310:                 if (is_array($oldOwners)) {
311:                     foreach ($oldOwners as $owner) {
312:                         $whups_driver->deleteTicketOwner($this->_id, $owner);
313:                         $whups_driver->updateLog(
314:                             $this->_id, $user,
315:                             array('unassign' => $owner),
316:                             $transaction);
317:                     }
318:                 }
319:                 break;
320: 
321:             case 'comment':
322:                 $commentId = $whups_driver->addComment(
323:                     $this->_id, $value, $user, $author_email);
324: 
325:                 // Store the comment id in the updates array for the log.
326:                 $updates['comment'] = $commentId;
327:                 if (!empty($this->_changes['comment-perms'])) {
328:                     $this->addCommentPerms(
329:                         $commentId,
330:                         $this->_changes['comment-perms']['to']);
331:                 }
332:                 break;
333: 
334:             case 'comment-email':
335:             case 'comment-perms':
336:                 // Skip these, handled in the comment case.
337:                 break;
338: 
339:             case 'attachment':
340:                 $this->addAttachment($value['name'], $value['tmp_name']);
341:                 // Store the new file name in the updates array for the
342:                 // log.
343:                 $updates['attachment'][] = $value['name'];
344:                 break;
345: 
346:             case 'attachments':
347:                 foreach ($value as $attachment) {
348:                     $this->addAttachment($attachment['name'], $attachment['tmp_name']);
349:                     // Store the new file name in the updates array for the
350:                     // log.
351:                     $updates['attachment'][] = $attachment['name'];
352:                 }
353:                 break;
354: 
355:             case 'delete-attachment':
356:                 $this->deleteAttachment($value);
357:                 // Store the deleted file name in the updates array for
358:                 // the log.
359:                 $updates['delete-attachment'] = $value;
360: 
361:                 break;
362: 
363:             case 'queue':
364:                 // Reset version if new queue is not versioned.
365:                 $newqueue = $whups_driver->getQueue($value);
366:                 if (empty($newqueue['queue_versioned'])) {
367:                     $updates['version'] = 0;
368:                 }
369:                 $updates['queue'] = $value;
370: 
371:             default:
372:                 if (strpos($detail, 'attribute_') === 0 &&
373:                     !is_string($value)) {
374:                     $value = Horde_Serialize::Serialize($value,
375:                                                         Horde_Serialize::JSON);
376:                 }
377:                 $updates[$detail] = $value;
378:             }
379:         }
380: 
381:         if (count($updates)) {
382:             $whups_driver->updateTicket($this->_id, $updates);
383:             $whups_driver->updateLog($this->_id, $user, $updates, $transaction);
384:         }
385: 
386:         // Reload $this->_details to make sure we have the latest information.
387:         //
388:         // @todo Only touch the db if we have to.
389:         $details = $whups_driver->getTicketDetails($this->_id);
390:         $this->_details = array_merge($this->_details, $details);
391: 
392:         // Send notification emails to all ticket listeners.
393:         if ($notify) {
394:             $this->notify($user, false);
395:         }
396: 
397:         // Reset the changes array.
398:         $this->_changes = array();
399:     }
400: 
401:     /**
402:      * Deletes this ticket.
403:      *
404:      * @throws Whups_Exception
405:      */
406:     public function delete()
407:     {
408:         global $whups_driver;
409: 
410:         /* Build message template. */
411:         $view = new Horde_View(array('templatePath' => WHUPS_BASE . '/config'));
412:         $view->date = strftime($GLOBALS['prefs']->getValue('date_format'));
413:         $view->auth_name = $GLOBALS['injector']
414:             ->getInstance('Horde_Core_Factory_Identity')
415:             ->create()
416:             ->getValue('fullname');
417:         if (empty($view->auth_name)) {
418:             $view->auth_name = $GLOBALS['registry']->getAuth('bare');
419:         }
420: 
421:         /* Get queue specific notification message text, if available. */
422:         $message_file = WHUPS_BASE . '/config/delete_email.plain';
423:         if (file_exists($message_file . '.' . $this->get('queue') . '.php')) {
424:             $message_file .= '.' . $this->get('queue') . '.php';
425:         } elseif (file_exists($message_file . '.local.php')) {
426:             $message_file .= '.local.php';
427:         } else {
428:             $message_file .= '.php';
429:         }
430:         $message_file = basename($message_file);
431: 
432:         if ($GLOBALS['conf']['mail']['incl_resp'] ||
433:             !count(current($whups_driver->getOwners($this->_id)))) {
434:             /* Include all responsible.  */
435:             $listeners = $whups_driver->getListeners(
436:                 $this->_id, true, false, true);
437:         } else {
438:             /* Don't include all responsible unless ticket is assigned. */
439:             $listeners = $whups_driver->getListeners(
440:                 $this->_id, true, false, false);
441:         }
442: 
443:         $this->change('comment', _("The ticket was deleted."));
444:         $this->commit(null, null, false);
445:         $whups_driver->deleteTicket($this->_id);
446:         $whups_driver->mail(array('ticket' => $this,
447:                                   'recipients' => $listeners,
448:                                   'subject' => _("Deleted:") . ' ' . $this->get('summary'),
449:                                   'view' => $view,
450:                                   'template' => $message_file,
451:                                   'from' => $GLOBALS['registry']->getAuth()));
452:     }
453: 
454:     /**
455:      * Adds an attachment to this ticket.
456:      *
457:      * @param string $attachment_name  The name of the attachment.
458:      * @param string $attachment_file  The temporary file containing the data
459:      *                                 to be stored.
460:      *
461:      * @throws Whups_Exception
462:      */
463:     public function addAttachment(&$attachment_name, $attachment_file)
464:     {
465:         if (!isset($GLOBALS['conf']['vfs']['type'])) {
466:             throw new Whups_Exception(
467:                 _("The VFS backend needs to be configured to enable attachment uploads."),
468:                 'horde.error');
469:         }
470: 
471:         try {
472:             $vfs = $GLOBALS['injector']
473:                 ->getInstance('Horde_Core_Factory_Vfs')
474:                 ->create();
475:         } catch (Horde_Vfs_Exception $e) {
476:             throw new Whups_Exception($e);
477:         }
478: 
479:         // Get existing attachment names.
480:         $used_names = $this->listAllAttachments();
481: 
482:         $dir = Whups::VFS_ATTACH_PATH . '/' . $this->_id;
483:         while ((array_search($attachment_name, $used_names) !== false) ||
484:                $vfs->exists($dir, $attachment_name)) {
485:             if (preg_match('/(.*)\[(\d+)\](\.[^.]*)?$/', $attachment_name,
486:                            $match)) {
487:                 $attachment_name = $match[1] . '[' . ++$match[2] . ']';
488:                 if (isset($match[3])) {
489:                     $attachment_name .= $match[3];
490:                 }
491:             } else {
492:                 $dot = strrpos($attachment_name, '.');
493:                 if ($dot === false) {
494:                     $attachment_name .= '[1]';
495:                 } else {
496:                     $attachment_name = substr($attachment_name, 0, $dot)
497:                         . '[1]' . substr($attachment_name, $dot);
498:                 }
499:             }
500:         }
501: 
502:         try {
503:             $vfs->write($dir, $attachment_name, $attachment_file, true);
504:         } catch (Horde_Vfs_Exception $e) {
505:             throw new Whups_Exception($e);
506:         }
507:     }
508: 
509:     /**
510:      * Removes an attachment from this ticket.
511:      *
512:      * @param string $attachment_name  The name of the attachment.
513:      *
514:      * @throws Whups_Exception
515:      */
516:     public function deleteAttachment($attachment_name)
517:     {
518:         if (!isset($GLOBALS['conf']['vfs']['type'])) {
519:             throw new Whups_Exception(
520:                 _("The VFS backend needs to be configured to enable attachment uploads."),
521:                 'horde.error');
522:         }
523: 
524:         try {
525:             $vfs = $GLOBALS['injector']
526:                 ->getInstance('Horde_Core_Factory_Vfs')
527:                 ->create();
528:         } catch (Horde_Vfs_Exception $e) {
529:             throw Whups_Exception($e);
530:         }
531: 
532:         $dir = Whups::VFS_ATTACH_PATH . '/' . $this->_id;
533:         if (!$vfs->exists($dir, $attachment_name)) {
534:             throw new Whups_Exception(
535:                 sprintf(_("Attachment %s not found."),
536:                         $attachment_name),
537:                 'horde.error');
538:         }
539: 
540:         try {
541:             $vfs->deleteFile($dir, $attachment_name);
542:         } catch (Horde_Vfs_Exception $e) {
543:             throw new Whups_Exception($e);
544:         }
545:     }
546: 
547:     /**
548:      * Returns a list of all files that have been attached to this ticket,
549:      * whether they still exist or not.
550:      *
551:      * @return array  The list of file attachments
552:      * @throws Whups_Exception
553:      */
554:     public function listAllAttachments()
555:     {
556:         $files = array();
557:         $history = $GLOBALS['whups_driver']->getHistory($this->_id);
558:         foreach ($history as $row) {
559:             if (isset($row['changes'])) {
560:                 foreach ($row['changes'] as $change) {
561:                     if (isset($change['type']) &&
562:                         $change['type'] == 'attachment') {
563:                         $files[] = $change['value'];
564:                     }
565:                 }
566:             }
567:         }
568: 
569:         return array_unique($files);
570:     }
571: 
572:     /**
573:      * Redirects the browser to this ticket's view.
574:      */
575:     public function show()
576:     {
577:         Whups::urlFor('ticket', $this->_id, true)->redirect();
578:     }
579: 
580:     /**
581:      * Returns a <link> tag for this ticket's feed.
582:      *
583:      * @return string  A full <link> tag.
584:      */
585:     public function feedLink()
586:     {
587:         return '<link rel="alternate" type="application/rss+xml" title="'
588:             . htmlspecialchars('[#' . $this->getId() . '] ' . $this->get('summary'))
589:             . '" href="' . Whups::urlFor('ticket_rss', $this->getId(), true, -1)
590:             . '" />';
591:     }
592: 
593:     /**
594:      * Sets exclusive read permissions on a comment to a certain group.
595:      *
596:      * @param integer $commentId  The id of the comment to restrict.
597:      * @param string  $group      The group name to limit access by.
598:      *
599:      * @return integer  The permission id.
600:      */
601:     static public function addCommentPerms($commentId, $group)
602:     {
603:         if (!empty($group)) {
604:             $perms = $GLOBALS['injector']
605:                 ->getInstance('Horde_Perms');
606:             $perms_core = $GLOBALS['injector']
607:                 ->getInstance('Horde_Core_Perms');
608:             if (!$perms->exists('whups')) {
609:                 $perm = $perms_core->newPermission('whups');
610:                 $perm->addDefaultPermission(Horde_Perms::ALL, false);
611:                 $perms->addPermission($perm);
612:             }
613:             if (!$perms->exists('whups:comments')) {
614:                 $perms->addPermission($perms_core->newPermission('whups:comments'));
615:             }
616:             $perm = $perms_core->newPermission('whups:comments:' . $commentId);
617:             $perm->addGroupPermission($group, Horde_Perms::READ, false);
618:             return $perms->addPermission($perm);
619:         }
620:     }
621: 
622:     /**
623:      * Sets all properties of the ticket necessary to display the
624:      * TicketDetailsForm.
625:      *
626:      * @param Horde_Variables $vars  The form variables object to set info in.
627:      */
628:     public function setDetails(Horde_Variables &$vars)
629:     {
630:         $vars->set('id', $this->getId());
631:         foreach ($this->getDetails() as $varname => $value) {
632:             $vars->set($varname, $value);
633:         }
634: 
635:         /* User formatting. */
636:         $vars->set('user_id_requester',
637:                    Whups::formatUser($this->get('user_id_requester')));
638:         $vars->set('user_id_owner', Whups::getOwners($this->_id));
639: 
640:         /* Attachments. */
641:         $attachments = array();
642:         try {
643:             $files = Whups::getAttachments($this->_id);
644:         } catch (Whups_Exception $e) {
645:             $GLOBALS['notification']->push($e->getMessage());
646:         }
647:         if ($files) {
648:             foreach ($files as $file) {
649:                 $attachments[] = Whups::attachmentUrl(
650:                     $this->_id, $file, $this->_details['queue']);
651:             }
652:             $vars->set('attachments', implode("<br />\n", $attachments));
653:         }
654:     }
655: 
656:     /**
657:      * Notifies all appropriate people of the creation/update of this ticket.
658:      *
659:      * @param string  $author   Who created/changed the ticket?
660:      * @param boolean $isNew    Is this a new ticket or a change to an existing
661:      *                          one?
662:      * @param array $listeners  The list of listener that should receive the
663:      *                          notification, with user names as keys and user
664:      *                          roles as values. If empty, the list will be
665:      *                          created automatically.
666:      */
667:     public function notify($author, $isNew, $listeners = array())
668:     {
669:         global $conf, $whups_driver;
670: 
671:         /* Get the attributes for this ticket. */
672:         $attributes = $whups_driver->getAttributesForType($this->get('type'));
673: 
674:         $fields = array(
675:             'queue' => _("Queue"),
676:             'version' => _("Version"),
677:             'type' => _("Type"),
678:             'state' => _("State"),
679:             'priority' => _("Priority"),
680:             'due' => _("Due"),
681:         );
682: 
683:         $field_names = array_merge($fields, array(_("Created By"),
684:                                                   _("Updated By"),
685:                                                   _("Summary"),
686:                                                   _("Owners"),
687:                                                   _("New Attachment"),
688:                                                   _("Deleted Attachment")));
689:         foreach ($attributes as $attribute) {
690:             $field_names[] = $attribute['human_name'];
691:         }
692: 
693:         /* Find the longest translated field name. */
694:         $length = 0;
695:         foreach ($field_names as $field_name) {
696:             $length = max($length, Horde_String::length($field_name));
697:         }
698:         $wrap_break = "\n" . str_repeat(' ', $length + 2) . '| ';
699:         $wrap_width = 73 - $length;
700: 
701:         /* Ticket URL. */
702:         $url = sprintf(_("Ticket URL: %s"),
703:                        Whups::urlFor('ticket', $this->_id, true, -1));
704: 
705:         /* Ticket properties. */
706:         $table = "------------------------------------------------------------------------------\n"
707:             . ' ' . Horde_String::pad(_("Ticket"), $length) . ' | '
708:             . $this->_id . "\n" . ' '
709:             . Horde_String::pad($isNew ? _("Created By") : _("Updated By"), $length)
710:             . ' | ' . Whups::formatUser($author) . "\n";
711:         if (isset($this->_changes['summary'])) {
712:             $table .= '-' . Horde_String::pad(_("Summary"), $length) . ' | '
713:                 . Horde_String::wrap($this->_changes['summary']['from'],
714:                                $wrap_width, $wrap_break)
715:                 . "\n" . '+' . Horde_String::pad(_("Summary"), $length) . ' | '
716:                 . Horde_String::wrap($this->get('summary'), $wrap_width, $wrap_break)
717:                 . "\n";
718:         } else {
719:             $table .= ' ' . Horde_String::pad(_("Summary"), $length) . ' | '
720:                 . Horde_String::wrap($this->get('summary'), $wrap_width, $wrap_break)
721:                 . "\n";
722:         }
723: 
724:         foreach ($fields as $field => $label) {
725:             if ($name = $this->get($field . '_name')) {
726:                 if (isset($this->_changes[$field])) {
727:                     $table .= '-' . Horde_String::pad($label, $length) . ' | '
728:                         . Horde_String::wrap($this->_changes[$field]['from_name'],
729:                                        $wrap_width, $wrap_break)
730:                         . "\n" . '+' . Horde_String::pad($label, $length) . ' | '
731:                         . Horde_String::wrap($name, $wrap_width, $wrap_break) . "\n";
732:                 } else {
733:                     $table .= ' ' . Horde_String::pad($label, $length) . ' | '
734:                         . Horde_String::wrap($name, $wrap_width, $wrap_break) . "\n";
735:                 }
736:             }
737:         }
738: 
739:         /* Attribute changes. */
740:         foreach ($attributes as $id => $attribute) {
741:             $attribute_id = 'attribute_' . $id;
742:             $label = $attribute['human_name'];
743:             if (isset($this->_changes[$attribute_id])) {
744:                 $table .= '-' . Horde_String::pad($label, $length) . ' | '
745:                     . Horde_String::wrap($this->_changes[$attribute_id]['from'],
746:                                    $wrap_width, $wrap_break)
747:                     . "\n" . '+' . Horde_String::pad($label, $length) . ' | '
748:                     . Horde_String::wrap($this->_changes[$attribute_id]['to'],
749:                                    $wrap_width, $wrap_break)
750:                     . "\n";
751:             } else {
752:                 $table .= ' ' . Horde_String::pad($label, $length) . ' | '
753:                     . Horde_String::wrap($this->get($attribute_id),
754:                                    $wrap_width, $wrap_break)
755:                     . "\n";
756:             }
757:         }
758: 
759:         /* Show any change in ticket owners. */
760:         $owners = $oldOwners = Horde_String::wrap(
761:             Whups::getOwners($this->_id, false, true),
762:             $wrap_width, $wrap_break);
763:         if (isset($this->_changes['oldowners'])) {
764:             $oldOwners = Horde_String::wrap(
765:                 Whups::getOwners($this->_id, false, true,
766:                                  $this->_changes['oldowners']),
767:                 $wrap_width, $wrap_break);
768:         }
769:         if ($owners != $oldOwners) {
770:             $table .= '-' . Horde_String::pad(_("Owners"), $length) . ' | '
771:                 . $oldOwners . "\n" . '+' . Horde_String::pad(_("Owners"), $length)
772:                 . ' | ' . $owners . "\n";
773:         } else {
774:             $table .= ' ' . Horde_String::pad(_("Owners"), $length) . ' | '
775:                 . $owners . "\n";
776:         }
777: 
778:         /* New Attachments. */
779:         if (isset($this->_changes['attachment'])) {
780:             $table .= '+' . Horde_String::pad(_("New Attachment"), $length) . ' | '
781:                 . $this->_changes['attachment']['to']['name'] . "\n";
782:         }
783:         if (!empty($this->_changes['attachments'])) {
784:             foreach ($this->_changes['attachments']['to'] as $attachment) {
785:                 $table .= '+' . Horde_String::pad(_("New Attachment"), $length)
786:                     . ' | ' . $attachment['name'] . "\n";
787:             }
788:         }
789: 
790:         /* Deleted Attachments. */
791:         if (isset($this->_changes['delete-attachment'])) {
792:             $table .= '+' . Horde_String::pad(_("Deleted Attachment"), $length)
793:                 . ' | ' . $this->_changes['delete-attachment']['to'] . "\n";
794:         }
795: 
796:         $table .= "------------------------------------------------------------------------------";
797: 
798:         /* Add the "do not reply" tag if we don't monitor incoming  mail. */
799:         if (empty($conf['mail']['reply'])) {
800:             $dont_reply = _("DO NOT REPLY TO THIS MESSAGE. THIS EMAIL ADDRESS IS NOT MONITORED.") . "\n\n";
801:         } else {
802:             $dont_reply = '';
803:         }
804: 
805:         /* Build message template. */
806:         $view = new Horde_View(array('templatePath' => WHUPS_BASE . '/config'));
807:         $view->ticket_url = $url;
808:         $view->table = $table;
809:         $view->dont_reply = empty($conf['mail']['reply']);
810:         $view->date = strftime($GLOBALS['prefs']->getValue('date_format'));
811:         $view->auth_name = $GLOBALS['injector']
812:             ->getInstance('Horde_Core_Factory_Identity')
813:             ->create()
814:             ->getValue('fullname');
815:         if (empty($view->auth_name)) {
816:             $view->auth_name = $GLOBALS['registry']->getAuth('bare');
817:         }
818: 
819:         /* Get queue specific notification message text, if available. */
820:         $message_file = WHUPS_BASE . '/config/'
821:             . ($isNew ? 'create_email.plain' : 'notify_email.plain');
822:         if (file_exists($message_file . '.' . $this->get('queue') . '.php')) {
823:             $message_file .= '.' . $this->get('queue') . '.php';
824:         } elseif (file_exists($message_file . '.local.php')) {
825:             $message_file .= '.local.php';
826:         } else {
827:             $message_file .= '.php';
828:         }
829:         $message_file = basename($message_file);
830: 
831:         /* Include Re: if the ticket isn't new for easy
832:          * filtering/eyeballing. */
833:         $subject = $this->get('summary');
834:         if (!$isNew) {
835:             $subject = 'Re: ' . $subject;
836:         }
837: 
838:         if (empty($listeners)) {
839:             if ($conf['mail']['incl_resp'] ||
840:                 !count(current($whups_driver->getOwners($this->_id)))) {
841:                 /* Include all responsible.  */
842:                 $listeners = $whups_driver->getListeners(
843:                     $this->_id, true, true, true);
844:             } else {
845:                 /* Don't include all responsible unless ticket is assigned. */
846:                 $listeners = $whups_driver->getListeners(
847:                     $this->_id, true, true, false);
848:             }
849: 
850:             /* Notify both old and new queue users if the queue has changed. */
851:             if (isset($this->_changes['queue'])) {
852:                 foreach ($whups_driver->getQueueUsers($this->_changes['queue']['from_name']) as $user) {
853:                     $listeners[$user] = 'queue';
854:                 }
855:             }
856:         }
857: 
858:         /* Pass off to Whups_Driver::mail() to do the actual comment fetching,
859:          * permissions checks, etc. */
860:         $whups_driver->mail(array('ticket' => $this,
861:                                   'recipients' => $listeners,
862:                                   'subject' => $subject,
863:                                   'view' => $view,
864:                                   'template' => $message_file,
865:                                   'from' => $author,
866:                                   'new' => $isNew));
867:     }
868: 
869:     /**
870:      * Returns a plain text representation of a ticket.
871:      */
872:     public function toString()
873:     {
874:         $fields = array('queue' => _("Queue"),
875:                         'version' => _("Version"),
876:                         'type' => _("Type"),
877:                         'state' => _("State"),
878:                         'priority' => _("Priority"),
879:                         'due' => _("Due"));
880: 
881:         /* Find longest translated field name. */
882:         $length = 0;
883:         foreach (array_merge($fields, array(_("Summary"), _("Owners")))
884:                  as $field) {
885:             $length = max($length, Horde_String::length($field));
886:         }
887:         $wrap_break = "\n" . str_repeat(' ', $length + 2) . '| ';
888:         $wrap_width = 73 - $length;
889: 
890:         /* Ticket properties. */
891:         $message = ' ' . Horde_String::pad(_("Ticket"), $length) . ' | '
892:             . $this->_id . "\n" . ' ' . Horde_String::pad(_("Summary"), $length)
893:             . ' | ' . Horde_String::wrap($this->get('summary'),
894:                                    $wrap_width, $wrap_break)
895:             . "\n";
896: 
897:         foreach ($fields as $field => $label) {
898:             if ($name = $this->get($field . '_name')) {
899:                 $message .= ' ' . Horde_String::pad($label, $length) . ' | '
900:                     . Horde_String::wrap($name, $wrap_width, $wrap_break) . "\n";
901:             }
902:         }
903: 
904:         $message .= ' ' . Horde_String::pad(_("Owners"), $length) . ' | '
905:             . Horde_String::wrap(Whups::getOwners($this->_id, false, true),
906:                            $wrap_width, $wrap_break)
907:             . "\n";
908: 
909:         return $message;
910:     }
911: 
912:     public function __toString()
913:     {
914:         return $this->toString();
915:     }
916: 
917:     /**
918:      * Adds ticket attribute values to the ticket's details, and returns the
919:      * list of attributes.
920:      *
921:      * @return array  List of ticket attribute hashes.
922:      */
923:     public function addAttributes()
924:     {
925:         $attributes = $GLOBALS['whups_driver']
926:             ->getAllTicketAttributesWithNames($this->getId());
927:         foreach ($attributes as $attribute_id => $attribute) {
928:             $this->set('attribute_' . $attribute_id, $attribute['value']);
929:         }
930:         return $attributes;
931:     }
932: 
933: }
934: 
API documentation generated by ApiGen