print pdfs and send mails with ics file
This commit is contained in:
parent
5f827fb177
commit
728c4e05fa
12 changed files with 461 additions and 183 deletions
|
@ -17,6 +17,8 @@ import { PdfExport } from "../../helpers/pdfExport";
|
|||
import UserService from "../../service/userService";
|
||||
import { TemplateHelper } from "../../helpers/templateHelper";
|
||||
import MailHelper from "../../helpers/mailHelper";
|
||||
import { NewsletterHelper } from "../../helpers/newsletterHelper";
|
||||
import { Salutation } from "../../enums/salutation";
|
||||
|
||||
/**
|
||||
* @description get all newsletters
|
||||
|
@ -89,8 +91,9 @@ export async function getNewsletterPrintoutsById(req: Request, res: Response): P
|
|||
|
||||
let newsletter = await NewsletterService.getById(newsletterId);
|
||||
|
||||
let folderPath = FileSystemHelper.formatPath("export", "newsletter", `${newsletter.id}_${newsletter.title}`);
|
||||
let filesInFolder = FileSystemHelper.getFilesInDirectory(folderPath);
|
||||
let filesInFolder = FileSystemHelper.getFilesInDirectory(
|
||||
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`
|
||||
);
|
||||
|
||||
res.json(filesInFolder);
|
||||
}
|
||||
|
@ -107,9 +110,13 @@ export async function getNewsletterPrintoutByIdAndPrint(req: Request, res: Respo
|
|||
|
||||
let newsletter = await NewsletterService.getById(newsletterId);
|
||||
|
||||
let filepath = FileSystemHelper.formatPath("export", "newsletter", `${newsletter.id}_${newsletter.title}`, filename);
|
||||
let filepath = FileSystemHelper.formatPath(
|
||||
"newsletter",
|
||||
`${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
||||
filename
|
||||
);
|
||||
|
||||
res.sendFile(process.cwd() + filepath, {
|
||||
res.sendFile(filepath, {
|
||||
headers: {
|
||||
"Content-Type": "application/pdf",
|
||||
},
|
||||
|
@ -128,54 +135,15 @@ export async function createNewsletterPrintoutPreviewById(req: Request, res: Res
|
|||
let dates = await NewsletterDatesService.getAll(newsletterId);
|
||||
let recipient = await UserService.getById(parseInt(req.userId));
|
||||
|
||||
let data = {
|
||||
title: newsletter.title,
|
||||
description: newsletter.description,
|
||||
newsletterTitle: newsletter.newsletterTitle,
|
||||
newsletterText: newsletter.newsletterText,
|
||||
newsletterSignatur: newsletter.newsletterSignatur,
|
||||
dates: dates.map((d) => ({
|
||||
title: d.diffTitle ?? d.calendar.title,
|
||||
content: d.diffDescription ?? d.calendar.content,
|
||||
starttime: d.calendar.starttime,
|
||||
formattedStarttime: new Date(d.calendar.starttime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
}),
|
||||
formattedFullStarttime: new Date(d.calendar.starttime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}),
|
||||
endtime: d.calendar.endtime,
|
||||
formattedEndtime: new Date(d.calendar.endtime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
}),
|
||||
formattedFullEndtime: new Date(d.calendar.endtime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}),
|
||||
location: d.calendar.location,
|
||||
})),
|
||||
recipient: {
|
||||
firstname: recipient.firstname,
|
||||
lastname: recipient.lastname,
|
||||
salutation: "none",
|
||||
nameaffix: "",
|
||||
street: "Straße",
|
||||
streetNumber: "Hausnummer",
|
||||
streetNumberAdd: "Adresszusatz",
|
||||
},
|
||||
let data = NewsletterHelper.buildData(newsletter, dates);
|
||||
data.recipient = {
|
||||
firstname: recipient.firstname,
|
||||
lastname: recipient.lastname,
|
||||
salutation: Salutation.none,
|
||||
nameaffix: "",
|
||||
street: "Straße",
|
||||
streetNumber: "Hausnummer",
|
||||
streetNumberAdd: "Adresszusatz",
|
||||
};
|
||||
|
||||
let pdf = await PdfExport.renderFile({
|
||||
|
@ -219,15 +187,8 @@ export async function createNewsletter(req: Request, res: Response): Promise<any
|
|||
*/
|
||||
export async function createNewsletterPrintoutById(req: Request, res: Response): Promise<any> {
|
||||
let newsletterId = parseInt(req.params.newsletterId);
|
||||
let newsletter = await NewsletterService.getById(newsletterId);
|
||||
let dates = await NewsletterDatesService.getAll(newsletterId);
|
||||
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
|
||||
|
||||
// print newsletter pdf for every member having newsletter type configured to print or if all members get printout
|
||||
// check if all users have mail or adress
|
||||
// squash all files to single for printing
|
||||
// use Helper for Newsletter printing and mail sending
|
||||
// default template
|
||||
await NewsletterHelper.printPdfs(newsletterId);
|
||||
|
||||
res.sendStatus(204);
|
||||
}
|
||||
|
@ -244,54 +205,15 @@ export async function createNewsletterMailPreviewById(req: Request, res: Respons
|
|||
let dates = await NewsletterDatesService.getAll(newsletterId);
|
||||
let recipient = await UserService.getById(parseInt(req.userId));
|
||||
|
||||
let data = {
|
||||
title: newsletter.title,
|
||||
description: newsletter.description,
|
||||
newsletterTitle: newsletter.newsletterTitle,
|
||||
newsletterText: newsletter.newsletterText,
|
||||
newsletterSignatur: newsletter.newsletterSignatur,
|
||||
dates: dates.map((d) => ({
|
||||
title: d.diffTitle ?? d.calendar.title,
|
||||
content: d.diffDescription ?? d.calendar.content,
|
||||
starttime: d.calendar.starttime,
|
||||
formattedStarttime: new Date(d.calendar.starttime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
}),
|
||||
formattedFullStarttime: new Date(d.calendar.starttime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}),
|
||||
endtime: d.calendar.endtime,
|
||||
formattedEndtime: new Date(d.calendar.endtime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
}),
|
||||
formattedFullEndtime: new Date(d.calendar.endtime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}),
|
||||
location: d.calendar.location,
|
||||
})),
|
||||
recipient: {
|
||||
firstname: recipient.firstname,
|
||||
lastname: recipient.lastname,
|
||||
salutation: "none",
|
||||
nameaffix: "",
|
||||
street: "Straße",
|
||||
streetNumber: "Hausnummer",
|
||||
streetNumberAdd: "Adresszusatz",
|
||||
},
|
||||
let data = NewsletterHelper.buildData(newsletter, dates);
|
||||
data.recipient = {
|
||||
firstname: recipient.firstname,
|
||||
lastname: recipient.lastname,
|
||||
salutation: Salutation.none,
|
||||
nameaffix: "",
|
||||
street: "Straße",
|
||||
streetNumber: "Hausnummer",
|
||||
streetNumberAdd: "Adresszusatz",
|
||||
};
|
||||
|
||||
const { body } = await TemplateHelper.renderFileForModule({
|
||||
|
@ -313,11 +235,8 @@ export async function createNewsletterMailPreviewById(req: Request, res: Respons
|
|||
*/
|
||||
export async function sendNewsletterById(req: Request, res: Response): Promise<any> {
|
||||
let newsletterId = parseInt(req.params.newsletterId);
|
||||
let newsletter = await NewsletterService.getById(newsletterId);
|
||||
let dates = await NewsletterDatesService.getAll(newsletterId);
|
||||
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
|
||||
|
||||
// attach ics files for date entries to mail
|
||||
await NewsletterHelper.sendMails(newsletterId);
|
||||
|
||||
res.sendStatus(204);
|
||||
}
|
||||
|
|
|
@ -264,7 +264,7 @@ export async function createProtocolPrintoutById(req: Request, res: Response): P
|
|||
let printout: CreateProtocolPrintoutCommand = {
|
||||
title,
|
||||
iteration: iteration + 1,
|
||||
filename: FileSystemHelper.formatPath("protocol", filename),
|
||||
filename: FileSystemHelper.normalizePath("protocol", filename),
|
||||
protocolId,
|
||||
};
|
||||
await ProtocolPrintoutCommandHandler.create(printout);
|
||||
|
|
|
@ -6,6 +6,7 @@ import { createEvents } from "ics";
|
|||
import moment from "moment";
|
||||
import InternalException from "../exceptions/internalException";
|
||||
import CalendarFactory from "../factory/admin/calendar";
|
||||
import { CalendarHelper } from "../helpers/calendarHelper";
|
||||
|
||||
/**
|
||||
* @description get all calendar items by types or nscdr
|
||||
|
@ -45,59 +46,8 @@ export async function getCalendarItemsByTypes(req: Request, res: Response): Prom
|
|||
if (output == "json") {
|
||||
res.json(CalendarFactory.mapToBase(items));
|
||||
} else {
|
||||
let events = createEvents(
|
||||
items.map((i) => ({
|
||||
calName: process.env.CLUB_NAME,
|
||||
uid: i.id,
|
||||
sequence: 1,
|
||||
...(i.allDay
|
||||
? {
|
||||
start: moment(i.starttime)
|
||||
.format("YYYY-M-D")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number],
|
||||
end: moment(i.endtime)
|
||||
.format("YYYY-M-D")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number],
|
||||
}
|
||||
: {
|
||||
start: moment(i.starttime)
|
||||
.format("YYYY-M-D-H-m")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number, number, number],
|
||||
end: moment(i.endtime)
|
||||
.format("YYYY-M-D-H-m")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number, number, number],
|
||||
}),
|
||||
title: i.title,
|
||||
description: i.content,
|
||||
location: i.location,
|
||||
categories: [i.type.type],
|
||||
created: moment(i.createdAt)
|
||||
.format("YYYY-M-D-H-m")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number, number, number],
|
||||
lastModified: moment(i.updatedAt)
|
||||
.format("YYYY-M-D-H-m")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number, number, number],
|
||||
transp: "OPAQUE" as "OPAQUE",
|
||||
url: "https://www.ff-merching.de",
|
||||
alarms: [
|
||||
{
|
||||
action: "display",
|
||||
description: "Erinnerung",
|
||||
trigger: {
|
||||
minutes: 30,
|
||||
before: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}))
|
||||
);
|
||||
let { error, value } = CalendarHelper.buildICS(items);
|
||||
|
||||
res.type("ics").send(events.value);
|
||||
res.type("ics").send(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ export const MAIL_PORT = Number(process.env.MAIL_PORT ?? "587");
|
|||
export const MAIL_SECURE = process.env.MAIL_SECURE ?? "false";
|
||||
|
||||
export const CLUB_NAME = process.env.CLUB_NAME ?? "";
|
||||
export const CLUB_WEBSITE = process.env.CLUB_WEBSITE ?? "";
|
||||
|
||||
export function configCheck() {
|
||||
if (DB_TYPE != "mysql" && DB_TYPE != "sqlite") throw new Error("set valid value to DB_TYPE (mysql|sqlite)");
|
||||
|
@ -39,6 +40,13 @@ export function configCheck() {
|
|||
if (MAIL_HOST == "" || typeof MAIL_HOST != "string") throw new Error("set valid value to MAIL_HOST");
|
||||
if (typeof MAIL_PORT != "number") throw new Error("set valid numeric value to MAIL_PORT");
|
||||
if (MAIL_SECURE != "true" && MAIL_SECURE != "false") throw new Error("set 'true' or 'false' to MAIL_SECURE");
|
||||
|
||||
console.log(CLUB_WEBSITE);
|
||||
if (
|
||||
CLUB_WEBSITE != "" &&
|
||||
!/^(http(s):\/\/.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/.test(CLUB_WEBSITE)
|
||||
)
|
||||
throw new Error("CLUB_WEBSITE is not valid url");
|
||||
}
|
||||
|
||||
function checkMS(input: string, origin: string) {
|
||||
|
|
62
src/helpers/calendarHelper.ts
Normal file
62
src/helpers/calendarHelper.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { createEvents } from "ics";
|
||||
import { calendar } from "../entity/calendar";
|
||||
import moment from "moment";
|
||||
import { CLUB_NAME, CLUB_WEBSITE, MAIL_USERNAME } from "../env.defaults";
|
||||
|
||||
export abstract class CalendarHelper {
|
||||
public static buildICS(entries: Array<calendar>): { error?: Error; value?: string } {
|
||||
return createEvents(
|
||||
entries.map((i) => ({
|
||||
calName: process.env.CLUB_NAME,
|
||||
uid: i.id,
|
||||
sequence: i.sequence,
|
||||
...(i.allDay
|
||||
? {
|
||||
start: moment(i.starttime)
|
||||
.format("YYYY-M-D")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number],
|
||||
end: moment(i.endtime)
|
||||
.format("YYYY-M-D")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number],
|
||||
}
|
||||
: {
|
||||
start: moment(i.starttime)
|
||||
.format("YYYY-M-D-H-m")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number, number, number],
|
||||
end: moment(i.endtime)
|
||||
.format("YYYY-M-D-H-m")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number, number, number],
|
||||
}),
|
||||
title: i.title,
|
||||
description: i.content,
|
||||
location: i.location,
|
||||
categories: [i.type.type],
|
||||
organizer: { name: CLUB_NAME, email: MAIL_USERNAME },
|
||||
created: moment(i.createdAt)
|
||||
.format("YYYY-M-D-H-m")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number, number, number],
|
||||
lastModified: moment(i.updatedAt)
|
||||
.format("YYYY-M-D-H-m")
|
||||
.split("-")
|
||||
.map((a) => parseInt(a)) as [number, number, number, number, number],
|
||||
transp: "OPAQUE" as "OPAQUE",
|
||||
...(CLUB_WEBSITE != "" ? { url: CLUB_WEBSITE } : {}),
|
||||
alarms: [
|
||||
{
|
||||
action: "display",
|
||||
description: "Erinnerung",
|
||||
trigger: {
|
||||
minutes: 30,
|
||||
before: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
|
@ -279,4 +279,91 @@ export default abstract class DynamicQueryBuilder {
|
|||
|
||||
return flattenedResults;
|
||||
}
|
||||
|
||||
public static async executeQuery(
|
||||
query: string | DynamicQueryStructure,
|
||||
offset: number,
|
||||
count: number
|
||||
): Promise<
|
||||
| {
|
||||
stats: "error";
|
||||
sql: string;
|
||||
code: string;
|
||||
msg: string;
|
||||
}
|
||||
| {
|
||||
stats: "success";
|
||||
rows: Array<{ [key: string]: FieldType }>;
|
||||
total: number;
|
||||
offset: number;
|
||||
count: number;
|
||||
}
|
||||
> {
|
||||
if (typeof query == "string") {
|
||||
const upperQuery = query.trim().toUpperCase();
|
||||
if (!upperQuery.startsWith("SELECT") || /INSERT|UPDATE|DELETE|ALTER|DROP|CREATE|TRUNCATE/.test(upperQuery)) {
|
||||
return {
|
||||
stats: "error",
|
||||
sql: query,
|
||||
code: "UNALLOWED",
|
||||
msg: "Not allowed to change rows",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
let data: Array<any> = [];
|
||||
|
||||
return await dataSource
|
||||
.transaction(async (manager) => {
|
||||
data = await manager.query(query);
|
||||
|
||||
throw new Error("AllwaysRollbackQuery");
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.message === "AllwaysRollbackQuery") {
|
||||
return {
|
||||
stats: "success",
|
||||
rows: data,
|
||||
total: data.length,
|
||||
offset: offset,
|
||||
count: count,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
stats: "error",
|
||||
sql: error.sql,
|
||||
code: error.code,
|
||||
msg: error.sqlMessage,
|
||||
};
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
return {
|
||||
stats: "error",
|
||||
sql: error.sql,
|
||||
code: error.code,
|
||||
msg: error.sqlMessage,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
let [rows, total] = await this.buildQuery(query, offset, count).getManyAndCount();
|
||||
|
||||
return {
|
||||
stats: "success",
|
||||
rows: this.flattenQueryResult(rows),
|
||||
total: total,
|
||||
offset: offset,
|
||||
count: count,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
stats: "error",
|
||||
sql: error.sql,
|
||||
code: error.code,
|
||||
msg: error.sqlMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,43 @@
|
|||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { readdirSync } from "fs";
|
||||
|
||||
export abstract class FileSystemHelper {
|
||||
static createFolder(newFolder: string) {
|
||||
const exportPath = join(process.cwd(), "export", newFolder);
|
||||
static createFolder(...args: string[]) {
|
||||
const exportPath = this.formatPath(...args);
|
||||
if (!existsSync(exportPath)) {
|
||||
mkdirSync(exportPath, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
static readFile(filePath: string) {
|
||||
return readFileSync(join(process.cwd(), filePath), "utf8");
|
||||
static readFile(...filePath: string[]) {
|
||||
return readFileSync(this.formatPath(...filePath), "utf8");
|
||||
}
|
||||
|
||||
static writeFile(filePath: string, file: any) {
|
||||
writeFileSync(filePath, file);
|
||||
static readFileasBase64(...filePath: string[]) {
|
||||
return readFileSync(this.formatPath(...filePath), "base64");
|
||||
}
|
||||
|
||||
static readTemplateFile(filePath: string) {
|
||||
return readFileSync(process.cwd() + filePath, "utf8");
|
||||
}
|
||||
|
||||
static writeFile(filePath: string, filename: string, file: any) {
|
||||
this.createFolder(filePath);
|
||||
let path = this.formatPath(filePath, filename);
|
||||
writeFileSync(path, file);
|
||||
}
|
||||
|
||||
static formatPath(...args: string[]) {
|
||||
return join(process.cwd(), "export", ...args);
|
||||
}
|
||||
|
||||
static normalizePath(...args: string[]) {
|
||||
return join(...args);
|
||||
}
|
||||
|
||||
static getFilesInDirectory(directoryPath: string, filetype?: string): string[] {
|
||||
const fullPath = join(process.cwd(), directoryPath);
|
||||
const fullPath = this.formatPath(directoryPath);
|
||||
if (!existsSync(fullPath)) {
|
||||
return [];
|
||||
}
|
||||
|
@ -31,4 +45,17 @@ export abstract class FileSystemHelper {
|
|||
.filter((dirent) => !dirent.isDirectory() && (!filetype || dirent.name.endsWith(filetype)))
|
||||
.map((dirent) => dirent.name);
|
||||
}
|
||||
|
||||
static clearDirectoryByFiletype(directoryPath: string, filetype: string) {
|
||||
const fullPath = this.formatPath(directoryPath);
|
||||
if (!existsSync(fullPath)) {
|
||||
return;
|
||||
}
|
||||
readdirSync(fullPath, { withFileTypes: true })
|
||||
.filter((dirent) => !dirent.isDirectory() && dirent.name.endsWith(filetype))
|
||||
.forEach((dirent) => {
|
||||
const filePath = join(fullPath, dirent.name);
|
||||
unlinkSync(filePath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Transporter, createTransport, TransportOptions } from "nodemailer";
|
||||
import { CLUB_NAME, MAIL_HOST, MAIL_PASSWORD, MAIL_PORT, MAIL_SECURE, MAIL_USERNAME } from "../env.defaults";
|
||||
import { Attachment } from "nodemailer/lib/mailer";
|
||||
|
||||
export default abstract class MailHelper {
|
||||
private static readonly transporter: Transporter = createTransport({
|
||||
|
@ -19,7 +20,12 @@ export default abstract class MailHelper {
|
|||
* @param {string} content
|
||||
* @returns {Prmose<*>}
|
||||
*/
|
||||
static async sendMail(target: string, subject: string, content: string): Promise<any> {
|
||||
static async sendMail(
|
||||
target: string,
|
||||
subject: string,
|
||||
content: string,
|
||||
attach: Array<Attachment> = []
|
||||
): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.transporter
|
||||
.sendMail({
|
||||
|
@ -28,6 +34,7 @@ export default abstract class MailHelper {
|
|||
subject,
|
||||
text: content,
|
||||
html: content,
|
||||
attachments: attach,
|
||||
})
|
||||
.then((info) => resolve(info.messageId))
|
||||
.catch((e) => reject(e));
|
||||
|
|
218
src/helpers/newsletterHelper.ts
Normal file
218
src/helpers/newsletterHelper.ts
Normal file
|
@ -0,0 +1,218 @@
|
|||
import Mail from "nodemailer/lib/mailer";
|
||||
import { member } from "../entity/member";
|
||||
import { newsletter } from "../entity/newsletter";
|
||||
import { newsletterDates } from "../entity/newsletterDates";
|
||||
import { newsletterRecipients } from "../entity/newsletterRecipients";
|
||||
import MemberService from "../service/memberService";
|
||||
import NewsletterDatesService from "../service/newsletterDatesService";
|
||||
import NewsletterRecipientsService from "../service/newsletterRecipientsService";
|
||||
import NewsletterService from "../service/newsletterService";
|
||||
import { CalendarHelper } from "./calendarHelper";
|
||||
import DynamicQueryBuilder from "./dynamicQueryBuilder";
|
||||
import { FileSystemHelper } from "./fileSystemHelper";
|
||||
import MailHelper from "./mailHelper";
|
||||
import { CLUB_NAME } from "../env.defaults";
|
||||
import { TemplateHelper } from "./templateHelper";
|
||||
import { PdfExport } from "./pdfExport";
|
||||
import NewsletterConfigService from "../service/newsletterConfigService";
|
||||
import { NewsletterConfigType } from "../enums/newsletterConfigType";
|
||||
import InternalException from "../exceptions/internalException";
|
||||
|
||||
export abstract class NewsletterHelper {
|
||||
public static buildData(
|
||||
newsletter: newsletter,
|
||||
dates: Array<newsletterDates>,
|
||||
recipient?: member,
|
||||
showAdress: boolean = false
|
||||
) {
|
||||
return {
|
||||
title: newsletter.title,
|
||||
description: newsletter.description,
|
||||
newsletterTitle: newsletter.newsletterTitle,
|
||||
newsletterText: newsletter.newsletterText,
|
||||
newsletterSignatur: newsletter.newsletterSignatur,
|
||||
dates: dates.map((d) => ({
|
||||
title: d.diffTitle ?? d.calendar.title,
|
||||
content: d.diffDescription ?? d.calendar.content,
|
||||
starttime: d.calendar.starttime,
|
||||
formattedStarttime: new Date(d.calendar.starttime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
}),
|
||||
formattedFullStarttime: new Date(d.calendar.starttime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}),
|
||||
endtime: d.calendar.endtime,
|
||||
formattedEndtime: new Date(d.calendar.endtime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
}),
|
||||
formattedFullEndtime: new Date(d.calendar.endtime).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}),
|
||||
location: d.calendar.location,
|
||||
})),
|
||||
...(recipient
|
||||
? {
|
||||
recipient: {
|
||||
firstname: recipient.firstname,
|
||||
lastname: recipient.lastname,
|
||||
salutation: recipient.salutation,
|
||||
nameaffix: recipient.nameaffix,
|
||||
...(showAdress
|
||||
? {
|
||||
street: recipient.sendNewsletter.street ?? "",
|
||||
streetNumber: recipient.sendNewsletter.streetNumber ?? "",
|
||||
streetNumberAdd: recipient.sendNewsletter.streetNumberAddition ?? "",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
public static async transformRecipientsToMembers(
|
||||
newsletter: newsletter,
|
||||
recipients: Array<newsletterRecipients>
|
||||
): Promise<Array<member>> {
|
||||
let useQuery = newsletter.recipientsByQuery?.query;
|
||||
|
||||
let queryMemberIds: Array<number> = [];
|
||||
if (useQuery) {
|
||||
let result = await DynamicQueryBuilder.executeQuery(
|
||||
useQuery.startsWith("{") ? JSON.parse(useQuery) : useQuery,
|
||||
0,
|
||||
1000
|
||||
);
|
||||
if (result.stats == "success") {
|
||||
let keys = Object.keys(result.rows?.[0] ?? {});
|
||||
let memberKey = keys.find((k) => k.includes("member_id"));
|
||||
queryMemberIds = result.rows.map((t) => parseInt((t[memberKey] ?? t.id) as string));
|
||||
}
|
||||
}
|
||||
|
||||
for (let recipient of recipients) {
|
||||
if (!queryMemberIds.includes(recipient.memberId)) {
|
||||
queryMemberIds.push(recipient.memberId);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(queryMemberIds);
|
||||
|
||||
let members = await MemberService.getAll(0, 1000);
|
||||
|
||||
return members[0].filter((m) => queryMemberIds.includes(m.id));
|
||||
}
|
||||
|
||||
public static getICSFilePath(newsletter: newsletter) {
|
||||
return FileSystemHelper.formatPath(
|
||||
"newsletter",
|
||||
`${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
||||
`events.ics`
|
||||
);
|
||||
}
|
||||
|
||||
public static saveIcsToFile(newsletter: newsletter, ics: string) {
|
||||
FileSystemHelper.writeFile(`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`, "events.ics", ics);
|
||||
}
|
||||
|
||||
public static async sendMails(newsletterId: number) {
|
||||
let newsletter = await NewsletterService.getById(newsletterId);
|
||||
let dates = await NewsletterDatesService.getAll(newsletterId);
|
||||
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
|
||||
let config = await NewsletterConfigService.getAll();
|
||||
|
||||
const { value, error } = CalendarHelper.buildICS(dates.map((r) => r.calendar));
|
||||
if (error) throw new InternalException("Failed Building ICS form Mail", error);
|
||||
this.saveIcsToFile(newsletter, value);
|
||||
|
||||
let allowedForMail = config.filter((c) => c.config == NewsletterConfigType.mail).map((c) => c.comTypeId);
|
||||
|
||||
const members = await this.transformRecipientsToMembers(newsletter, recipients);
|
||||
const mailRecipients = members.filter(
|
||||
(m) =>
|
||||
m.sendNewsletter != null &&
|
||||
m.sendNewsletter?.email != null &&
|
||||
allowedForMail.includes(m.sendNewsletter?.type?.id)
|
||||
);
|
||||
|
||||
for (const rec of mailRecipients) {
|
||||
let data = this.buildData(newsletter, dates, rec);
|
||||
const { body } = await TemplateHelper.renderFileForModule({
|
||||
module: "newsletter",
|
||||
bodyData: data,
|
||||
title: `Newsletter von ${CLUB_NAME}`,
|
||||
});
|
||||
await MailHelper.sendMail(rec.sendNewsletter.email, `Newsletter von ${CLUB_NAME}`, body, [
|
||||
{ filename: "events.ics", path: this.getICSFilePath(newsletter) },
|
||||
])
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
console.log("mail send", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static async printPdfs(newsletterId: number) {
|
||||
let newsletter = await NewsletterService.getById(newsletterId);
|
||||
let dates = await NewsletterDatesService.getAll(newsletterId);
|
||||
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
|
||||
let config = await NewsletterConfigService.getAll();
|
||||
|
||||
FileSystemHelper.clearDirectoryByFiletype(
|
||||
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
||||
".pdf"
|
||||
);
|
||||
|
||||
const { value, error } = CalendarHelper.buildICS(dates.map((r) => r.calendar));
|
||||
if (error) throw new InternalException("Failed Building ICS form Pdf", error);
|
||||
this.saveIcsToFile(newsletter, value);
|
||||
|
||||
let notAllowedForPdf = config.filter((c) => c.config == NewsletterConfigType.mail).map((c) => c.comTypeId);
|
||||
let printWithAdress = config.filter((c) => c.config == NewsletterConfigType.pdf).map((c) => c.comTypeId);
|
||||
|
||||
const members = await this.transformRecipientsToMembers(newsletter, recipients);
|
||||
const pdfRecipients = members.filter(
|
||||
(m) => !notAllowedForPdf.includes(m.sendNewsletter?.type?.id) || m.sendNewsletter == null
|
||||
);
|
||||
|
||||
for (const rec of pdfRecipients) {
|
||||
let data = this.buildData(newsletter, dates, rec, printWithAdress.includes(rec.sendNewsletter?.type?.id));
|
||||
|
||||
await PdfExport.renderFile({
|
||||
template: "newsletter",
|
||||
title: `Newsletter von ${CLUB_NAME}`,
|
||||
filename: `${rec.lastname}_${rec.firstname}_${rec.id}`,
|
||||
folder: `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
||||
data: data,
|
||||
})
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
console.log("pdf print", err);
|
||||
});
|
||||
}
|
||||
|
||||
await PdfExport.sqashToSingleFile(
|
||||
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
||||
"allPdfsTogether",
|
||||
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`
|
||||
)
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
console.log("pdf squash", err);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ export abstract class PdfExport {
|
|||
const page = await browser.newPage();
|
||||
await page.setContent(body, { waitUntil: "domcontentloaded" });
|
||||
|
||||
const exportPath = FileSystemHelper.formatPath(process.cwd(), "export", folder, `${filename}.pdf`);
|
||||
const exportPath = FileSystemHelper.formatPath(folder, `${filename}.pdf`);
|
||||
|
||||
let pdf = await page.pdf({
|
||||
...(saveToDisk ? { path: exportPath } : {}),
|
||||
|
@ -66,10 +66,12 @@ export abstract class PdfExport {
|
|||
|
||||
let pdfFilePaths = FileSystemHelper.getFilesInDirectory(inputFolder, ".pdf");
|
||||
|
||||
if (pdfFilePaths.length == 0) return;
|
||||
|
||||
const mergedPdf = await PDFDocument.create();
|
||||
|
||||
for (const pdfPath of pdfFilePaths) {
|
||||
const pdfBytes = FileSystemHelper.readFile(pdfPath);
|
||||
const pdfBytes = FileSystemHelper.readFileasBase64(inputFolder, pdfPath);
|
||||
const pdf = await PDFDocument.load(pdfBytes);
|
||||
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
||||
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
||||
|
@ -77,8 +79,6 @@ export abstract class PdfExport {
|
|||
|
||||
const mergedPdfBytes = await mergedPdf.save();
|
||||
|
||||
const exportPath = FileSystemHelper.formatPath(process.cwd(), "export", outputFolder, `${outputFile}.pdf`);
|
||||
|
||||
FileSystemHelper.writeFile(exportPath, mergedPdfBytes);
|
||||
FileSystemHelper.writeFile(outputFolder, `${outputFile}.pdf`, mergedPdfBytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { FileSystemHelper } from "./fileSystemHelper";
|
|||
|
||||
export abstract class TemplateHelper {
|
||||
static getTemplateFromFile(template: string) {
|
||||
return FileSystemHelper.readFile(`/src/templates/${template}.template.html`);
|
||||
return FileSystemHelper.readTemplateFile(`/src/templates/${template}.template.html`);
|
||||
}
|
||||
|
||||
static async getTemplateFromStore(templateId: number): Promise<string> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div style="font-size: 10pt; width: 100%; margin: 0 20px; padding-top: 5px; color: #888; border-top: 0.5px solid black">
|
||||
{{recipient.lastname}}, {{recipient.firstname}}, {{recipient.street}} {{recipient.streetNumber}}
|
||||
{{recipient.streetNumberAdd}}
|
||||
{{recipient.lastname}}, {{recipient.firstname}}{{#if recipient.street}},{{/if}} {{recipient.street}}
|
||||
{{recipient.streetNumber}} {{recipient.streetNumberAdd}}
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue