Jak správně poslat Template jako přílohu pomocí Nette\Mail
- Petr Motejlek
- Člen | 293
Ahoj,
narazil jsem na menší problém — nevím, jak správně poslat instanci
Template jako přílohu e–mailu. Pomocí metody setHtmlBody() není
problém
Template nastavit jako tělo zprávy, já bych ale potřeboval v rámci jedné
zprávy poslat několik vygenerovaných HTML stránek jako přílohu. Zkoušel
jsem toho docela dost a doteď si nemyslím, že jsem našel to správné —
používám takovouto konstrukci:
<?php
$sablonaKOdeslani = new Template;
$sablonaKOdeslani->setFile(dirname(__FILE__) . '/odeslatEmailem.phtml');
/* Přiřazení proměnných do šablony, např. název klienta, číslo, atp. se
mi sem nechtělo psát, tak je tady jen tehle komentářík ;) */
$mail = new Mail;
$mail->addTo('muj@klient.cz', 'Jméno klienta');
$mail->setFrom('ja@mojefirma.cz', 'Moje jméno');
$mail->setBody("Vizte přílohy. Děkuji.");
$part = $mail->addPart();
$part->setContentType('text/html');
$part->setEncoding(Mail::ENCODING_BASE64);
$part->setBody($sablonaKOdeslani);
$part->setHeader('Content-Disposition', 'attachment; filename="Nějaký soubor.html"');
$mail->send();
?>
Normálně mám zprávu i v HTML formátu, aby to vypadalo hezčejc, teď jsem pro příklad použil jen setBody(). Těch příloh můžu stejným způsobem přidat i víc, v tom mi nic nebrání.
Problém u tohoto přístupu je, že je to pro mě neskutečně odporné — víceméně tam dělám to, co dělá metoda addAttachment(), akorát, že ta (bohužel) počítá s názvem souboru, neumí tedy přidat string jako přílohu (což by vlastně stačilo ;)).
Další nevýhoda tohohle řešení je, že Thunderbird odmítá zobrazit tu přílohu jako přílohu — prostě dělá, že tam žádná příloha není. Gmail ukáže, že příloha je, ale nepojmenuje ji Nějaký soubor.html, ale noname — asi nedokáže přečíst tu hlavičku Content-Disposition, konkrétně tu část filename :(.
Jak ukazovat tu přílohu v TB jsem nakonec vyřešil (ale je to fakt pekelně ošklivý) — metoda addAttachment() po tom, co vytvoří novou část zprávy (part), ji ještě přidá do pole $attachments — do toho pole tedy musím ten $part přidat taky, problém je, že $attachments je private :(. V Mail.php v Nette se tedy musí změnit viditelnost proměnné $attachments alespoň na protected (a rozšířit Mail nějakou vlastní třídou), nebo na public (potom do toho pole můžu přidat ten $part ručně).
Výše popsané zapříčiní, že se příloha ukazuje v TB, ten ale (stejně jako Gmail) nedokáže vyluštit jméno té přílohy a tak použije Část 1.X, kde X je pořadí partu ve zprávě.
Moje otázka tedy zní: tuší někdo, nebo podařilo se někomu, jak jde poslat string jako přílohu e-mailu pomocí Nette\Mail?
Díky moc.
Editoval Petr Motejlek (3. 8. 2009 14:09)
- David Grudl
- Nette Core | 8228
Pomohlo by, když bych přidal další parametr do addAttachment, který by volitelně mohl obsahovat tělo souboru?
Problémy se zobrazováním teď nestíhám analyzovat, každopádně zkus porovnat vygenerované tělo emailu z Nette s obdobným, který vytvoří Gmail nebo TD a kde je zakopaný pes by se mělo rychle ukázat.
- Petr Motejlek
- Člen | 293
Já už jsem to tak nějak analyzoval, když jsem čekal na odpověď ;).
Problém je ten, že ty v setHeader() uděláš escapování celého řetězce, tedy když použiju např. setHeader(‚Content-Disposition‘, ‚attachment; filename=„Bleble“‘);, tak se celá část ‚attachment…‘ zakóduje, to je ale (myslím) v rozporu s RFC, protože Gmail v celém Content-Disposition kóduje jen Bleble, takže od Gmailu přijde zpráva s Content-Disposition: attachment, filename=„<Zakódované Bleble>“;
Co je vlastně u Content-Disposition to filename? Jak se tomu správně říká? Parametr hlavičky? Nevím. Je několik cest, jak tohle řešit — mě osobně přijde asi nejhezčí přidat metody setContentDisposition a getContentDisposition, který by s $filename počitali jako s parametrem — musely by ale manipulovat přímo s $headers (musely by obejít setHeader()). Druhá možnost je upravit setHeader a getHeader, aby počítaly s tím, že každá hlavička může mít tyhle „parametry“ — to by ale nutně obnášelo změnu otisku těch metod, takže by nebylo zpětně kompatibilní :(.
Pak ještě k addAttachment → hodilo by se mít addAttachment (součásná verze, která počítá se souborem) a pak addStringAttachment (?), která by počítala se stringem. Stejná úprava by se musela udělat i s tou druhou metodou, na jejíž jméno si nemůžu vzpomenout, ale přidává inline přílohy ;).
Editoval Petr Motejlek (3. 8. 2009 15:16)
- Petr Motejlek
- Člen | 293
Jenom takovej malej update: všiml jsem si, že když udělám ruční kódování v rámcí té hlavičky, tak se na Gmailu už název přílohy ukazuje správně — díky tomu ručnímu zakódování už Nette\Mail nenajde v té hlavičce nic, co by musel kódovat, tak se na automatické kódování vykálí, což je správně. Takže problém se špatným názvem je (snad) možné vyřešit bez zásahu do kódu Nette\Mail.
Že se ta příloha neukáže v TB se tím ale nevyřešilo, musím ji přidat do Mail::$attachments, a abych se tam dostal, potřebuju si u toho přepsat viditelnost z private na public, nebo alespoň protected.
Volám teda po zřízení nějaké metody addAttachment pro přílohy, které mají jako zdroj stringu a zároveň navrhuji úpravu v současné (i té nové lepší ;)) metodě addAttachment takovou, že řádek $part->setHeader(‚Content-Disposition‘, ‚attachment; filename=„‘ . basename($file) . ‚“‘); se nahradí řádkem $part->setHeader(‚Content-Disposition‘, ‚attachment; filename=„=?UTF-8?Q?‘ . self::encodeQuotedPrintable(basename($file)) . ‚?=“‘); — tím by se taky mohla odbourat nutnost nějak upravovat setHeader a getHeader; bude stačit jen vydat best practice, že je potřeba při setHeader dělat vlastní kódování řetězců v případě, že ta hlavička má takovýhle parametr.
- David Grudl
- Nette Core | 8228
Na ty hlavičky Content-Disposition se podívám.
Váhám mezi addStringAttachment()
a nepovinným parametrem
$content
pro addAttachment()
, už proto, že by se to
stejně dobře hodilo pro addEmbeddedFile()
. A asi to dopadne
drobným BC breakem, totiž vyhodil bych u těchto metod poměrně málo
užitečný třetí parametr $encoding
(dá se psát čitelnější
$mail->addAttachment('example.zip')->setEncoding(Mail::ENCODING_QUOTED_PRINTABLE);
)
a nahradil ho tím $content
.
- Petr Motejlek
- Člen | 293
Taky mi přijde zbytečný tam mít ten parametr pro kódování, když to jde napsat jinak ;).
Přijde mi, že dávat do addAttachment() další parametr by bylo podivné ;). Metody addStringAttachment() a addEmbeddedString() dávají docela smysl. Když už něco dělat s addAttachment() a addEmbeddedFile(), tak do nich přidat $filename pro nastavení jména souboru v e-mailu (ne vždycky chci mít soubor v e-mailu stejně pojmenovaný jako ten na disku). Určitě taky ale budeš mít pravdu, když řekneš, že addAttachment() a addEmbeddedFile() nebudou dělat nic jiného, než file_get_contents() a potom volat addStringAttachment() a addEmbeddedString() ;).
- David Grudl
- Nette Core | 8228
Šlo by pak
addAttachment('fakename.zip', file_get_contents('realname.zip'))
.
- Petr Motejlek
- Člen | 293
Jo tak ;) Nenapadlo mě, že bys' to chtěl otočit úplně celý — otisk té metody by tedy byl addAttachment($filenameInEmail, $contentType, $data) ? IMHO: Když se už jsi se rozhodl vyhodit ten $encoding, co vyhodit i $contentType — ten se přeci dá nastavit úplně stejně jako to kódování ;). Zůstalo by pak jen addAttachment($filenameInEmail, $data) (obdobně i pro addEmbeddedString() (nebo snad addEmbeddedData()).
- Petr Motejlek
- Člen | 293
Tak takhle jsi to myslel — to je pěkný řešení, jen mi tak přijde, že by bylo IMHO intuitivnější, kdyby se $file jmenovalo $filename, protože tak jako tak, v obou případech, které řešíme, zadávám jméno souboru ^^.
Ještě mrkni na správné kódování té hlavičky a je to zase super :D
- Petr Motejlek
- Člen | 293
Jenom kdyby někoho zajímalo, jak může mít hezky česky pojmenovanou přílohu ještě dřív než s tím David pohne, dá se to udělat např. takto:
<?php
...
$mail->addAttachment('=?UTF-8?Q?' . Mail::encodeQuotedPrintable('Krásný žluťoučký kůň úpěl ďábelské ódy.html') . '?=', $content);
...
?>