Commit a922990e authored by Roman Mátyus's avatar Roman Mátyus
Browse files

v0.2

parent 3485f93f
...@@ -20,7 +20,8 @@ use Nette\Object, ...@@ -20,7 +20,8 @@ use Nette\Object,
* setup: * setup:
* - $tempDir(%appDir%/../temp/mails) * - $tempDir(%appDir%/../temp/mails)
* *
* @author Roman Mátyus * @author Jan Drábek, Roman Mátyus
* @copyright (c) Jan Drábek 2013
* @copyright (c) Roman Mátyus 2013 * @copyright (c) Roman Mátyus 2013
* @license MIT * @license MIT
* @package FileMailer * @package FileMailer
...@@ -47,8 +48,10 @@ class FileMailer extends Object implements IMailer ...@@ -47,8 +48,10 @@ class FileMailer extends Object implements IMailer
{ {
$this->checkRequirements(); $this->checkRequirements();
$content = $message->generateMessage(); $content = $message->generateMessage();
$message_id = self::mailParser($content)->message_id;
$path = $this->tempDir."/".$this->prefix.$message_id; preg_match('/Message-ID: <(?<message_id>\w+)@\w+>/', $content, $matches);
$path = $this->tempDir."/".$this->prefix.$matches['message_id'];
if ($bytes = file_put_contents($path, $content)) if ($bytes = file_put_contents($path, $content))
return $bytes; return $bytes;
else else
...@@ -71,34 +74,56 @@ class FileMailer extends Object implements IMailer ...@@ -71,34 +74,56 @@ class FileMailer extends Object implements IMailer
throw new InvalidArgumentException("Directory '".$this->tempDir."' is not writeable."); throw new InvalidArgumentException("Directory '".$this->tempDir."' is not writeable.");
} }
public function getPrefix()
{
return $this->prefix;
}
/** /**
* Parser of stored files. * Parser of stored files.
* @param string $content * @param string $content
* @param string $filename
* @return StdClass * @return StdClass
*/ */
public static function mailParser($content) public static function mailParser($content, $filename = NULL)
{ {
preg_match("/Message-ID: <[a-zA-Z0-9-]*@[a-zA-Z0-9-]*>/", $content, $match);
$message_id = (isset($match[0])) ? substr($match[0], 13, -2) : NULL;
$message_id = explode("@",$message_id);
$message_id = $message_id[0];
$mess = explode("\r\n\r\n", $content); $mess = explode("\r\n\r\n", $content);
preg_match_all("/[a-zA-Z-]*: .*/", $mess[0], $matches); preg_match_all("/[a-zA-Z-]*: .*/", $mess[0], $matches);
$header = array(); $header = array();
foreach ($matches[0] as $line) { foreach ($matches[0] as $line) {
$temp = explode(": ",$line); $temp = explode(": ",$line);
$header[strtolower($temp[0])] = iconv_mime_decode(str_replace(array("\r","\n","\r\n"),"",$temp[1])); $header[strtolower($temp[0])] = iconv_mime_decode(Strings::trim($temp[1]));
} }
if (isset($header["date"])) if (isset($header["date"]))
$header["date"] = new DateTime($header["date"]); $header["date"] = new DateTime($header["date"]);
if (preg_match("/\r\n\r\n----------/", $content)) { // html mail $message_id = explode("@",$header['message-id']);
$message_id = substr($message_id[0], 1);
$attachments = array();
$mess = array(
"plain" => NULL,
"html" => NULL,
);
if (preg_match("/multipart\/mixed/", $content)) { // mail with attachments
foreach (explode("----------",$content) as $part) {
if (preg_match("/Content-Type: text\/plain; charset=UTF-8/", $part)) {
$tmp = explode("\r\n\r\n", $part);
$mess["plain"] = Strings::trim($tmp[1]);
} elseif (preg_match("/Content-Type: text\/html; charset=UTF-8/", $part)) {
$tmp = explode("\r\n\r\n", $part);
$mess["html"] = Strings::trim($tmp[1]);
} elseif (preg_match("/Content-Disposition: attachment;/", $part)) {
$tmp = explode("\r\n", $part);
unset($tmp[0]);
$part = implode("\r\n", $tmp);
$tmp = explode("\r\n\r\n", $part);
$tmp_header = explode("\r\n", $tmp[0]);
$output = array(
"type" => substr($tmp_header[0], 14),
"encoding" => substr($tmp_header[1], 27),
"filename" => substr($tmp_header[2], 43, -1),
"data" => $tmp[1],
);
$attachments[md5($output['type'].$output['encoding'].$output['filename'].$output['data'])] = (object) $output;
}
}
} elseif (preg_match("/multipart\/alternative/", $content)) { // html mail
$mess = explode("\r\n\r\n----------", $content); $mess = explode("\r\n\r\n----------", $content);
$mess = substr($mess[1], 10, -22); $mess = substr($mess[1], 10, -22);
$mess = explode("----------", $mess); $mess = explode("----------", $mess);
...@@ -118,7 +143,7 @@ class FileMailer extends Object implements IMailer ...@@ -118,7 +143,7 @@ class FileMailer extends Object implements IMailer
$temp_mess[$type] = implode("\r\n",$temp_mess[$type]); $temp_mess[$type] = implode("\r\n",$temp_mess[$type]);
} }
$mess = $temp_mess; $mess = $temp_mess;
} else { } elseif (preg_match("/text\/plain/", $content)) { // plaintext mail
$mess = array( $mess = array(
"plain" => $mess[1], "plain" => $mess[1],
"html" => NULL, "html" => NULL,
...@@ -127,13 +152,20 @@ class FileMailer extends Object implements IMailer ...@@ -127,13 +152,20 @@ class FileMailer extends Object implements IMailer
return (object) array_merge( return (object) array_merge(
array( array(
"filename" => $filename,
"message_id" => $message_id, "message_id" => $message_id,
"header" => $header, "header" => $header,
"plain" => $mess['plain'], "plain" => $mess['plain'],
"html" => $mess['html'], "html" => $mess['html'],
"raw" => $content, "raw" => $content,
"attachments" => $attachments,
), ),
$header $header
); );
} }
public function getPrefix()
{
return $this->prefix;
}
} }
\ No newline at end of file
{* {*
* Template for MailPanel. * Template for MailPanel.
* *
* @author Roman Mátyus * @author Jan Drábek, Roman Mátyus
* @copyright (c) Jan Drábek 2013
* @copyright (c) Roman Mátyus 2013 * @copyright (c) Roman Mátyus 2013
* @license MIT * @license MIT
* @package FileMailer * @package FileMailer
...@@ -19,17 +20,17 @@ ...@@ -19,17 +20,17 @@
position: relative; position: relative;
} }
#mailpanel-count { #mailpanel-count {
background: red; background: red;
border-radius: 8px; border-radius: 8px;
font-size: 8px; font-size: 8px;
color: white; color: white;
font-weight: 800; font-weight: 800;
margin-left: -8px; margin-left: -8px;
z-index: 2; z-index: 2;
position: absolute; position: absolute;
width: 15px; width: 15px;
height: 15px; height: 15px;
text-align: center; text-align: center;
} }
img#mailpanel-icon { img#mailpanel-icon {
z-index: 1; z-index: 1;
...@@ -39,12 +40,22 @@ img#mailpanel-icon { ...@@ -39,12 +40,22 @@ img#mailpanel-icon {
#nette-debug div.nette-mailPanel-box { #nette-debug div.nette-mailPanel-box {
border:1px solid silver; border:1px solid silver;
} }
#nette-debug a.nette-mailPanel-highlight {
color: red;
}
#nette-debug a.nette-mailPanel-highlight:focus,
#nette-debug a.nette-mailPanel-highlight:active,
#nette-debug a.nette-mailPanel-highlight:hover {
color: white;
}
</style> </style>
<h1>MailPanel: {if $countAll==0}no{else}{$countAll}{/if} mail{if $countAll>1}s{/if}, {$countNew} new</h1> <h1>MailPanel: {if $countAll==0}no{else}{$countAll}{/if} mail{if $countAll>1}s{/if}, {$countNew} new</h1>
<div n:if="$countAll>0"><a href="/?mail-panel=delete-all">Delete All</a></div> <div n:if="$countAll>0"><a href="/?mail-panel=delete-all">Delete All</a></div>
<div n:if="$countAll>0" class="nette-inner"> <div n:if="$countAll>0" class="nette-inner">
<div n:foreach="$messages as $message"> <div n:foreach="$messages as $message">
<a class="nette-toggler" rel="#nette-mailPanel-message-{$message->message_id}" href="#">{$message->date} {$message->subject} ►</a> <a class="nette-toggler{if $iterator->counter<=$countNew} nette-mailPanel-highlight{/if}" rel="#nette-mailPanel-message-{$message->message_id}" href="#" >{$message->date|date:"d.m.Y H:i:s"} {$message->subject} ►</a>
<div id="nette-mailPanel-message-{$message->message_id}" class="nette-collapsed"> <div id="nette-mailPanel-message-{$message->message_id}" class="nette-collapsed">
<table n:if="!empty($show)" n:inner-foreach="$show as $item"> <table n:if="!empty($show)" n:inner-foreach="$show as $item">
<tr n:if="$message->$item"> <tr n:if="$message->$item">
...@@ -54,14 +65,12 @@ img#mailpanel-icon { ...@@ -54,14 +65,12 @@ img#mailpanel-icon {
</table> </table>
<a class="nette-toggler" rel="#nette-mailPanel-message-header-{$message->message_id}" href="#">header ►</a> <a class="nette-toggler" rel="#nette-mailPanel-message-header-{$message->message_id}" href="#">header ►</a>
<span id="nette-mailPanel-message-header-{$message->message_id}" class="nette-collapsed"> <span id="nette-mailPanel-message-header-{$message->message_id}" class="nette-collapsed">
<div class="nette-mailPanel-box">
<table n:inner-foreach="$message->header as $key => $value"> <table n:inner-foreach="$message->header as $key => $value">
<tr> <tr>
<td>{$key}</td> <td>{$key|firstUpper}:</td>
<td>{$value}</td> <td>{$value}</td>
</tr> </tr>
</table> </table>
</div>
</span> </span>
<a class="nette-toggler" rel="#nette-mailPanel-message-plain-{$message->message_id}" href="#">plain ►</a> <a class="nette-toggler" rel="#nette-mailPanel-message-plain-{$message->message_id}" href="#">plain ►</a>
<span id="nette-mailPanel-message-plain-{$message->message_id}" class="nette-collapsed"> <span id="nette-mailPanel-message-plain-{$message->message_id}" class="nette-collapsed">
...@@ -72,7 +81,11 @@ img#mailpanel-icon { ...@@ -72,7 +81,11 @@ img#mailpanel-icon {
<a n:if="$message->html" class="nette-toggler" rel="#nette-mailPanel-message-html-{$message->message_id}" href="#">html ►</a> <a n:if="$message->html" class="nette-toggler" rel="#nette-mailPanel-message-html-{$message->message_id}" href="#">html ►</a>
<span n:if="$message->html" id="nette-mailPanel-message-html-{$message->message_id}" class="nette-collapsed"> <span n:if="$message->html" id="nette-mailPanel-message-html-{$message->message_id}" class="nette-collapsed">
<div class="nette-mailPanel-box"> <div class="nette-mailPanel-box">
{!$message->html} {var $mailContent = str_replace(array("'","\n","\r","\t"),array("&apos;","","",""),$message->html)}
<iframe id="nette-mailPanel-message-html-iframe-{$message->message_id}"></iframe>
<script type="text/javascript">
document.getElementById('nette-mailPanel-message-html-iframe-{!$message->message_id}').contentWindow.document.write('{!$mailContent}');
</script>
</div> </div>
</span> </span>
<a class="nette-toggler" rel="#nette-mailPanel-message-raw-{$message->message_id}" href="#">raw ►</a> <a class="nette-toggler" rel="#nette-mailPanel-message-raw-{$message->message_id}" href="#">raw ►</a>
...@@ -81,6 +94,18 @@ img#mailpanel-icon { ...@@ -81,6 +94,18 @@ img#mailpanel-icon {
<pre>{$message->raw}</pre> <pre>{$message->raw}</pre>
</div> </div>
</span> </span>
<a n:if="$message->attachments" class="nette-toggler" rel="#nette-mailPanel-message-attachments-{$message->message_id}" href="#">attachments ►</a>
<span n:if="$message->attachments" id="nette-mailPanel-message-attachments-{$message->message_id}" class="nette-collapsed">
<div class="nette-mailPanel-box">
<ul n:inner-foreach="$message->attachments as $filehash => $attachment">
<li>
<a href="/?mail-panel=download&mail-panel-mail={$message->filename}&mail-panel-file={$filehash}">
{$attachment->filename}
</a>
</li>
</ul>
</div>
</span>
</div> </div>
</div> </div>
</div> </div>
\ No newline at end of file
<?php <?php
namespace RM; namespace RM;
use Nette\Application\UI\Control, use Nette\DateTime,
Nette\Diagnostics\IBarPanel,
Nette\Http\Request,
Nette\Application\Application, Nette\Application\Application,
Nette\Utils\Finder, Nette\Application\UI\Control,
Nette\DateTime, Nette\Diagnostics\IBarPanel,
Nette\Caching\Cache, Nette\Caching\Cache,
Nette\Caching\IStorage; Nette\Caching\IStorage,
Nette\Http\Response,
Nette\Http\Request,
Nette\Utils\Finder;
/** /**
* Bar panel showing stored mails. * Bar panel showing stored mails.
...@@ -42,7 +43,8 @@ use Nette\Application\UI\Control, ...@@ -42,7 +43,8 @@ use Nette\Application\UI\Control,
* - $show( [from,to] ) * - $show( [from,to] )
* - $hideEmpty(TRUE) * - $hideEmpty(TRUE)
* *
* @author Roman Mátyus * @author Jan Drábek, Roman Mátyus
* @copyright (c) Jan Drábek 2013
* @copyright (c) Roman Mátyus 2013 * @copyright (c) Roman Mátyus 2013
* @license MIT * @license MIT
* @package FileMailer * @package FileMailer
...@@ -61,6 +63,9 @@ class MailPanel extends Control implements IBarPanel { ...@@ -61,6 +63,9 @@ class MailPanel extends Control implements IBarPanel {
/** @var Cache */ /** @var Cache */
private $cache; private $cache;
/** @var Response */
private $response;
/** @var integer */ /** @var integer */
private $countAll = 0; private $countAll = 0;
...@@ -89,16 +94,20 @@ class MailPanel extends Control implements IBarPanel { ...@@ -89,16 +94,20 @@ class MailPanel extends Control implements IBarPanel {
/** @var bool */ /** @var bool */
public $hideEmpty = TRUE; public $hideEmpty = TRUE;
public function __construct(Application $application, Request $request, FileMailer $fileMailer, IStorage $cacheStorage) public function __construct(Application $application, Request $request, FileMailer $fileMailer, IStorage $cacheStorage, Response $response)
{ {
$this->application = $application; $this->application = $application;
$this->request = $request; $this->request = $request;
$this->fileMailer = $fileMailer; $this->fileMailer = $fileMailer;
$this->cache = new Cache($cacheStorage, "MailPanel"); $this->cache = new Cache($cacheStorage, "MailPanel");
$this->response = $response;
switch($request->getQuery("mail-panel")) { switch($request->getQuery("mail-panel")) {
case 'delete-all': case 'delete-all':
$this->handleDeleteAll(); $this->handleDeleteAll();
break; break;
case 'download':
$this->handleDownload($request->getQuery("mail-panel-mail"),$request->getQuery("mail-panel-file"));
break;
default: default:
break; break;
} }
...@@ -135,24 +144,8 @@ class MailPanel extends Control implements IBarPanel { ...@@ -135,24 +144,8 @@ class MailPanel extends Control implements IBarPanel {
return ob_get_clean(); return ob_get_clean();
} }
/**
* Delete all stored mails from filesystem an cache.
*/
public function handleDeleteAll()
{
foreach (Finder::findFiles('*')->in($this->fileMailer->tempDir) as $file)
unlink($file);
$this->cache->clean(
array(
Cache::ALL => TRUE,
));
header("Location: ".$this->request->getReferer());
exit;
}
/** /**
* Process all messages. * Process all messages.
* @return mixed
*/ */
private function processMessage() private function processMessage()
{ {
...@@ -164,7 +157,7 @@ class MailPanel extends Control implements IBarPanel { ...@@ -164,7 +157,7 @@ class MailPanel extends Control implements IBarPanel {
foreach (Finder::findFiles('*')->in($this->fileMailer->tempDir) as $file) { foreach (Finder::findFiles('*')->in($this->fileMailer->tempDir) as $file) {
$message = $this->cache->load($file->getFilename()); $message = $this->cache->load($file->getFilename());
if ($message === NULL) { if ($message === NULL) {
$message = FileMailer::mailParser(file_get_contents($file)); $message = FileMailer::mailParser(file_get_contents($file),$file->getFilename());
$this->cache->save($file->getFilename(),$message); $this->cache->save($file->getFilename(),$message);
} }
$time = new DateTime; $time = new DateTime;
...@@ -173,7 +166,10 @@ class MailPanel extends Control implements IBarPanel { ...@@ -173,7 +166,10 @@ class MailPanel extends Control implements IBarPanel {
$this->countAll++; $this->countAll++;
$this->messages[] = $message; $this->messages[] = $message;
} }
return TRUE; usort($this->messages, function($a1, $a2) {
return $a2->date->getTimestamp() - $a1->date->getTimestamp();
});
} }
/** /**
...@@ -195,4 +191,32 @@ class MailPanel extends Control implements IBarPanel { ...@@ -195,4 +191,32 @@ class MailPanel extends Control implements IBarPanel {
} }
} }
/**
* Delete all stored mails from filesystem an cache.
*/
public function handleDeleteAll()
{
foreach (Finder::findFiles('*')->in($this->fileMailer->tempDir) as $file)
unlink($file);
$this->cache->clean(
array(
Cache::ALL => TRUE,
));
header("Location: ".$this->request->getReferer());
exit;
}
/**
* Download attachment from file.
*/
public function handleDownload($filename, $filehash)
{
$message = $this->cache->load($filename);
$file = $message->attachments[$filehash];
$this->response->setContentType($file->type, 'UTF-8');
$this->response->setHeader('Content-Disposition', 'attachment; filename="' . $file->filename . '"');
$this->response->setHeader('Content-Length', strlen($file->data));
print base64_decode($file->data);
exit;
}
} }
#FileMailer #FileMailer
Addon for catching mails in Nette to filesystem and showing it in debug bar. Addon for catching e-mails in Nette FW to filesystem and showing it in the Debug Bar.
``` ```
After configuring this addon is e-mails not sended, but ONLY stored to filesystem and showed in Debug Bar! After configuring this addon is e-mails not sended, but ONLY stored to filesystem and showed in Debug Bar!
...@@ -96,3 +96,18 @@ Options for addon customization. ...@@ -96,3 +96,18 @@ Options for addon customization.
| $show | array | `array("subject", "from", "to")` | no | Array of default displayed headers. | | $show | array | `array("subject", "from", "to")` | no | Array of default displayed headers. |
| $autoremove | bool/string | `FALSE` | no | Define limit for automatic remove old e-mails. E.g. `-2 minutes` | | $autoremove | bool/string | `FALSE` | no | Define limit for automatic remove old e-mails. E.g. `-2 minutes` |
| $hideEmpty | bool | `TRUE` | no | Hide empty panel? | | $hideEmpty | bool | `TRUE` | no | Hide empty panel? |
##Changelog
###Version 0.2
- Add support of e-mail attachments
- Sort e-mails by date
- HTML e-mails showed into iframe
- Refactoring
- Fix copyright
- Many bug fixes
###Version 0.1
- Initial commit
\ No newline at end of file
{ {
"name": "rm/filemailer", "name": "rm/filemailer",
"type": "library", "type": "library",
"version": "0.1", "version": "0.2",
"description": "Addon for catch in Nette mails to filesystem and showing it in debug bar.", "description": "Addon for catching e-mails in Nette FW to filesystem and showing it in the Debug Bar.",
"keywords": ["nette", "mailer", "mail", "debugbar"], "keywords": ["nette", "mailer", "mail", "debugbar", "debuging"],
"homepage": "https://github.com/romanmatyus/FileMailer", "homepage": "https://github.com/romanmatyus/FileMailer",
"license": ["MIT"], "license": ["MIT"],
"authors": [ "authors": [
{ {
"name": "Jan Drábek",
"name": "Roman Mátyus" "name": "Roman Mátyus"
} }
], ],
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment