Added Email Logging for Emailnotification class

This commit is contained in:
Frank Schubert
2025-08-22 17:49:54 +02:00
parent 866c3b2ae1
commit 3a86160b38
26 changed files with 1636 additions and 46 deletions

View File

@@ -643,6 +643,65 @@ $pagination_entity_name = "Vorbestellungen";
</div>
</div>
<style>
#email-log-modal p {
margin-top: 8px;
margin-bottom: 8px;
}
</style>
<div class="modal" tabindex="-1" id="email-log-modal" style="max-height: 100vh;">
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content" style="overflow: auto;">
<div class="modal-header">
<h5 class="modal-title">Emailansicht</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col">
<table class="table">
<tr>
<th>Von:</th>
<td id="email-log-from-value" class="text-monospace"></td>
</tr><tr>
<th>An:</th>
<td id="email-log-to-value" class="text-monospace"></td>
</tr><tr>
<th>Gesendet:</th>
<td id="email-log-sent-value" class="text-monospace"></td>
</tr><tr>
<th>Betreff:</th>
<td id="email-log-subject-value" class="text-monospace"></td>
</tr>
</table>
</div>
</div>
<div class="row mb-3" id="email-log-header-section">
<div class="col">
<a href="#" onclick="displayEmailLogHeaders(); return false;"><i class="fas fa-chevron-right fa-fw"></i> Alle Header anzeigen</a>
<div id="email-log-header-view" class="mt-2 text-monospace hidden" style="min-height: 48px; width:100%; border: 1px solid #ccc; padding: 8px; font-family: sans-serif;"></div>
</div>
</div>
<div class="row mb-2" id="email-log-body-section">
<div class="col">
<div id="email-log-body-view" style="min-height: 48px; width:100%; border: 1px solid #000; padding: 8px; font-family: sans-serif; overflow-y: auto;"></div>
</div>
</div>
<div class="row mb-3 hidden" id="email-log-attachments-section">
<div class="col">
<h5>Dateianhänge</h5>
<ul id="email-log-attachment-list" class="list-group"></ul>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
@@ -1690,6 +1749,88 @@ $pagination_entity_name = "Vorbestellungen";
new_remark_elem.prop("readonly", false);
}
/***********************
* Email Log
***********************/
async function displayEmailLogEmail(preorder_id, email_id) {
$("#email-log-from-value").text("");
$("#email-log-to-value").text("");
$("#email-log-log-sent-value").text("");
$("#email-log-subject-value").text("");
$("#email-log-view").empty();
$("#email-log-attachments-section").hide();
$("#email-log-attachment-list").empty();
hideEmailLogHeaders();
if(!email_id) return false;
var resp = await fetch("<?=self::getUrl("Preorder", "api", ["do" => "getLoggedEmail"])?>&eid=" + email_id + "&pid=" + preorder_id);
if(!resp.ok) {
notify("error", "Email konnte nicht geladen werden");
return false;
}
var response = await resp.json();
if(!("result" in response) || typeof response.result !== "object") {
notify("error", "Email konnte nicht geladen werden");
return false;
}
var email = response.result;
$("#email-log-from-value").text(email.from);
$("#email-log-to-value").text(email.to);
$("#email-log-sent-value").text(email.sent);
$("#email-log-subject-value").text(email.subject);
if("headers" in email && typeof email.headers === "object" && Object.keys(email.headers).length > 0) {
for (const [key, value] of Object.entries(email.headers)) {
$("#email-log-header-view").append(
"<strong>" + key + "</strong>: "
+ value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
+ "<br/>");
if(key == "From") {
$("#email-log-from-value").text(value);
}
}
}
if("attachments" in email && typeof email.attachments === "object" && Object.keys(email.attachments).length > 0) {
email.attachments.forEach( (item, index) => {
$("#email-log-attachment-list").append('<li class="list-group-item flex-fill p-1" style="border-radius: 0;"><a href="<?=self::getUrl("EmailLog", "downloadContent")?>?aid=' + item.id + '&hash=' + item.hash + '"><span class="filename text-monospace"><i class="far fa-file"></i> ' + item.filename + '</span></a> (' + Math.round((item.filesize/1024/1024)*100)/100 + ' MB)</li>');
});
$("#email-log-attachments-section").show();
}
if(email.bodyIsHtml) {
$("#email-log-body-view").html(email.body);
} else {
$("#email-logbody-view").text(email.body);
}
$("#email-log-modal").modal("show");
}
function displayEmailLogHeaders() {
$("#email-log-header-view").toggleClass("hidden");
if($("#email-log-header-view").hasClass("hidden")) {
$("#email-log-header-section").find("i").addClass("fa-chevron-right").removeClass("fa-chevron-down");
} else {
$("#email-log-header-section").find("i").addClass("fa-chevron-down").removeClass("fa-chevron-right");
}
}
function hideEmailLogHeaders() {
$("#email-log-header-view").empty();
if(!$("#email-log-header-view").hasClass("hidden")) {
$("#email-log-header-view").addClass("hidden");
$("#email-log-header-section").find("i").addClass("fa-chevron-down").removeClass("fa-chevron-right");
}
}
</script>
<script>
$(document).ready(function() {

View File

@@ -823,6 +823,7 @@
<div id="preorder-detail-<?=$preorder->id?>-emails" class="tab-pane preorder-emails-view">
<?php
$email_logs = EmailLog::search(["object_type" => "preorder", "object_id" => $preorder->id]);
$allPreorderEmails = PreordernotificationLogModel::search(["preorder_id" => $preorder->id]);
$allStatusFlagEmails = PreorderStatusnotificationLog::search(["preorder_id" => $preorder->id]);
?>
@@ -830,6 +831,7 @@
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h5 class="mb-3">
<i class="fas fa-envelope mr-2 text-muted"></i>
E-Mail Benachrichtigungen
@@ -984,8 +986,34 @@
</div>
<?php endif; ?>
<?php if($email_logs): ?>
<h5>Alle ausgehenden Emails <small>(ab 25.08.2025)</small></h5>
<table class="table table-sm table-striped mb-3">
<tr>
<th>Von</th>
<th>An</th>
<th>Betreff</th>
<th>Anhänge</th>
<th>Gesendet</th>
<th></th>
</tr>
<?php foreach($email_logs as $email): ?>
<tr>
<td><?=$email->from?></td>
<td><?=$email->to?></td>
<td><?=htmlentities($email->subject)?></td>
<td><?=(is_array($email->attachments)) ? count($email->attachments) : 0?></td>
<td><?=date("d.m.Y H:i",$email->create)?></td>
<td><i class="fas fa-eye text-primary pointer" onclick="displayEmailLogEmail(<?=$preorder->id?>,<?=$email->id?>); return false;"></i></td>
</tr>
<?php endforeach; ?>
</table>
<?php endif; ?>
<!-- Empty State -->
<?php if (empty($allPreorderEmails) && empty($allStatusFlagEmails)): ?>
<?php if (empty($email_logs) && empty($allPreorderEmails) && empty($allStatusFlagEmails)): ?>
<div class="card border-0">
<div class="card-body text-center py-4">
<div class="mb-3">

View File

@@ -0,0 +1,250 @@
<?php
$pagination_baseurl = $this->getUrl($Mod, "EmailLog/$notification->id");
$pagination_baseurl_params = ["filter" => $filter];
$pagination_entity_name = "Emails";
?>
<?php include(realpath(dirname(__FILE__)."/../../$mfLayoutPackage")."/header.php"); ?>
<?php //var_dump($notification->filter);exit;?>
<!-- start page title -->
<div class="row">
<div class="col-12">
<div class="page-title-box">
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="<?=self::getUrl("Dashboard")?>"><?=MFAPPNAME_SLUG?></a></li>
<li class="breadcrumb-item"><a href="<?=self::getUrl("Preorder")?>">Vorbestellung</a></li>
<li class="breadcrumb-item"><a href="<?=self::getUrl("Preordernotification")?>">Emailaussendungen</a></li>
<li class="breadcrumb-item active"><?=$notification->campaign->name?> "<?=htmlentities($notification->subject)?>"</li>
</ol>
</div>
<h4 class="page-title">Emailaussendungen <?=$notification->campaign->name?> "<?=htmlentities($notification->subject)?>"</h4>
</div>
</div>
</div>
<!-- end page title -->
<div class="row">
<div class="col">
<div class="card">
<div class="card-body mb-3">
<h4 class="header-title mb-3">Filter</h4>
<form method="get" action="<?=self::getUrl("Invoice")?>">
<div class="row">
<div class="col-2">
<label class="form-label" for="filter_from">Absender</label>
<input type="text" class="form-control" name="filter[from]" id="filter_from" value="<?=(array_key_exists("from", $filter)) ? $filter['from'] : ""?>"/>
</div>
<div class="col-2">
<label class="form-label" for="filter_to">Empfänger</label>
<input type="text" class="form-control" name="filter[to]" id="filter_to" value="<?=(array_key_exists("to", $filter)) ? $filter['to'] : ""?>"/>
</div>
<div class="col-2">
<label class="form-label" for="filter_subject">Betreff</label>
<input type="text" class="form-control" name="filter[subject]" id="filter_subject" value="<?=(array_key_exists("subject", $filter)) ? $filter['subject'] : ""?>"/>
</div>
<!--div class="col-1">
<label class="form-label" for="filter_has_attachments">Anhang</label>
<select name="filter[has_attachments]" id="filter_has_attachments" class="form-control">
<option></option>
<option value="1" <?=((array_key_exists("has_attachments", $filter)) && ($filter['has_attachments'])) ? "selected" : ""?>>Mit Anhang</option>
<option value="0" <?=((array_key_exists("has_attachments", $filter)) && (!$filter['has_attachments'])) ? "selected" : ""?>>Ohne Anhang</option>
</select>
</div-->
</div>
<div class="row mt-2">
<div class="col-12">
<button type="submit" class="btn btn-primary"><i class="fas fa-fw fa-search"></i> Filter anwenden</button>
<a class="btn btn-secondary" href="<?=self::getUrl("Preordernotification", "EmailLog", ["id" => $notification->id])?>?resetFilter=1"><i class="fas fa-fw fa-xmark-large"></i> Filter zurücksetzen</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<h4 class="header-title mb-3">Ausgehende Emails zur Aussendung <?=$notification->campaign->name?> "<?=htmlentities($notification->subject)?>"</h4>
<?php include(realpath(dirname(__FILE__)."/../")."/tpl/pagination.php"); ?>
<?php include(realpath(dirname(__FILE__)."/../")."/tpl/pagination-summary.php"); ?>
<table class="table table-striped table-hover">
<tr>
<th>Von</th>
<th>An</th>
<th>Betreff</th>
<th>Anhänge</th>
<th>Gesendet</th>
<th></th>
</tr>
<?php foreach($emails as $email): ?>
<tr>
<td><?=$email->from?></td>
<td><?=$email->to?></td>
<td><?=$email->subject?></td>
<td><?=(is_array($email->attachments)) ? count($email->attachments) : 0?></td>
<td><?=date("d.m.Y H:i",$email->create)?></td>
<td><i class="fas fa-eye text-primary pointer" onclick="displayEmailLogEmail(<?=$email->id?>); return false;"></i></td>
</tr>
<?php endforeach; ?>
</table>
<?php include(realpath(dirname(__FILE__)."/../")."/tpl/pagination-summary.php"); ?>
<?php include(realpath(dirname(__FILE__)."/../")."/tpl/pagination.php"); ?>
</div>
</div>
</div>
</div>
<div class="modal" tabindex="-1" id="email-log-modal" style="max-height: 100vh;">
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content" style="overflow: auto;">
<div class="modal-header">
<h5 class="modal-title">Emailansicht</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col">
<table class="table">
<tr>
<th>Von:</th>
<td id="email-log-from-value" class="text-monospace"></td>
</tr><tr>
<th>An:</th>
<td id="email-log-to-value" class="text-monospace"></td>
</tr><tr>
<th>Gesendet:</th>
<td id="email-log-sent-value" class="text-monospace"></td>
</tr><tr>
<th>Betreff:</th>
<td id="email-log-subject-value" class="text-monospace"></td>
</tr>
</table>
</div>
</div>
<div class="row mb-3" id="email-log-header-section">
<div class="col">
<a href="#" onclick="displayEmailLogHeaders(); return false;"><i class="fas fa-chevron-right fa-fw"></i> Alle Header anzeigen</a>
<div id="email-log-header-view" class="mt-2 text-monospace hidden" style="min-height: 48px; width:100%; border: 1px solid #ccc; padding: 8px; font-family: sans-serif;"></div>
</div>
</div>
<div class="row mb-2" id="email-log-body-section">
<div class="col">
<div id="email-log-body-view" style="min-height: 48px; width:100%; border: 1px solid #000; padding: 8px; font-family: sans-serif; overflow-y: auto;"></div>
</div>
</div>
<div class="row mb-3 hidden" id="email-log-attachments-section">
<div class="col-6">
<h5>Dateianhänge</h5>
<ul id="email-log-attachment-list" class="list-group"></ul>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
async function displayEmailLogEmail(email_id) {
$("#email-log-from-value").text("");
$("#email-log-to-value").text("");
$("#email-log-log-sent-value").text("");
$("#email-log-subject-value").text("");
$("#email-log-view").empty();
$("#email-log-attachments-section").hide();
$("#email-log-attachment-list").empty();
hideHeaders();
if(!email_id) return false;
var resp = await fetch("<?=self::getUrl("Preordernotification", "api", ["do" => "getLoggedEmail", "nid" => $notification->id])?>&eid=" + email_id);
console.log(resp);
if(!resp.ok) {
notify("error", "Email konnte nicht geladen werden");
return false;
}
var response = await resp.json();
if(!("result" in response) || typeof response.result !== "object") {
notify("error", "Email konnte nicht geladen werden");
return false;
}
var email = response.result;
$("#email-log-from-value").text(email.from);
$("#email-log-to-value").text(email.to);
$("#email-log-sent-value").text(email.sent);
$("#email-log-subject-value").text(email.subject);
if("headers" in email && typeof email.headers === "object" && Object.keys(email.headers).length > 0) {
for (const [key, value] of Object.entries(email.headers)) {
$("#email-log-header-view").append(
"<strong>" + key + "</strong>: "
+ value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
+ "<br/>");
if(key == "From") {
$("#email-log-from-value").text(value);
}
}
}
if("attachments" in email && typeof email.attachments === "object" && Object.keys(email.attachments).length > 0) {
email.attachments.forEach( (item, index) => {
$("#email-log-attachment-list").append('<li class="list-group-item flex-fill p-1" style="border-radius: 0;"><a href="<?=self::getUrl("EmailLog", "downloadContent")?>?aid=' + item.id + '&hash=' + item.hash + '"><span class="filename text-monospace"><i class="far fa-file"></i> ' + item.filename + '</span></a> (' + Math.round((item.filesize/1024/1024)*100)/100 + ' MB)</li>');
});
$("#email-log-attachments-section").show();
}
if(email.bodyIsHtml) {
$("#email-log-body-view").html(email.body);
} else {
$("#email-logbody-view").text(email.body);
}
$("#email-log-modal").modal("show");
}
function displayEmailLogHeaders() {
$("#email-log-header-view").toggleClass("hidden");
if($("#email-log-header-view").hasClass("hidden")) {
$("#email-log-header-section").find("i").addClass("fa-chevron-right").removeClass("fa-chevron-down");
} else {
$("#email-log-header-section").find("i").addClass("fa-chevron-down").removeClass("fa-chevron-right");
}
}
function hideEmailLogHeaders() {
$("#email-log-header-view").empty();
if(!$("#email-log-header-view").hasClass("hidden")) {
$("#email-log-header-view").addClass("hidden");
$("#email-log-header-section").find("i").addClass("fa-chevron-down").removeClass("fa-chevron-right");
}
}
//$("#email-log-modal").modal("show");
</script>

View File

@@ -91,7 +91,7 @@
<tr>
<td class="text-center">
<?php if($notification->send_start && $notification->send_finish): ?>
<i class='fas fa-check text-success' title='Versand erfolgreich abgeschlossen'></i>
<a href="<?=self::getUrl("Preordernotification", "EmailLog", ["id" => $notification->id])?>"><i class='fas fa-check text-success' title='Versand erfolgreich abgeschlossen'></i></a>
<?php elseif($notification->send_start && !$notification->send_finish): ?>
<i class='fas fa-loader text-info' title='Versand gestartet'></i>
<?php elseif(!$notification->send_start && !$notification->send_finish): ?>

View File

@@ -278,7 +278,7 @@ class Address extends mfBaseModel {
if(!$subject || !$from || !$from_name || !$to) {
$this->log->warn("Service PIN copy Email not sent. (subject: '$subject', from: '$from_name', from_email: '$from', to: '$to')");
} else {
$email = new Emailnotification();
$email = new Emailnotification("Address", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);

View File

@@ -467,7 +467,7 @@ class Contract extends mfBaseModel {
if(!$subject || !$from || !$from_name || !$to) {
$this->log->warn("Service PIN Email not sent. (subject: '$subject', from: '$from_name', from_email: '$from', to: '$to')");
} else {
$email = new Emailnotification();
$email = new Emailnotification("Contract", $this->id, json_encode(["type" => "cancel_notification"]));
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);
@@ -476,7 +476,7 @@ class Contract extends mfBaseModel {
$email->addAttachment($pdfpath, null, $pdfname, "application/pdf");
$email->send();
$oemail = new Emailnotification();
$oemail = new Emailnotification("Contract", $this->id, json_encode(["type" => "cancel_notification_int"]));
$oemail->setSubject("Kündigungbestätigung " . $this->owner->customer_number . " " . str_replace(["\n", "\r"], "", $this->owner->getCompanyOrName()));
$oemail->setBody($body);
$oemail->setFrom($from, $from_name);
@@ -499,7 +499,7 @@ class Contract extends mfBaseModel {
$tk_body = $tk_tpl->render();
$tk_values = $tk_tpl->getReturnedValue();
$tk_email = new Emailnotification();
$tk_email = new Emailnotification("Contract", $this->id);
$tk_email->setSubject($tk_values["subject"]);
$tk_email->setBody($tk_body);
$tk_email->setFrom($tk_values["from_email"], $tk_values["from_email_name"]);
@@ -525,7 +525,7 @@ class Contract extends mfBaseModel {
$tk_body = $tk_tpl->render();
$tk_values = $tk_tpl->getReturnedValue();
$tk_email = new Emailnotification();
$tk_email = new Emailnotification("Contract", $this->id);
$tk_email->setSubject($tk_values["subject"]);
$tk_email->setBody($tk_body);
$tk_email->setFrom($tk_values["from_email"], $tk_values["from_email_name"]);

View File

@@ -0,0 +1,243 @@
<?php
class EmailLog extends mfBaseModel
{
protected $forcestr = ["object_type", "object_data", "from", "to", "subject"];
private $attachments;
protected function beforeUpdate($data) {
if(!array_key_exists("edit_by", $data)) {
$me = new User();
$me->loadMe();
$data["edit_by"] = $me->id;
}
return $data;
}
public function getProperty($name) {
if($this->$name == null) {
if($name == "attachments") {
$att = EmailLogAttachment::search(["emaillog_id" => $this->id]);
if($att) {
$this->attachments = $att;
}
return $this->attachments;
}
$classname = ucfirst($name);
$idfield = $name."_id";
$this->$name = mfValuecache::singleton()->getMfObject($classname, $this->$idfield);
if(!$this->$name) {
return null;
}
}
return $this->$name;
}
/********************************
* Begin static Model functions
*/
public static function create(Array $data) {
$model = new EmailLog();
$table_fields = [
"object_type", "object_id", "object_data", "from", "to", "headers", "subject", "body_text", "body_html",
"create_by","edit_by","create","edit"
];
foreach($data as $field => $value) {
if(in_array($field, $table_fields)) {
$model->$field = $value;
}
}
$me = new User();
$me->loadMe();
if($model->create_by === null) {
$model->create_by = $me->id;
}
if($model->edit_by === null) {
$model->edit_by = $me->id;
}
return $model;
}
public static function getAll() {
$items = [];
$db = FronkDB::singleton();
$res = $db->select("EmailLog", "*", "1 = 1 ORDER BY `create`");
if($db->num_rows($res)) {
while($data = $db->fetch_object($res)) {
$items[] = new EmailLog($data);
}
}
return $items;
}
public static function getFirst($filter) {
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLog
WHERE $where
ORDER BY id LIMIT 1";
//var_dump($sql);exit;
//mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new EmailLog($data);
if($item->id) {
return $item;
} else {
return null;
}
}
return null;
}
public static function getLast($filter, $order = false) {
$db = FronkDB::singleton();
if(!$order) {
$order = "`create` DESC";
}
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLog
WHERE $where
ORDER BY $order LIMIT 1";
//var_dump($sql);exit;
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new EmailLog($data);
if($item->id) {
return $item;
} else {
return null;
}
}
return null;
}
public static function count($filter) {
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT COUNT(*) as cnt FROM EmailLog
WHERE $where";
//var_dump($filter);exit;
mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
return $data->cnt;
}
return 0;
}
public static function search($filter, $limit = false, $order = false) {
//var_dump($filter);exit;
$items = [];
if(!$order) {
$order = "`create` ASC";
}
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLog
WHERE $where
ORDER BY $order";
if(is_array($limit) && count($limit)) {
if(is_numeric($limit['start']) && is_numeric($limit['count'])) {
$sql .= " LIMIT ".$limit['start'].", ".$limit['count'];
} elseif(is_numeric($limit['count'])) {
$sql .= " LIMIT ".$limit['count'];
}
}
mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
while($data = $db->fetch_object($res)) {
$items[$data->id] = new EmailLog($data);
}
}
return $items;
}
private static function getSqlFilter($filter) {
$where = "1=1 ";
if(array_key_exists("object_type", $filter)) {
$object_type = FronkDB::singleton()->escape($filter["object_type"]);
if($object_type) {
$where .= " AND object_type='$object_type'";
}
}
if(array_key_exists("object_id", $filter)) {
$object_id = $filter["object_id"];
if(is_numeric($object_id)) {
$where .= " AND object_id=$object_id";
}
}
if(array_key_exists("object_data", $filter)) {
$object_data = FronkDB::singleton()->escape($filter["object_data"]);
if($object_data) {
$where .= " AND object_data LIKE '$object_data'";
}
}
if(array_key_exists("from", $filter)) {
$from = FronkDB::singleton()->escape($filter["from"]);
if($from) {
$where .= " AND from LIKE '$from'";
}
}
if(array_key_exists("to", $filter)) {
$to = FronkDB::singleton()->escape($filter["to"]);
if($to) {
$where .= " AND to LIKE '$to'";
}
}
if(array_key_exists("subject", $filter)) {
$subject = FronkDB::singleton()->escape($filter["subject"]);
if($subject) {
$where .= " AND subject LIKE '$subject'";
}
}
if(array_key_exists("add-where", $filter)) {
$where .= " ".$filter['add-where'];
}
//var_dump($filter, $where);exit;
return $where;
}
}

View File

@@ -0,0 +1,48 @@
<?php
class EmailLogController extends mfBaseController {
protected function init() {
$this->needlogin = true;
$me = new User();
$me->loadMe();
$this->me = $me;
$this->layout()->set("me", $me);
}
protected function downloadContentAction() {
$hash = $this->request->hash;
$aid = $this->request->aid;
$att = EmailLogAttachment::getOne($aid);
if(!$att) {
header("{$_SERVER['SERVER_PROTOCOL']} 404 Not Found");
echo "File not found";
}
//var_dump($att);exit;
$content = EmailLogAttachmentContent::getFirst(["sha256" => $hash]);
if(!$content) {
header("{$_SERVER['SERVER_PROTOCOL']} 404 Not Found");
echo "File not found";
}
if($att->content_id != $content->id) {
header("{$_SERVER['SERVER_PROTOCOL']} 404 Not Found");
echo "File not found";
}
set_time_limit(36000);
if($content->mimetype) {
header("Content-Type: $content->mimetype");
} else {
header("Content-Type: application/octet-stream");
}
header('Content-disposition: attachment; filename="' . $att->filename . '"');
header("Content-Length: $content->filesize");
echo $content->content;
exit;
}
}

View File

@@ -0,0 +1,230 @@
<?php
class EmailLogAttachment extends mfBaseModel
{
protected $forcestr = ["filename"];
private $content;
protected function beforeUpdate($data) {
if(!array_key_exists("edit_by", $data)) {
$me = new User();
$me->loadMe();
$data["edit_by"] = $me->id;
}
return $data;
}
public function getProperty($name) {
if($this->$name == null) {
if($name == "content") {
$content = EmailLogAttachmentContent::getOne($this->content_id);
if($content) {
$this->content = $content;
}
return $this->content;
}
$classname = ucfirst($name);
$idfield = $name."_id";
$this->$name = mfValuecache::singleton()->getMfObject($classname, $this->$idfield);
if(!$this->$name) {
return null;
}
}
return $this->$name;
}
/********************************
* Begin static Model functions
*/
public static function create(Array $data) {
$model = new EmailLogAttachment();
$table_fields = [
"emaillog_id", "content_id", "filename",
"create_by","edit_by","create","edit"
];
foreach($data as $field => $value) {
if(in_array($field, $table_fields)) {
$model->$field = $value;
}
}
$me = new User();
$me->loadMe();
if($model->create_by === null) {
$model->create_by = $me->id;
}
if($model->edit_by === null) {
$model->edit_by = $me->id;
}
return $model;
}
public static function getAll() {
$items = [];
$db = FronkDB::singleton();
$res = $db->select("EmailLogAttachment", "*", "1 = 1 ORDER BY `create`");
if($db->num_rows($res)) {
while($data = $db->fetch_object($res)) {
$items[] = new EmailLogAttachment($data);
}
}
return $items;
}
public static function getOne($id) {
$db = FronkDB::singleton();
if(!is_numeric($id)) return null;
$item = new EmailLogAttachment($id);
if($item && $item->id) {
return $item;
}
return null;
}
public static function getFirst($filter) {
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLogAttachment
WHERE $where
ORDER BY `create` LIMIT 1";
//var_dump($sql);exit;
//mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new EmailLogAttachment($data);
if($item->id) {
return $item;
} else {
return null;
}
}
return null;
}
public static function getLast($filter, $order = false) {
$db = FronkDB::singleton();
if(!$order) {
$order = "`create` DESC";
}
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLogAttachment
WHERE $where
ORDER BY $order LIMIT 1";
//var_dump($sql);exit;
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new EmailLogAttachment($data);
if($item->id) {
return $item;
} else {
return null;
}
}
return null;
}
public static function count($filter) {
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT COUNT(*) as cnt FROM EmailLogAttachment
WHERE $where";
//var_dump($filter);exit;
mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
return $data->cnt;
}
return 0;
}
public static function search($filter, $limit = false, $order = false) {
//var_dump($filter);exit;
$items = [];
if(!$order) {
$order = "`create` ASC";
}
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLogAttachment
WHERE $where
ORDER BY $order";
if(is_array($limit) && count($limit)) {
if(is_numeric($limit['start']) && is_numeric($limit['count'])) {
$sql .= " LIMIT ".$limit['start'].", ".$limit['count'];
} elseif(is_numeric($limit['count'])) {
$sql .= " LIMIT ".$limit['count'];
}
}
mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
while($data = $db->fetch_object($res)) {
$items[$data->id] = new EmailLogAttachment($data);
}
}
return $items;
}
private static function getSqlFilter($filter) {
$where = "1=1 ";
if(array_key_exists("emaillog_id", $filter)) {
$emaillog_id = $filter["emaillog_id"];
if(is_numeric($emaillog_id)) {
$where .= " AND emaillog_id=$emaillog_id";
}
}
if(array_key_exists("content_id", $filter)) {
$content_id = $filter["content_id"];
if(is_numeric($content_id)) {
$where .= " AND content_id=$content_id";
}
}
if(array_key_exists("add-where", $filter)) {
$where .= " ".$filter['add-where'];
}
//var_dump($filter, $where);exit;
return $where;
}
}

View File

@@ -0,0 +1,222 @@
<?php
class EmailLogAttachmentContent extends mfBaseModel
{
protected $forcestr = ["sha256", "data"];
protected $forcebinary = ["content"];
private $attachments;
protected function beforeUpdate($data) {
if(!array_key_exists("edit_by", $data)) {
$me = new User();
$me->loadMe();
$data["edit_by"] = $me->id;
}
return $data;
}
public function getProperty($name) {
if($this->$name == null) {
$classname = ucfirst($name);
$idfield = $name."_id";
$this->$name = mfValuecache::singleton()->getMfObject($classname, $this->$idfield);
if(!$this->$name) {
return null;
}
}
return $this->$name;
}
/********************************
* Begin static Model functions
*/
public static function create(Array $data) {
$model = new EmailLogAttachmentContent();
$table_fields = [
"mimetype", "filesize", "sha256", "content",
"create_by","edit_by","create","edit"
];
foreach($data as $field => $value) {
if(in_array($field, $table_fields)) {
$model->$field = $value;
}
}
$me = new User();
$me->loadMe();
if($model->create_by === null) {
$model->create_by = $me->id;
}
if($model->edit_by === null) {
$model->edit_by = $me->id;
}
return $model;
}
public static function getOne($id) {
$db = FronkDB::singleton();
if(!is_numeric($id)) return null;
$item = new EmailLogAttachmentContent($id);
if($item && $item->id) {
return $item;
}
return null;
}
public static function getAll() {
$items = [];
$db = FronkDB::singleton();
$res = $db->select("EmailLogAttachmentContent", "*", "1 = 1 ORDER BY `create`");
if($db->num_rows($res)) {
while($data = $db->fetch_object($res)) {
$items[] = new EmailLogAttachmentContent($data);
}
}
return $items;
}
public static function getFirst($filter) {
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLogAttachmentContent
WHERE $where
ORDER BY id LIMIT 1";
//var_dump($sql);exit;
//mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new EmailLogAttachmentContent($data);
if($item->id) {
return $item;
} else {
return null;
}
}
return null;
}
public static function getLast($filter, $order = false) {
$db = FronkDB::singleton();
if(!$order) {
$order = "`create` DESC";
}
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLogAttachmentContent
WHERE $where
ORDER BY $order LIMIT 1";
//var_dump($sql);exit;
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
$item = new EmailLogAttachmentContent($data);
if($item->id) {
return $item;
} else {
return null;
}
}
return null;
}
public static function count($filter) {
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT COUNT(*) as cnt FROM EmailLogAttachmentContent
WHERE $where";
//var_dump($filter);exit;
mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
$data = $db->fetch_object($res);
return $data->cnt;
}
return 0;
}
public static function search($filter, $limit = false, $order = false) {
//var_dump($filter);exit;
$items = [];
if(!$order) {
$order = "`create` ASC";
}
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT * FROM EmailLogAttachmentContent
WHERE $where
ORDER BY $order";
if(is_array($limit) && count($limit)) {
if(is_numeric($limit['start']) && is_numeric($limit['count'])) {
$sql .= " LIMIT ".$limit['start'].", ".$limit['count'];
} elseif(is_numeric($limit['count'])) {
$sql .= " LIMIT ".$limit['count'];
}
}
mfLoghandler::singleton()->debug($sql);
$res = $db->query($sql);
if($db->num_rows($res)) {
while($data = $db->fetch_object($res)) {
$items[$data->id] = new EmailLogAttachmentContent($data);
}
}
return $items;
}
private static function getSqlFilter($filter) {
$where = "1=1 ";
if(array_key_exists("mimetype", $filter)) {
$mimetype = FronkDB::singleton()->escape($filter["mimetype"]);
if($mimetype) {
$where .= " AND mimetype LIKE '$mimetype'";
}
}
if(array_key_exists("sha256", $filter)) {
$sha256 = FronkDB::singleton()->escape($filter["sha256"]);
if($sha256) {
$where .= " AND sha256='$sha256'";
}
}
if(array_key_exists("add-where", $filter)) {
$where .= " ".$filter['add-where'];
}
//var_dump($filter, $where);exit;
return $where;
}
}

