public function render($transaction, &$vars) { global $prefs, $conf, $registry; static $canUpdate, $comment_count = 0; if (!isset($canUpdate)) { $canUpdate = $GLOBALS['registry']->getAuth() && Whups::hasPermission($vars->get('queue'), 'queue', 'update'); } $comment = ''; $private = false; $changes = array(); $changelist = $vars->get('changes'); if (!$changelist) { return ''; } /* Format each change in this history entry, including comments, * etc. */ foreach ($changelist as $change) { switch ($change['type']) { case 'summary': $changes[] = sprintf(_("Summary ⇒ %s"), htmlspecialchars($change['value'])); break; case 'message': $ticket = $vars->get('ticket_id'); try { if (Whups::hasMessage($ticket, $change['value'])) { $changes[] = implode(' ', Whups::messageUrls($ticket, $change['value'], $vars->get('queue'))); } } catch (Whups_Exception $e) { } break; case 'delete-attachment': $changes[] = _("Deleted Original Message"); break; case 'attachment': $ticket = $vars->get('ticket_id'); try { if ($file = Whups::getAttachments($ticket, $change['value'])) { $changes[] = sprintf(_("New Attachment: %s"), implode(' ', Whups::attachmentUrl($ticket, $file, $vars->get('queue')))); } else { $changes[] = sprintf(_("New Attachment: %s"), htmlspecialchars($change['value'])); } } catch (Whups_Exception $e) { $changes[] = sprintf(_("New Attachment: %s"), htmlspecialchars($change['value'])); } break; case 'delete-attachment': $changes[] = sprintf(_("Deleted Attachment: %s"), htmlspecialchars($change['value'])); break; case 'assign': $changes[] = sprintf(_("Assigned to %s"), Whups::formatUser($change['value'], false, true, true)); break; case 'unassign': $changes[] = sprintf(_("Taken from %s"), Whups::formatUser($change['value'], false, true, true)); break; case 'comment': $comment = $change['comment']; $private = !empty($change['private']); if ($comment) { $reply = Horde::link(Horde::url($canUpdate ? 'ticket/update.php' : 'ticket/comment.php')->add(array('id' => $vars->get('ticket_id'), 'transaction' => $transaction))) . _("Reply to this comment") . '</a>'; } break; case 'queue': $changes[] = sprintf(_("Queue ⇒ %s"), htmlspecialchars($change['label'])); break; case 'version': $changes[] = sprintf(_("Version ⇒ %s"), htmlspecialchars($change['label'])); break; case 'type': $changes[] = sprintf(_("Type ⇒ %s"), htmlspecialchars($change['label'])); break; case 'state': $changes[] = sprintf(_("State ⇒ %s"), htmlspecialchars($change['label'])); break; case 'priority': $changes[] = sprintf(_("Priority ⇒ %s"), htmlspecialchars($change['label'])); break; case 'attribute': $changes[] = sprintf(_("%s ⇒ %s"), htmlspecialchars($change['label']), htmlspecialchars($change['human'])); break; case 'due': if ($change['label']) { $changes[] = sprintf(_("Due ⇒ %s"), strftime($prefs->getValue('date_format'), $change['label'])); } break; } } if ($comment) { $flowed = new Horde_Text_Flowed($comment, 'UTF-8'); $flowed->setDelSp(true); $comment = $flowed->toFlowed(false); $comment = $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($comment, array('text2html', 'simplemarkup', 'highlightquotes'), array(array('parselevel' => Horde_Text_Filter_Text2html::MICRO), array('html' => true), array('hideBlocks' => true))); if ($prefs->getValue('autolink_tickets') && $conf['prefs']['autolink_terms']) { // Replace existing links by tokens to avoid double linking. $comment = preg_replace_callback('/<a.*?<\\/a>/', array($this, '_writeTokens'), $comment); $comment = preg_replace_callback('/(' . $conf['prefs']['autolink_terms'] . ')\\s*#?(\\d+)/i', array($this, '_autolink'), $comment); $comment = preg_replace_callback('/\\0/', array($this, '_readTokens'), $comment); } $comment_count++; if ($private) { $comment_label = Horde::img('locked.png') . sprintf(_("Comment #%d (Private)"), $comment_count); } else { $comment_label = sprintf(_("Comment #%d"), $comment_count); } array_unshift($changes, '<a href="#c' . $comment_count . '" id="c' . $comment_count . '">' . $comment_label . '</a>'); } if (count($changes)) { // Admins can delete entries. $delete_link = ''; if (Whups::hasPermission($vars->get('queue'), 'queue', Horde_Perms::DELETE)) { $delete_link = Horde::url('ticket/delete_history.php')->add(array('transaction' => $transaction, 'id' => $vars->get('ticket_id'), 'url' => Whups::urlFor('ticket', $vars->get('ticket_id'), true)))->link(array('title' => _("Delete entry"), 'onclick' => 'return window.confirm(\'' . addslashes(_("Permanently delete entry?")) . '\');')) . Horde::img('delete.png', _("Delete entry")) . '</a>'; } Horde::startBuffer(); $class = $private ? 'pc' : 'c'; ?> <div id="t<?php echo (int) $transaction; ?> "> <table cellspacing="0" width="100%"> <tr> <td width="20%" class="<?php echo $class; ?> _l nowrap" valign="top"><?php echo strftime($prefs->getValue('date_format') . ' ' . $prefs->getValue('time_format'), $vars->get('timestamp')); ?> </td> <td width="20%" class="<?php echo $class; ?> _m" valign="top"><?php echo $vars->get('user_id') ? Whups::formatUser($vars->get('user_id'), false, true, true) : ' '; ?> </td> <td width="30%" class="<?php echo $class; ?> _m" valign="top"><?php echo implode('<br />', $changes); ?> </td> <td width="30%" class="<?php echo $class; ?> _r rightAlign" valign="top"><?php if ($comment && !$private) { echo $reply . ' '; } echo $delete_link; ?> </td> </tr> <?php if ($comment) { ?> <tr><td colspan="4" class="<?php echo $class; ?> _b"> <div class="comment-body fixed"> <?php echo $comment; ?> </div> </td></tr> <?php } else { ?> <tr><td colspan="4" class="c_b"> </td></tr> <?php } ?> </table> </div> <?php $html = Horde::endBuffer(); return $html; } return ''; }
* did not receive this file, see http://www.horde.org/licenses/bsdl.php. * * @author Jan Schneider <*****@*****.**> */ require_once __DIR__ . '/../lib/Application.php'; Horde_Registry::appInit('whups'); $vars = Horde_Variables::getDefaultVariables(); $ticket = Whups::getCurrentTicket(); $view = $injector->createInstance('Horde_View'); try { $files = $ticket->listAllAttachments(); } catch (Whups_Exception $e) { $notification->push($e); } if ($files) { $format = array($prefs->getValue('date_format'), $prefs->getValue('time_format')); $attachments = Whups::getAttachments($ticket->getId()); $view->attachments = array(); foreach ($files as $file) { $view->attachments[] = array_merge(array('timestamp' => $file['timestamp'], 'date' => strftime($format[0], $file['timestamp']) . ' ' . strftime($format[1], $file['timestamp']), 'user' => Whups::formatUser(Whups::getUserAttributes($file['user_id']), true, true, true)), Whups::attachmentUrl($ticket->getId(), $attachments[$file['value']], $ticket->get('queue'))); } } Whups::addTopbarSearch(); Whups::addFeedLink(); $page_output->addLinkTag($ticket->feedLink()); $page_output->addScriptFile('tables.js', 'horde'); $page_output->header(array('title' => sprintf(_("Attachments for %s"), '[#' . $id . '] ' . $ticket->get('summary')))); $notification->notify(array('listeners' => 'status')); echo Whups::getTicketTabs($vars, $ticket->getId())->render('attachments'); echo $view->render('ticket/attachments'); $page_output->footer();
/** * Sends email notifications to a list of recipients. * * We do some ugly work in here to make sure that no one gets comments * mailed to them that they shouldn't see (because of group permissions). * * @param array $opts Option hash with notification information. * Possible values: * - ticket: (Whups_Ticket) A ticket. If not set, * this is assumed to be a reminder * message. * - recipients: (array|string) The list of recipients, * with user names as keys and user roles * as values. * - subject: (string) The email subject. * - view: (Horde_View) The view object for the * message text. * - template: (string) The template file for the * message text. * - from: (string) The email sender. * - new: (boolean, optional) Whether the passed * ticket was just created. */ public function mail(array $opts) { global $conf, $registry, $prefs; $opts = array_merge(array('ticket' => false, 'new' => false), $opts); /* Set up recipients and message headers. */ $mail = new Horde_Mime_Mail(array('X-Whups-Generated' => 1, 'User-Agent' => 'Whups ' . $registry->getVersion(), 'Precedence' => 'bulk', 'Auto-Submitted' => $opts['ticket'] ? 'auto-replied' : 'auto-generated')); $mail_always = null; if ($opts['ticket'] && !empty($conf['mail']['always_copy'])) { $mail_always = $conf['mail']['always_copy']; if (strpos($mail_always, '<@>') !== false) { try { $mail_always = str_replace('<@>', $opts['ticket']->get('queue_name'), $mail_always); } catch (Whups_Exception $e) { $mail_always = null; } } if ($mail_always && !isset($opts['recipients'][$mail_always])) { $opts['recipients'][$mail_always] = 'always'; } } if ($opts['ticket'] && ($queue = $this->getQueue($opts['ticket']->get('queue'))) && !empty($queue['email'])) { $mail->addHeader('From', $queue['email']); } elseif (!empty($conf['mail']['from_addr'])) { $mail->addHeader('From', $conf['mail']['from_addr']); } else { $mail->addHeader('From', Whups::formatUser($opts['from'])); } if (!empty($conf['mail']['return_path'])) { $mail->addHeader('Return-Path', $conf['mail']['return_path']); } if ($opts['ticket']) { $opts['subject'] = '[' . $registry->get('name') . ' #' . $opts['ticket']->getId() . '] ' . $opts['subject']; } $mail->addHeader('Subject', $opts['subject']); /* Get our array of comments, sorted in the appropriate order. */ if ($opts['ticket']) { $comments = $this->getHistory($opts['ticket']->getId()); if ($conf['mail']['commenthistory'] == 'new' && count($comments)) { $comments = array_pop($comments); $comments = array($comments); } elseif ($conf['mail']['commenthistory'] != 'chronological') { $comments = array_reverse($comments); } } else { $comments = array(); } /* Don't notify any email address more than once. */ $seen_email_addresses = array(); /* Get VFS handle for attachments. */ if ($opts['ticket']) { $vfs = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')->create(); try { $attachments = Whups::getAttachments($opts['ticket']->getId()); } catch (Whups_Exception $e) { $attachments = array(); Horde::log($e); } } $from = Whups::getUserAttributes($opts['from']); foreach ($opts['recipients'] as $user => $role) { /* Make sure to check permissions as a guest for the 'always_copy' * address, and as the recipient for all others. */ $to = $full_name = ''; if (!empty($mail_always) && $user == $mail_always) { $details = null; $mycomments = Whups::permissionsFilter($comments, 'comment', Horde_Perms::READ, ''); $to = $mail_always; } else { $details = Whups::getUserAttributes($user); if (!empty($details['email'])) { $to = Whups::formatUser($details); $mycomments = Whups::permissionsFilter($comments, 'comment', Horde_Perms::READ, $details['user']); } $full_name = $details['name']; } /* We may have no recipients due to users excluding themselves * from self notifies. */ if (!$to) { continue; } if ($details && $details['type'] == 'user') { $user_prefs = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Prefs')->create('whups', array('user' => $details['user'])); if (($details['user'] == $registry->getAuth() || !$registry->getAuth()) && $from['type'] == 'user' && $details['user'] == $from['user'] && $user_prefs->getValue('email_others_only')) { continue; } } if ($opts['ticket']) { /* Add attachments. */ $attachmentAdded = false; if (empty($GLOBALS['conf']['mail']['link_attach'])) { /* We need to remove all attachments because the attachment * list is potentially limited by permissions. */ $mail->clearParts(); foreach ($mycomments as $comment) { foreach ($comment['changes'] as $change) { if ($change['type'] != 'attachment') { continue; } foreach ($attachments as $attachment) { if ($attachment['name'] != $change['value']) { continue; } if (!isset($attachment['part'])) { $attachment['part'] = new Horde_Mime_Part(); $attachment['part']->setType(Horde_Mime_Magic::filenameToMime($change['value'], false)); $attachment['part']->setDisposition('attachment'); $attachment['part']->setContents($vfs->read(Whups::VFS_ATTACH_PATH . '/' . $opts['ticket']->getId(), $change['value'])); $attachment['part']->setName($change['value']); } $mail->addMimePart($attachment['part']); $attachmentAdded = true; break; } } } } $formattedComment = $this->formatComments($mycomments, $opts['ticket']->getId()); if (!$attachmentAdded && !strlen(trim($formattedComment)) && $details && $details['type'] == 'user' && $user_prefs->getValue('email_comments_only')) { continue; } $opts['view']->comment = $formattedComment; } $addr_ob = new Horde_Mail_Rfc822_Address($to); if ($addr_ob->valid) { $bare_address = $addr_ob->bare_address; if (!empty($seen_email_addresses[$bare_address])) { continue; } $seen_email_addresses[$bare_address] = true; if (empty($full_name) && !is_null($addr_ob->personal)) { $full_name = $addr_ob->personal; } } // Use email address as fallback. if (empty($full_name)) { $full_name = $to; } $opts['view']->full_name = $full_name; $opts['view']->role = $role; $body = $opts['view']->render($opts['template']); if (!strlen(trim($body))) { continue; } $mail->setBody($body); $mail->addHeaderOb(Horde_Mime_Headers_MessageId::create()); if ($opts['ticket']) { $message_id = '<whups-' . $opts['ticket']->getId() . '-' . md5($user) . '@' . $conf['server']['name'] . '>'; if ($opts['new']) { $mail->addHeader('Message-ID', $message_id); } else { $mail->addHeader('In-Reply-To', $message_id); $mail->addHeader('References', $message_id); } } $mail->clearRecipients(); $mail->addHeader('To', $to); try { $mail->send($GLOBALS['injector']->getInstance('Horde_Mail'), true); $entry = sprintf('%s Message sent to %s from "%s"', $_SERVER['REMOTE_ADDR'], $to, $GLOBALS['registry']->getAuth()); Horde::log($entry, 'INFO'); } catch (Horde_Mime_Exception $e) { Horde::log($e, 'ERR'); } } }