diff --git a/Layout/default/Preorder/Index.php b/Layout/default/Preorder/Index.php index 2f92d366f..331b3c390 100644 --- a/Layout/default/Preorder/Index.php +++ b/Layout/default/Preorder/Index.php @@ -654,6 +654,65 @@ $pagination_entity_name = "Vorbestellungen"; + + \ No newline at end of file diff --git a/Layout/default/Preordernotification/Index.php b/Layout/default/Preordernotification/Index.php index 6e43b8ea0..4da674e72 100644 --- a/Layout/default/Preordernotification/Index.php +++ b/Layout/default/Preordernotification/Index.php @@ -91,7 +91,7 @@ send_start && $notification->send_finish): ?> - + $notification->id])?>"> send_start && !$notification->send_finish): ?> send_start && !$notification->send_finish): ?> diff --git a/application/Address/Address.php b/application/Address/Address.php index ca8376f34..614da5153 100644 --- a/application/Address/Address.php +++ b/application/Address/Address.php @@ -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); diff --git a/application/Contract/Contract.php b/application/Contract/Contract.php index ac10bfe50..43c7cedac 100644 --- a/application/Contract/Contract.php +++ b/application/Contract/Contract.php @@ -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"]); diff --git a/application/EmailLog/EmailLog.php b/application/EmailLog/EmailLog.php new file mode 100644 index 000000000..5fa916654 --- /dev/null +++ b/application/EmailLog/EmailLog.php @@ -0,0 +1,243 @@ +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; + } + +} \ No newline at end of file diff --git a/application/EmailLog/EmailLogController.php b/application/EmailLog/EmailLogController.php new file mode 100644 index 000000000..2e490514b --- /dev/null +++ b/application/EmailLog/EmailLogController.php @@ -0,0 +1,48 @@ +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; + } +} \ No newline at end of file diff --git a/application/EmailLogAttachment/EmailLogAttachment.php b/application/EmailLogAttachment/EmailLogAttachment.php new file mode 100644 index 000000000..a1fc65b97 --- /dev/null +++ b/application/EmailLogAttachment/EmailLogAttachment.php @@ -0,0 +1,230 @@ +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; + } + +} \ No newline at end of file diff --git a/application/EmailLogAttachmentContent/EmailLogAttachmentContent.php b/application/EmailLogAttachmentContent/EmailLogAttachmentContent.php new file mode 100644 index 000000000..140bee8a0 --- /dev/null +++ b/application/EmailLogAttachmentContent/EmailLogAttachmentContent.php @@ -0,0 +1,222 @@ +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; + } + +} \ No newline at end of file diff --git a/application/Emailnotification/Emailnotification.php b/application/Emailnotification/Emailnotification.php index 491567bf3..a686a9067 100644 --- a/application/Emailnotification/Emailnotification.php +++ b/application/Emailnotification/Emailnotification.php @@ -12,19 +12,29 @@ class Emailnotification { private $email_from = false; private $email_to = false; private $attachments = []; + + private $object_type = ""; + private $object_id = 0; + private $object_data = ""; + + private $_mimeheaders; - public function __construct() { + 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(); } } \ No newline at end of file diff --git a/application/Invoice/Invoice.php b/application/Invoice/Invoice.php index 454099e69..058c34d97 100644 --- a/application/Invoice/Invoice.php +++ b/application/Invoice/Invoice.php @@ -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); diff --git a/application/MailtemplateDispatch/MailtemplateDispatch.php b/application/MailtemplateDispatch/MailtemplateDispatch.php index 7a8de96b7..f981c7f14 100644 --- a/application/MailtemplateDispatch/MailtemplateDispatch.php +++ b/application/MailtemplateDispatch/MailtemplateDispatch.php @@ -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); diff --git a/application/MaintenanceNotification/MaintenanceNotification.php b/application/MaintenanceNotification/MaintenanceNotification.php index b101c8219..07db5d0d3 100644 --- a/application/MaintenanceNotification/MaintenanceNotification.php +++ b/application/MaintenanceNotification/MaintenanceNotification.php @@ -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); diff --git a/application/Order/Order.php b/application/Order/Order.php index 5ba673da4..1774f35fb 100644 --- a/application/Order/Order.php +++ b/application/Order/Order.php @@ -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); diff --git a/application/Preorder/Preorder.php b/application/Preorder/Preorder.php index 85daabd01..87942b1be 100644 --- a/application/Preorder/Preorder.php +++ b/application/Preorder/Preorder.php @@ -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); diff --git a/application/Preorder/PreorderController.php b/application/Preorder/PreorderController.php index 581175fe6..442bfbf92 100644 --- a/application/Preorder/PreorderController.php +++ b/application/Preorder/PreorderController.php @@ -1042,6 +1042,9 @@ class PreorderController extends mfBaseController { $data = []; switch($do) { + case "getLoggedEmail": + $return = $this->getLoggedEmail(); + break; case "saveAttribute": $return = $this->saveAttributeApi(); break; @@ -1102,6 +1105,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; diff --git a/application/PreorderBillingInvoice/PreorderBillingInvoice.php b/application/PreorderBillingInvoice/PreorderBillingInvoice.php index bf2160242..f970d5e18 100644 --- a/application/PreorderBillingInvoice/PreorderBillingInvoice.php +++ b/application/PreorderBillingInvoice/PreorderBillingInvoice.php @@ -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); diff --git a/application/Preordernotification/Preordernotification.php b/application/Preordernotification/Preordernotification.php index f70ddd70d..84cf12e33 100644 --- a/application/Preordernotification/Preordernotification.php +++ b/application/Preordernotification/Preordernotification.php @@ -4,6 +4,8 @@ class Preordernotification extends mfBaseModel { private $campaign; 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); @@ -186,6 +188,14 @@ class Preordernotification extends mfBaseModel { $this->files = PreordernotificationFileModel::search(["preordernotification_id" => $this->id]); 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); diff --git a/application/Preordernotification/PreordernotificationController.php b/application/Preordernotification/PreordernotificationController.php index 77fdecf99..c6dfc681f 100644 --- a/application/Preordernotification/PreordernotificationController.php +++ b/application/Preordernotification/PreordernotificationController.php @@ -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; + } } \ No newline at end of file diff --git a/application/PreordernotificationLog/PreordernotificationLog.php b/application/PreordernotificationLog/PreordernotificationLog.php index bc8c5a70f..d77f5bf2f 100644 --- a/application/PreordernotificationLog/PreordernotificationLog.php +++ b/application/PreordernotificationLog/PreordernotificationLog.php @@ -1,5 +1,34 @@ $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; + } } \ No newline at end of file diff --git a/db/migrations/20250819112354_create_email_log.php b/db/migrations/20250819112354_create_email_log.php new file mode 100644 index 000000000..6b0ef775a --- /dev/null +++ b/db/migrations/20250819112354_create_email_log.php @@ -0,0 +1,79 @@ +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") { + + } + } +} diff --git a/lib/FronkDB/FronkDB.php b/lib/FronkDB/FronkDB.php index 1c60149a3..ba4fb0bba 100644 --- a/lib/FronkDB/FronkDB.php +++ b/lib/FronkDB/FronkDB.php @@ -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,13 +225,19 @@ class FronkDB { $_Q = ""; } } - if($v === null) { - $_Q = ''; - $v = "NULL"; - } $fields .= ",`$f`"; - $values .= "," . $_Q . $v . $_Q; + + 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); @@ -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,12 +282,16 @@ class FronkDB { $_Q = ''; } } - if($v === null) { - $_Q = ''; - $v = "NULL"; - } - $Pairs .= ", `$f`=" . $_Q . $v . $_Q; + 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"); + } } diff --git a/lib/mvcfronk/mfBase/mfBaseModel.php b/lib/mvcfronk/mfBase/mfBaseModel.php index 9cf127d37..2a1fe67ae 100644 --- a/lib/mvcfronk/mfBase/mfBaseModel.php +++ b/lib/mvcfronk/mfBase/mfBaseModel.php @@ -20,6 +20,7 @@ class mfBaseModel { protected $edit; private $worker; protected $forcestr; + protected $forcebinary; protected $mode = "new"; protected $saved = 0; @@ -158,17 +159,28 @@ 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) { - $forcestr[] = $this->fieldprefix . "_$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)) { return false; } @@ -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++; diff --git a/lib/mvcfronk/mfLayout/mfLayout.php b/lib/mvcfronk/mfLayout/mfLayout.php index 717ad93ff..8b912ac03 100644 --- a/lib/mvcfronk/mfLayout/mfLayout.php +++ b/lib/mvcfronk/mfLayout/mfLayout.php @@ -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 (\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 (\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; } diff --git a/scripts/preorder/custom_notifications/init.php b/scripts/preorder/custom_notifications/init.php index 332433c27..a040370a6 100644 --- a/scripts/preorder/custom_notifications/init.php +++ b/scripts/preorder/custom_notifications/init.php @@ -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);