import type { HttpContext } from '@adonisjs/core/http'; import User from '#models/user'; import Dataset from '#models/dataset'; import Field from '#app/Library/Field'; import BaseModel from '#models/base_model'; import { DateTime } from 'luxon'; import { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'; import vine from '@vinejs/vine'; import mail from '@adonisjs/mail/services/main'; import logger from '@adonisjs/core/services/logger'; import { validate } from 'deep-email-validator'; interface Dictionary { [index: string]: string; } export default class DatasetsController { public async index({ auth, request, inertia }: HttpContext) { const user = (await User.find(auth.user?.id)) as User; const page = request.input('page', 1); let datasets: ModelQueryBuilderContract = Dataset.query(); // if (request.input('search')) { // // users = users.whereRaw('name like %?%', [request.input('search')]) // const searchTerm = request.input('search'); // datasets.where('name', 'ilike', `%${searchTerm}%`); // } if (request.input('sort')) { type SortOrder = 'asc' | 'desc' | undefined; let attribute = request.input('sort'); let sortOrder: SortOrder = 'asc'; if (attribute.substr(0, 1) === '-') { sortOrder = 'desc'; // attribute = substr(attribute, 1); attribute = attribute.substr(1); } datasets.orderBy(attribute, sortOrder); } else { // users.orderBy('created_at', 'desc'); datasets.orderBy('id', 'asc'); } // const users = await User.query().orderBy('login').paginate(page, limit); const myDatasets = await datasets .where('server_state', 'approved') .where('reviewer_id', user.id) .preload('titles') .preload('user', (query) => query.select('id', 'login')) .preload('editor', (query) => query.select('id', 'login')) .paginate(page, 10); return inertia.render('Reviewer/Dataset/Index', { datasets: myDatasets.serialize(), filters: request.all(), can: { review: await auth.user?.can(['dataset-review']), reject: await auth.user?.can(['dataset-review-reject']), }, }); } public async review({ request, inertia, response }: HttpContext) { const id = request.param('id'); const dataset = await Dataset.query() .where('id', id) // .preload('titles') // .preload('descriptions') .preload('user', (builder) => { builder.select('id', 'login'); }) .firstOrFail(); const validStates = ['approved']; if (!validStates.includes(dataset.server_state)) { // session.flash('errors', 'Invalid server state!'); return response .flash( 'warning', `Invalid server state. Dataset with id ${id} cannot be reviewed. Datset has server state ${dataset.server_state}.`, ) .redirect() .toRoute('reviewer.dataset.list'); } const fieldnames: Array = await dataset.describe(); const fields: Dictionary = {}; for (const fieldName of fieldnames) { const field: Field = dataset.getField(fieldName) as Field; const modelClass = field.getValueModelClass(); let fieldValues = field.getValue(); let value = ''; if (fieldValues === null || fieldValues == undefined) { continue; } if (modelClass === null) { if (typeof fieldValues === 'number') { // If the field values are a number, use them as is value = fieldValues.toString(); } else { // If the field values are not a number, use the replace() function to remove non-printable characters value = fieldValues.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '\xEF\xBF\xBD '); } } else { if (!Array.isArray(fieldValues)) { fieldValues = [fieldValues]; } for (const fieldValue of fieldValues) { if (fieldValue === null) { continue; } if (modelClass.prototype instanceof BaseModel) { // this.mapModelAttributes(fieldValue, childNode); value = ''; } else if (modelClass instanceof DateTime) { // console.log('Value is a luxon date'); // this.mapDateAttributes(fieldValue, childNode); value = value + ' Year ' + modelClass.year.toString(); value = value + ' Month ' + modelClass.month.toString(); value = value + ' Day ' + modelClass.day.toString(); value = value + ' Hour ' + modelClass.hour.toString(); value = value + ' Minute ' + modelClass.minute.toString(); value = value + ' Second ' + modelClass.second.toString(); value = value + ' UnixTimestamp ' + modelClass.toUnixInteger().toString(); let zoneName = modelClass.zoneName ? modelClass.zoneName : ''; value = value + ' Timezone ' + zoneName; } } } if (value != '') { fields[fieldName] = value; } } return inertia.render('Reviewer/Dataset/Review', { dataset, fields: fields, }); } public async reviewUpdate({ request, response }: HttpContext) { const id = request.param('id'); // const { id } = params; const dataset = await Dataset.findOrFail(id); const validStates = ['approved']; if (!validStates.includes(dataset.server_state)) { // throw new Error('Invalid server state!'); // return response.flash('warning', 'Invalid server state. Dataset cannot be released to editor').redirect().back(); return response .flash( 'warning', `Invalid server state. Dataset with id ${id} cannot be reviewed. Datset has server state ${dataset.server_state}.`, ) .redirect() .toRoute('reviewer.dataset.list'); } dataset.server_state = 'reviewed'; try { // await dataset.related('editor').associate(user); // speichert schon ab await dataset.save(); return response.toRoute('reviewer.dataset.list').flash('message', `You have successfully reviewed dataset ${dataset.id}!`); } catch (error) { // Handle any errors console.error(error); return response.status(500).json({ error: 'An error occurred while reviewing the data.' }); } } public async reject({ request, inertia, response }: HttpContext) { const id = request.param('id'); const dataset = await Dataset.query() .where('id', id) // .preload('titles') // .preload('descriptions') .preload('editor', (builder) => { builder.select('id', 'login', 'email'); }) .firstOrFail(); const validStates = ['approved']; if (!validStates.includes(dataset.server_state)) { // session.flash('errors', 'Invalid server state!'); return response .flash( 'warning', `Invalid server state. Dataset with id ${id} cannot be rejected. Datset has server state ${dataset.server_state}.`, ) .redirect() .toRoute('reviewer.dataset.list'); } return inertia.render('Reviewer/Dataset/Reject', { dataset, }); } public async rejectUpdate({ request, response, auth }: HttpContext) { const authUser = auth.user!; const id = request.param('id'); const dataset = await Dataset.query() .where('id', id) .preload('editor', (builder) => { builder.select('id', 'login', 'email'); }) .firstOrFail(); // const newSchema = schema.create({ // server_state: schema.string({ trim: true }), // reject_reviewer_note: schema.string({ trim: true }, [rules.minLength(10), rules.maxLength(500)]), // }); const newSchema = vine.object({ server_state: vine.string().trim(), reject_reviewer_note: vine.string().trim().minLength(10).maxLength(500), send_mail: vine.boolean().optional(), }); try { // await request.validate({ schema: newSchema }); const validator = vine.compile(newSchema); await request.validateUsing(validator); } catch (error) { // return response.badRequest(error.messages); throw error; } const validStates = ['approved']; if (!validStates.includes(dataset.server_state)) { // throw new Error('Invalid server state!'); // return response.flash('warning', 'Invalid server state. Dataset cannot be released to editor').redirect().back(); return response .flash( `Invalid server state. Dataset with id ${id} cannot be rejected. Datset has server state ${dataset.server_state}.`, 'warning', ) .redirect() .toRoute('reviewer.dataset.list'); } // dataset.server_state = 'reviewed'; dataset.server_state = 'rejected_reviewer'; const rejectReviewerNote = request.input('reject_reviewer_note', ''); dataset.reject_reviewer_note = rejectReviewerNote; // add logic for sending reject message const sendMail = request.input('send_email', false); // const validRecipientEmail = await this.checkEmailDomain('arno.kaimbacher@outlook.at'); const validationResult = await validate({ email: dataset.editor.email, validateSMTP: false, }); const validRecipientEmail: boolean = validationResult.valid; let emailStatusMessage = ''; if (sendMail == true) { if (dataset.editor.email && validRecipientEmail) { try { await mail.send((message) => { message.to(dataset.editor.email).subject('Dataset Rejection Notification').html(`

Dear editor ${dataset.editor.login},

Your approved dataset with ID ${dataset.id} has been rejected.

Reason for rejection: ${rejectReviewerNote}

Best regards,
Your Tethys reviewer: ${authUser.login}

`); }); emailStatusMessage = ` A rejection email was successfully sent to ${dataset.editor.email}.`; } catch (error) { logger.error(error); return response .flash('Dataset has not been rejected due to an email error: ' + error.message, 'error') .toRoute('reviewer.dataset.list'); } } else { emailStatusMessage = ` However, the email could not be sent because the editor's email address (${dataset.editor.email}) is not valid.`; } } await dataset.save(); return response .toRoute('reviewer.dataset.list') .flash(`You have rejected dataset ${dataset.id}! to editor ${dataset.editor.login}`, 'message'); } }