View File

@@ -13,18 +13,28 @@ class Emailnotification {
private $email_to = false;
private $attachments = [];
public function __construct() {
private $object_type = "";
private $object_id = 0;
private $object_data = "";
private $_mimeheaders;
public function __construct($object_type = false, $object_id = false, $object_data = false) {
$this->headers = [
'X-Mailer' => MFAPPNAME.' Mailer',
];
$this->email_from = TT_OUTGOING_EMAIL;
$this->from_name = TT_OUTGOING_EMAIL_NAME;
if($object_type !== false) $this->object_type = $object_type;
if($object_id !== false) $this->object_id = $object_id;
if($object_data !== false) $this->object_data = $object_data;
}
public function addAttachment($file = null, $content = null, $name = false, $c_type = "application/octet-stream", $disposition = "attachment", $encoding = "base64" , $charset = "utf-8") {
public function addAttachment($filepath = null, $content = null, $name = false, $c_type = "application/octet-stream", $disposition = "attachment", $encoding = "base64" , $charset = "utf-8") {
$attachment = [
"file" => $file,
"file" => $filepath,
"content" => $content,
"name" => $name,
"c_type" => $c_type,
@@ -34,7 +44,7 @@ class Emailnotification {
];
$attachment["isfile"] = false;
if($file) {
if($filepath) {
$attachment['isfile'] = true;
}
@@ -119,6 +129,7 @@ class Emailnotification {
$body = $mime->get($mimeparams);
$headers = $mime->headers($this->headers);
$this->_mimeheaders = $headers;
//$log->debug(print_r($headers, true));
$mail =& Mail::factory('mail', ["-f ".$this->email_from]);
@@ -135,6 +146,85 @@ class Emailnotification {
*/
$mail->sep = "\r\n";
$mail->send($this->email_to, $headers, $body);
$this->_createEmailLog();
return true;
}
private function _createEmailLog() {
if(!$this->object_id || !$this->object_type) return true;
$log = mfLoghandler::singleton();
if(array_key_exists("To", $this->_mimeheaders)) {
$this->_mimeheaders['To'] = $this->email_to;
}
FronkDB::singleton()->startTransaction();
$el = EmailLog::create([
'object_type' => $this->object_type,
'object_id' => $this->object_id,
'object_data' => $this->object_data,
'from' => $this->email_from,
'to' => $this->email_to,
'headers' => json_encode($this->_mimeheaders),
'subject' => $this->subject,
'body_text' => $this->body,
'body_html' => $this->html,
]);
if(!$el->save()) {
$log->error(__METHOD__.": Error saving EmailLog. {$this->object_type}: {$this->object_id} from: '{$this->email_from}' | to: '{$this->email_to}' | subject: '{$this->subject}'");
FronkDB::singleton()->rollbackTransaction();
return false;
}
if(is_array($this->attachments) && count($this->attachments)) {
foreach($this->attachments as $att) {
if($att['isfile']) {
if(!file_exists($att["file"])) {
$log->error(__METHOD__.": File not found. ".$this->object_type.": ".$this->object_id." / Filename: ".$att['name']);
continue;
}
$sha256_hash = hash_file("sha256", $att["file"]);
$filesize = filesize($att["file"]);
$file_content = file_get_contents($att["file"]);
} else {
$sha256_hash = hash("sha256", $att["content"]);
$filesize = strlen($att["content"]);
$file_content = $att["content"];
}
// find attachment content or create it
$ela_content = EmailLogAttachmentContent::getFirst(['sha256' => $sha256_hash]);
if(!$ela_content) {
$ela_content = EmailLogAttachmentContent::create([
'mimetype' => $att['c_type'],
'filesize' => $filesize,
'sha256' => $sha256_hash,
'content' => $file_content,
]);
if (!$ela_content->save()) {
$log->error(__METHOD__ . ": Error saving EmailLogAttachmentContent. " . $this->object_type . ": " . $this->object_id . " / Filename: " . $att['name']);
continue;
}
}
$ela = EmailLogAttachment::create([
'emaillog_id' => $el->id,
'content_id' => $ela_content->id,
'filename' => $att['name'],
]);
if(!$ela->save()) {
$log->error(__METHOD__.": Error saving EmailLogAttachment. ".$this->object_type.": ".$this->object_id." / Filename: ".$att['name']);
}
}
}
FronkDB::singleton()->commitTransaction();
}
}

View File

@@ -152,7 +152,7 @@ XINON GmbH";
$this->log->warn(__METHOD__.": Invoice ".$this->invoice_number." could not be sent. Values missing. (subject: '$subject', from: '$from_name', from_email: '$from', to: '$to')");
return false;
} else {
$email = new Emailnotification();
$email = new Emailnotification("Invoice", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);

View File

@@ -77,7 +77,7 @@ class MailtemplateDispatch extends mfBaseModel {
continue;
}
$email = new Emailnotification();
$email = new Emailnotification("MailtemplateDispatch", $this->id);
$email->setSubject($subject);
if($template->body_html) {
$email->setHtmlBody($body);

View File

@@ -48,7 +48,7 @@ class MaintenanceNotification extends mfBaseModel {
continue;
}
$email = new Emailnotification();
$email = new Emailnotification("MaintenanceNotification", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);
@@ -84,7 +84,7 @@ class MaintenanceNotification extends mfBaseModel {
$to = "wartungsverteiler.team@xinon.at";
$body .= "\n\nPLZ Liste:\n\n";
$body .= implode("\n", $this->getProperty("plzs"));
$email = new Emailnotification();
$email = new Emailnotification("MaintenanceNotification", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);

View File

@@ -157,7 +157,7 @@ class Order extends mfBaseModel {
if(!$subject || !$from || !$from_name || !$to) {
$this->log->warn("Service PIN Email not sent. (subject: '$subject', from: '$from_name', from_email: '$from', to: '$to')");
} else {
$email = new Emailnotification();
$email = new Emailnotification("Order", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);
@@ -192,7 +192,7 @@ class Order extends mfBaseModel {
return false;
} else {
$email = new Emailnotification();
$email = new Emailnotification("Order", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);
@@ -231,7 +231,7 @@ class Order extends mfBaseModel {
$this->log->warn("Internl Billing Email not sent. (subject: '$subject', from: '$from', from_email: '$from_name', to: '$to')");
return false;
} else {
$email = new Emailnotification();
$email = new Emailnotification("Order", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);
@@ -263,7 +263,7 @@ class Order extends mfBaseModel {
$this->log->warn("Billing NBE Email not sent. (subject: '$subject', from: '$from', from_email: '$from_name', to: '$to')");
return false;
} else {
$email = new Emailnotification();
$email = new Emailnotification("Order", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);
@@ -290,7 +290,7 @@ class Order extends mfBaseModel {
$tk_body = $tk_tpl->render();
$tk_values = $tk_tpl->getReturnedValue();
$tk_email = new Emailnotification();
$tk_email = new Emailnotification("Order", $this->id);
$tk_email->setSubject($tk_values["subject"]);
$tk_email->setBody($tk_body);
$tk_email->setFrom($tk_values["from_email"], $tk_values["from_email_name"]);
@@ -342,7 +342,7 @@ class Order extends mfBaseModel {
$this->log->warning("File (orderfile->id: ".$file->id.") not a valid mimetype: $mimetype");
}
$email = new Emailnotification();
$email = new Emailnotification("Order", $this->id);
$email->setSubject($subject);
$email->setBody($body);
$email->setFrom($from, $from_name);

View File

@@ -397,7 +397,7 @@ class Preorder extends mfBaseModel {
$body_type = $mailtemplate->body_html ? "html" : "text";
$email = new Emailnotification();
$email = new Emailnotification("Preorder", $this->id, json_encode(["template" => $action["template"]]));
$email->setSubject($subject);
if($body_type == "html") {
$email->setHtmlBody($body);

View File

@@ -1041,6 +1041,9 @@ class PreorderController extends mfBaseController {
$data = [];
switch($do) {
case "getLoggedEmail":
$return = $this->getLoggedEmail();
break;
case "saveAttribute":
$return = $this->saveAttributeApi();
break;
@@ -1101,6 +1104,60 @@ class PreorderController extends mfBaseController {
}
}
protected function getLoggedEmail() {
$preorder_id = $this->request->pid;
$emaillog_id = $this->request->eid;
if(!is_numeric($preorder_id) || $preorder_id < 1) {
return false;
}
if(!is_numeric($emaillog_id) || $emaillog_id < 1) {
return false;
}
$preorder = new Preorder($preorder_id);
if(!$preorder->id) {
return false;
}
$emaillog = new EmailLog($emaillog_id);
if(!$emaillog->id) {
return false;
}
if($emaillog->object_type != "Preorder" || $emaillog->object_id != $preorder->id) {
return false;
}
$return = [
"preorder_id" => $preorder_id,
"emaillog_id" => $emaillog_id,
"from" => $emaillog->from,
"to" => $emaillog->to,
"subject" => $emaillog->subject,
"body" => ($emaillog->body_html) ? $emaillog->body_html : $emaillog->body_text,
"bodyIsHtml" => (bool) $emaillog->body_html,
"sent" => date("d.m.Y H:i", $emaillog->create),
"headers" => json_decode($emaillog->headers),
"attachments" => [],
];
if(is_array($emaillog->attachments) && count($emaillog->attachments)) {
foreach($emaillog->attachments as $attachment) {
$return["attachments"][] = [
"id" => $attachment->id,
"hash" => $attachment->content->sha256,
"filename" => $attachment->filename,
"filesize" => ($attachment->content) ? $attachment->content->filesize : 0,
"mimetype" => ($attachment->content) ? $attachment->content->mimetype : "",
];
}
}
return $return;
}
protected function saveBorderpointStatusApi() {
$preorder_id = $this->request->preorder_id;
$status_type = $this->request->status_type;

View File

@@ -206,7 +206,7 @@ RML Infrastruktur GmbH";
$this->log->warn(__METHOD__.": Invoice ".$this->invoice_number." could not be sent. Values missing. (subject: '$subject', from: '$from_name', from_email: '$from', to: '$to')");
return false;
} else {
$email = new Emailnotification();
$email = new Emailnotification("PreorderBillingInvoice", $this->id);
$email->setSubject($subject);
$email->setHtmlBody($body);
$email->setFrom($from, $from_name);

View File

@@ -5,6 +5,8 @@ class Preordernotification extends mfBaseModel {
private $filter;
private $files;
private $email_logs;
private $creator;
private $editor;
@@ -96,7 +98,7 @@ class Preordernotification extends mfBaseModel {
$this->log->warn("Preordernotification not sent. subject, from or to missing. Preorder id ".$preorder->id);
return false;
} else {
$email = new Emailnotification();
$email = new Emailnotification("Preorder", $preorder->id, json_encode(["email_type" => "Preordernotification", "preordernotification_id" => $this->id]));
$email->setSubject($subject);
$email->setHtmlBody($body);
$email->setFrom($from, $from_name);
@@ -187,6 +189,14 @@ class Preordernotification extends mfBaseModel {
return $this->files;
}
if($name == "email_logs") {
$email_logs = EmailLog::search(["type" => "Preordernotification", "type_id" => $this->id]);
if($email_logs) {
$this->email_logs = $email_logs;
}
return $this->email_logs;
}
if($name == "creator") {
$user = mfValuecache::singleton()->get("Worker-id-".$this->create_by);
if($user) {

View File

@@ -470,4 +470,116 @@ class PreordernotificationController extends mfBaseController {
}
}
protected function EmailLog() {
$this->layout()->setTemplate("Preordernotification/EmailLog");
$id = $this->request->id;
if(!is_numeric($id) || $id < 1) {
$this->layout()->setFlash("Aussendung nicht gefunden", "error");
$this->redirect("Preordernotification");
}
$notification = new Preordernotification($id);
if(!$notification->id) {
$this->layout()->setFlash("Aussendung nicht gefunden", "error");
$this->redirect("Preordernotification");
}
$this->layout()->set("notification", $notification);
$filter = $this->request->filter;
if(!is_array($filter)) {
$filter = [];
}
$this->layout->set("filter", $filter);
$filter["object_type"] = "Preordernotification";
$filter["object_id"] = $id;
// pagination defaults
$pagination = [];
$pagination['start'] = 0;
$pagination['count'] = 25;
$pagination['maxItems'] = 0;
if(is_numeric($this->request->s)) {
$pagination['start'] = intval($this->request->s);
}
$pagination["maxItems"] = EmailLog::count($filter);
$emails = EmailLog::search($filter, $pagination);
$this->layout()->set("pagination", $pagination);
$this->layout()->set("emails", $emails);
}
protected function apiAction() {
$do = $this->request->do;
$data = [];
switch($do) {
case "getLoggedEmail":
$return = $this->getLoggedEmail();
break;
default:
$return = false;
}
if(!is_array($return) || !count($return)) {
$data = ["status" => "error"];
$this->returnJson($data);
}
$data['status'] = "OK";
$data['result'] = $return;
$this->returnJson($data);
}
protected function getLoggedEmail() {
$preordernotification_id = $this->request->nid;
$emaillog_id = $this->request->eid;
if(!is_numeric($preordernotification_id) || $preordernotification_id < 1) {
return false;
}
if(!is_numeric($emaillog_id) || $emaillog_id < 1) {
return false;
}
$notification = new Preordernotification($preordernotification_id);
if(!$notification->id) {
return false;
}
$emaillog = new EmailLog($emaillog_id);
if(!$emaillog->id) {
return false;
}
$return = [
"preordernotification_id" => $preordernotification_id,
"emaillog_id" => $emaillog_id,
"from" => $emaillog->from,
"to" => $emaillog->to,
"subject" => $emaillog->subject,
"body" => ($emaillog->body_html) ? $emaillog->body_html : $emaillog->body_text,
"bodyIsHtml" => (bool) $emaillog->body_html,
"sent" => date("d.m.Y H:i", $emaillog->create),
"headers" => json_decode($emaillog->headers),
"attachments" => [],
];
if(is_array($emaillog->attachments) && count($emaillog->attachments)) {
foreach($emaillog->attachments as $attachment) {
$return["attachments"][] = [
"id" => $attachment->id,
"hash" => $attachment->content->sha256,
"filename" => $attachment->filename,
"filesize" => ($attachment->content) ? $attachment->content->filesize : 0,
"mimetype" => ($attachment->content) ? $attachment->content->mimetype : "",
];
}
}
return $return;
}
}

View File

@@ -1,5 +1,34 @@
<?php
class PreordernotificationLog extends mfBaseModel {
private $notification;
public function getProperty($name) {
if($this->$name == null) {
if($name == "notification") {
$notification = new Preordernotification($this->preordernotification_id);
if($notification->id) {
$this->notification = $notification;
}
return $this->notification;
}
$classname = ucfirst($name);
$idfield = $name."_id";
$this->$name = mfValuecache::singleton()->get("mfObjectmodel-$name-".$this->$idfield);
if(!$this->$name) {
$this->$name = new $classname($this->$idfield);
}
if($this->$name->id) {
mfValuecache::singleton()->set("mfObjectmodel-$name-".$this->$name->id, $this->$name);
return $this->$name;
} else {
return null;
}
}
return $this->$name;
}
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class CreateEmailLog extends AbstractMigration
{
public function up(): void
{
if($this->getEnvironment() == "thetool") {
$el = $this->table("EmailLog");
$el->addColumn("object_type", "string", ["null" => false, "length" => 255]);
$el->addColumn("object_id", "integer", ["null" => false]);
$el->addColumn("object_data", "string", ["null" => false, "length" => 255]);
$el->addColumn("from", "string", ["null" => false, "length" => 255]);
$el->addColumn("to", "string", ["null" => false, "length" => 255]);
$el->addColumn("headers", "json", ["null" => false]);
$el->addColumn("subject", "string", ["null" => false, "length" => 255]);
$el->addColumn("body_text", "text", ["null" => true, "length" => Phinx\Db\Adapter\MysqlAdapter::TEXT_LONG]);;
$el->addColumn("body_html", "text", ["null" => true, "length" => Phinx\Db\Adapter\MysqlAdapter::TEXT_LONG]);
$el->addColumn("create_by", "integer", ["null" => false]);
$el->addColumn("edit_by", "integer", ["null" => false]);
$el->addColumn("create", "integer", ["null" => false]);
$el->addColumn("edit", "integer", ["null" => false]);
$el->addIndex(["object_type"]);
$el->addIndex(["object_id"]);
$el->addIndex(["object_data"]);
$el->addIndex(["from"]);
$el->addIndex(["to"]);
$el->addIndex(["subject"]);
$el->create();
$ela = $this->table("EmailLogAttachment");
$ela->addColumn("emaillog_id", "integer", ["null" => false]);
$ela->addColumn("content_id", "string", ["null" => false, "length" => 255]);
$ela->addColumn("filename", "string", ["null" => false, "length" => 255]);
$ela->addColumn("create_by", "integer", ["null" => false]);
$ela->addColumn("edit_by", "integer", ["null" => false]);
$ela->addColumn("create", "integer", ["null" => false]);
$ela->addColumn("edit", "integer", ["null" => false]);
$ela->addIndex(["emaillog_id"]);
$ela->addIndex(["content_id"]);
$ela->addIndex(["filename"]);
$ela->create();
$elad = $this->table("EmailLogAttachmentContent");
$elad->addColumn("mimetype", "string", ["null" => false, "length" => 255]);
$elad->addColumn("filesize", "integer", ["null" => false]);
$elad->addColumn("sha256", "string", ["null" => false, "length" => 255]);
$elad->addColumn("content", "blob", ["null" => false, "length" => Phinx\Db\Adapter\MysqlAdapter::BLOB_LONG]);
$elad->addColumn("create_by", "integer", ["null" => false]);
$elad->addColumn("edit_by", "integer", ["null" => false]);
$elad->addColumn("create", "integer", ["null" => false]);
$elad->addColumn("edit", "integer", ["null" => false]);
$elad->addIndex(["sha256"]);
$elad->create();
}
if($this->getEnvironment() == "addressdb") {
}
}
public function down(): void
{
if($this->getEnvironment() == "thetool") {
$this->table("EmailLog")->drop()->save();
$this->table("EmailLogAttachment")->drop()->save();
$this->table("EmailLogAttachmentContent")->drop()->save();
}
if($this->getEnvironment() == "addressdb") {
}
}
}

View File

@@ -201,7 +201,7 @@ class FronkDB {
return $array;
}
public function insert($_table, $_data, $_forcestr = array(), $options = array()) {
public function insert($_table, $_data, $_forcestr = array(), $_forcebinary = array()) {
if(empty($_table)) {
$this->lastError = "Error constructing INSERT: tablename ommited";
return false;
@@ -225,14 +225,20 @@ class FronkDB {
$_Q = "";
}
}
if($v === null) {
$_Q = '';
$v = "NULL";
}
$fields .= ",`$f`";
if($v === null) {
$values .= ", NULL";
} else {
if(in_array($f, $_forcebinary)) {
$values .= ", _binary" . $_Q . $v . $_Q;
} else {
$values .= "," . $_Q . $v . $_Q;
}
}
}
$fields = preg_replace('/^,/', '', $fields);
$values = preg_replace('/^,/', '', $values);
@@ -246,7 +252,7 @@ class FronkDB {
return true;
}
public function update($_table, $_data, $_where, $_forcestr = array()) {
public function update($_table, $_data, $_where, $_forcestr = array(), $_forcebinary = array()) {
if(empty($_table)) {
$this->lastError = "Error constructing UPDATE: tablename ommited";
return false;
@@ -276,13 +282,17 @@ class FronkDB {
$_Q = '';
}
}
if($v === null) {
$_Q = '';
$v = "NULL";
}
if($v === null) {
$Pairs .= ", `$f`=NULL";
} else {
if(in_array($f, $_forcebinary)) {
$Pairs .= ", `$f`= _binary " . $_Q . $v . $_Q;
} else {
$Pairs .= ", `$f`=" . $_Q . $v . $_Q;
}
}
}
$Pairs = preg_replace('/^,/', '', $Pairs);
@@ -361,4 +371,16 @@ class FronkDB {
return false;
}
}
public function startTransaction() {
$this->query("START TRANSACTION");
}
public function commitTransaction() {
$this->query("COMMIT");
}
public function rollbackTransaction() {
$this->query("ROLLBACK");
}
}

View File

@@ -20,6 +20,7 @@ class mfBaseModel {
protected $edit;
private $worker;
protected $forcestr;
protected $forcebinary;
protected $mode = "new";
protected $saved = 0;
@@ -158,15 +159,26 @@ class mfBaseModel {
$fields = $this->buildFields();
$forcestr = array();
if(!$this->fieldprefix && is_array($this->forcestr) && count($this->forcestr)) {
$forcestr = $this->forcestr;
}
$forcebinary = array();
if(is_array($this->forcestr) && count($this->forcestr)) {
foreach($this->forcestr as $fstr) {
if ($this->fieldprefix) {
foreach ($this->forcestr as $fstr) {
$forcestr[] = $this->fieldprefix . "_$fstr";
}
} else {
$forcestr = $this->forcestr;
}
}
if(is_array($this->forcebinary) && count($this->forcebinary)) {
if ($this->fieldprefix) {
foreach ($this->forcebinary as $fstr) {
$forcebinary[] = $this->fieldprefix . "_$fstr";
}
} else {
$forcebinary = $this->forcebinary;
}
}
if(!is_array($fields) or !count($fields)) {
@@ -179,7 +191,7 @@ class mfBaseModel {
if($this->fieldprefix) {
$where = "`" . $this->fieldprefix . "_id`=$id";
}
if($this->db->update($this->table, $fields, $where, $forcestr)) {
if($this->db->update($this->table, $fields, $where, $forcestr, $forcebinary)) {
$this->saved++;
if(method_exists($this, "afterSave")) {
$this->afterSave($_params);
@@ -187,7 +199,7 @@ class mfBaseModel {
return $id;
}
} else {
if($this->db->insert($this->table, $fields, $forcestr)) {
if($this->db->insert($this->table, $fields, $forcestr, $forcebinary)) {
$id = $this->db->insert_id;
$this->id = $id;
$this->saved++;

View File

@@ -113,6 +113,23 @@ class mfLayout {
$file = PDFOUTPUTPATH . "/$filename";
if(file_exists($file)) {
$creator = mb_convert_encoding("Xinon Thetool", "UTF-16");
$pdf_contents = file_get_contents($file);
if(!$pdf_contents) return $file;
// /CreationDate (D:20250821155741+02\'00\')
$pdf_contents = preg_replace('#/CreationDate \([^)]*\)'."\n".'#im', '', $pdf_contents, 1);
// /Creator (<FE><FF>\0w\0k\0h\0t\0m\0l\0t\0o\0p\0d\0f\0 \00\0.\01\02\0.\06)\n
$pdf_contents = preg_replace('#/Creator \([^)]*\)#im', "/Creator (\xfe\xff$creator)", $pdf_contents, 1);
// /Producer (<FE><FF>\0Q\0t\0 \04\0.\08\0.\07)\n/CreationDate (D:20250821155741+02\'00\')\n
$pdf_contents = preg_replace('#/Producer \([^)]*\)#im', "/Producer (\xfe\xff$creator)", $pdf_contents, 1);
if(!$pdf_contents) return $file;
file_put_contents($file, $pdf_contents);
}
return $file;
}

View File

@@ -63,7 +63,7 @@ function sendPreorderEmail($mailtemplate, $preordercampaign, $preorder, $email_t
$body_type = $mailtemplate->body_html ? "html" : "text";
$email = new Emailnotification();
$email = new Emailnotification("Preorder", $preorder->id, json_encode(["type" => $email_type]));
$email->setSubject($subject);
if($body_type == "html") {
$email->setHtmlBody($body);