Merge pull request 'ranjith-dev' (#1037) from ranjith-dev into master
Some checks failed
Scan for leaked secrets using Kingfisher / kingfisher-secrets-scan (push) Has been cancelled
Some checks failed
Scan for leaked secrets using Kingfisher / kingfisher-secrets-scan (push) Has been cancelled
Reviewed-on: #1037
This commit was merged in pull request #1037.
This commit is contained in:
229
app/Console/Commands/SendDailyTaskReport.php
Normal file
229
app/Console/Commands/SendDailyTaskReport.php
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Mail\DailyTaskReportMail;
|
||||||
|
use App\Models\AlertMailRule;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Storage;
|
||||||
|
use Smalot\PdfParser\Parser;
|
||||||
|
|
||||||
|
class SendDailyTaskReport extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'send:daily-task-report {schedule_type}';
|
||||||
|
|
||||||
|
|
||||||
|
protected array $knownStatuses = [
|
||||||
|
'in progress', 'new', 'closed', 'on hold', 'rejected',
|
||||||
|
'done', 'resolved', 'confirmed', 'in specification', 'scheduled',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Command description';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$directory = 'task_updates';
|
||||||
|
$scheduleType = $this->argument('schedule_type');
|
||||||
|
|
||||||
|
$mailRules = AlertMailRule::where('module', 'DailyTaskReport')
|
||||||
|
->where('rule_name', 'DailyTaskReportMail')
|
||||||
|
->where('schedule_type', $scheduleType)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if (!$mailRules) {
|
||||||
|
$this->error('Daily Task Report mail rule not found.');
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all PDF files
|
||||||
|
$pdfFiles = collect(Storage::disk('local')->files($directory))
|
||||||
|
->filter(function ($file) {
|
||||||
|
return strtolower(pathinfo($file, PATHINFO_EXTENSION)) == 'pdf';
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
|
||||||
|
if ($pdfFiles->isEmpty()) {
|
||||||
|
$this->info('No PDF files found.');
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parser = new Parser();
|
||||||
|
$attachments = [];
|
||||||
|
$rows = [];
|
||||||
|
$sno = 1;
|
||||||
|
|
||||||
|
$attachments = [];
|
||||||
|
|
||||||
|
foreach ($pdfFiles as $file) {
|
||||||
|
$fullPath = Storage::disk('local')->path($file);
|
||||||
|
$filename = basename($file, '.pdf');
|
||||||
|
|
||||||
|
$attachments[] = [
|
||||||
|
'path' => Storage::disk('local')->path($file),
|
||||||
|
'name' => basename($file),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Read PDF content for name/task/status
|
||||||
|
try {
|
||||||
|
$pdf = $parser->parseFile($fullPath);
|
||||||
|
$text = $pdf->getText();
|
||||||
|
|
||||||
|
\Log::info('PDF RAW TEXT: ' . $text);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$text = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows[] = [
|
||||||
|
'sno' => $sno++,
|
||||||
|
'name' => $this->extractNameFromFilename($filename),
|
||||||
|
'project-name' => $this->extractProjectName($text),
|
||||||
|
'task' => $this->extractTaskTitle($text),
|
||||||
|
'status' => $this->extractStatus($text),
|
||||||
|
'completed' => $this->extractPercentComplete($text),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->info('Attached: ' . basename($file));
|
||||||
|
}
|
||||||
|
|
||||||
|
$toEmails = [];
|
||||||
|
$ccEmails = [];
|
||||||
|
|
||||||
|
foreach ($mailRules as $mailRule) {
|
||||||
|
|
||||||
|
// TO email
|
||||||
|
if (!empty($mailRule->email)) {
|
||||||
|
$toEmails[] = trim($mailRule->email);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CC emails
|
||||||
|
if (!empty($mailRule->cc_emails)) {
|
||||||
|
|
||||||
|
$ccEmails = array_merge(
|
||||||
|
$ccEmails,
|
||||||
|
array_map(
|
||||||
|
'trim',
|
||||||
|
explode(',', $mailRule->cc_emails)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove duplicate emails
|
||||||
|
$toEmails = array_values(array_unique(array_filter($toEmails)));
|
||||||
|
$ccEmails = array_values(array_unique(array_filter($ccEmails)));
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$mail = Mail::to($toEmails);
|
||||||
|
|
||||||
|
if (!empty($ccEmails)) {
|
||||||
|
$mail->cc($ccEmails);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->send(
|
||||||
|
new DailyTaskReportMail($attachments, $rows)
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($pdfFiles as $file) {
|
||||||
|
|
||||||
|
if (Storage::disk('local')->exists($file)) {
|
||||||
|
Storage::disk('local')->delete($file);
|
||||||
|
|
||||||
|
$this->info('Deleted: ' . basename($file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info(
|
||||||
|
$pdfFiles->count() . ' PDF(s) mailed and deleted successfully.'
|
||||||
|
);
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
|
||||||
|
// Mail failed -> DO NOT delete PDFs
|
||||||
|
$this->error('Mail sending failed.');
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extractNameFromFilename(string $filename): string
|
||||||
|
{
|
||||||
|
$name = preg_replace('/[-_]?task.*$/i', '', $filename);
|
||||||
|
$name = preg_replace('/[-_]+/', ' ', $name);
|
||||||
|
$name = preg_replace('/(?<!^)([A-Z])/', ' $1', $name);
|
||||||
|
return trim(ucwords(strtolower($name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extractTaskTitle(string $text): string
|
||||||
|
{
|
||||||
|
if (preg_match('/Task\s*#\d+\s*-\s*(.+)/i', $text, $matches)) {
|
||||||
|
$title = trim($matches[1]);
|
||||||
|
foreach ($this->knownStatuses as $status) {
|
||||||
|
$pos = stripos($title, $status);
|
||||||
|
if ($pos != false) {
|
||||||
|
$title = trim(substr($title, 0, $pos));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $title;
|
||||||
|
}
|
||||||
|
return 'Unknown task';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extractStatus(string $text): string
|
||||||
|
{
|
||||||
|
// Replace non-breaking spaces (common in OpenProject PDF exports) with regular spaces
|
||||||
|
$normalized = str_replace("\xC2\xA0", ' ', $text);
|
||||||
|
$normalized = preg_replace('/\s+/', ' ', $normalized);
|
||||||
|
|
||||||
|
foreach ($this->knownStatuses as $status) {
|
||||||
|
if (stripos($normalized, $status) !== false) {
|
||||||
|
return ucwords($status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extractPercentComplete(string $text): string
|
||||||
|
{
|
||||||
|
$normalized = str_replace("\xC2\xA0", ' ', $text);
|
||||||
|
$normalized = preg_replace('/[ \t]+/', ' ', $normalized);
|
||||||
|
|
||||||
|
if (preg_match('/%\s*Complete\s*([0-9]{1,3}\s*%)/i', $normalized, $matches)) {
|
||||||
|
return trim(str_replace(' ', '', $matches[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return '0%';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extractProjectName(string $text): string
|
||||||
|
{
|
||||||
|
$normalized = str_replace("\xC2\xA0", ' ', $text);
|
||||||
|
|
||||||
|
// Footer pattern: <page#><date>\t<Project Name> at the very end of the PDF text
|
||||||
|
if (preg_match('/\d{1,2}-\d{2}-\d{4}\s*\t\s*(.+?)\s*$/s', $normalized, $matches)) {
|
||||||
|
return trim($matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Unknown project';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -86,6 +86,7 @@ class AlertMailRuleResource extends Resource
|
|||||||
'LaserStopReport' => 'LaserStopReport',
|
'LaserStopReport' => 'LaserStopReport',
|
||||||
'PanelBoxAlert' => 'PanelBoxAlert',
|
'PanelBoxAlert' => 'PanelBoxAlert',
|
||||||
'PanelBoxReport' => 'PanelBoxReport',
|
'PanelBoxReport' => 'PanelBoxReport',
|
||||||
|
'DailyTaskReport' => 'DailyTaskReport',
|
||||||
]),
|
]),
|
||||||
Forms\Components\Select::make('rule_name')
|
Forms\Components\Select::make('rule_name')
|
||||||
->label('Rule Name')
|
->label('Rule Name')
|
||||||
@@ -104,7 +105,8 @@ class AlertMailRuleResource extends Resource
|
|||||||
'LaserStopAlertMail' => 'Laser Stop Alert Mail',
|
'LaserStopAlertMail' => 'Laser Stop Alert Mail',
|
||||||
'LaserStopMail' => 'Laser Stop Report Mail',
|
'LaserStopMail' => 'Laser Stop Report Mail',
|
||||||
'PanelBoxAlertMail' => 'Panel Box Alert Mail',
|
'PanelBoxAlertMail' => 'Panel Box Alert Mail',
|
||||||
'PanelBoxNotOkReportMail' => 'Panel Box Not Ok Report Mail'
|
'PanelBoxNotOkReportMail' => 'Panel Box Not Ok Report Mail',
|
||||||
|
'DailyTaskReportMail' => 'Daily Task Report Mail',
|
||||||
])
|
])
|
||||||
->required(),
|
->required(),
|
||||||
Forms\Components\TextInput::make('email')
|
Forms\Components\TextInput::make('email')
|
||||||
|
|||||||
69
app/Mail/DailyTaskReportMail.php
Normal file
69
app/Mail/DailyTaskReportMail.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Mail\Mailables\Content;
|
||||||
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Mail\Mailables\Attachment;
|
||||||
|
|
||||||
|
class DailyTaskReportMail extends Mailable
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public array $pdfAttachments;
|
||||||
|
|
||||||
|
public array $rows = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*/
|
||||||
|
public function __construct(array $pdfAttachments, array $rows)
|
||||||
|
{
|
||||||
|
$this->pdfAttachments = $pdfAttachments;
|
||||||
|
$this->rows = $rows;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the message envelope.
|
||||||
|
*/
|
||||||
|
public function envelope(): Envelope
|
||||||
|
{
|
||||||
|
return new Envelope(
|
||||||
|
subject: 'Daily Task Report Mail',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message content definition.
|
||||||
|
*/
|
||||||
|
public function content(): Content
|
||||||
|
{
|
||||||
|
return new Content(
|
||||||
|
view: 'mail.daily-task-report',
|
||||||
|
with: [
|
||||||
|
'rows' => $this->rows,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the attachments for the message.
|
||||||
|
*
|
||||||
|
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||||
|
*/
|
||||||
|
public function attachments(): array
|
||||||
|
{
|
||||||
|
return collect($this->pdfAttachments)
|
||||||
|
->map(function ($attachment) {
|
||||||
|
|
||||||
|
return Attachment::fromPath($attachment['path'])
|
||||||
|
->as($attachment['name'])
|
||||||
|
->withMime('application/pdf');
|
||||||
|
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
225
resources/views/mail/daily-task-report.blade.php
Normal file
225
resources/views/mail/daily-task-report.blade.php
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Daily Task Report</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="margin:0; padding:0; background-color:#f4f6f8; font-family:Arial, Helvetica, sans-serif; color:#333333;">
|
||||||
|
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" border="0"
|
||||||
|
style="background-color:#f4f6f8; padding:30px 15px;">
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
|
||||||
|
<table width="650" cellpadding="0" cellspacing="0" border="0"
|
||||||
|
style="max-width:650px; width:100%; background:#ffffff; border-radius:10px; overflow:hidden;">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<tr>
|
||||||
|
<td style="background:#1f4e78; padding:25px 30px; text-align:center;">
|
||||||
|
|
||||||
|
<div style="font-size:26px; font-weight:bold; color:#ffffff;">
|
||||||
|
CRI Digital Manufacturing IIOT
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="font-size:15px; color:#dceaf5; margin-top:7px;">
|
||||||
|
Daily Task Report - {{ now()->subDay()->format('d M Y') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Introduction -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding:35px 35px 10px 35px;">
|
||||||
|
|
||||||
|
<p style="font-size:16px; margin:0 0 18px 0;">
|
||||||
|
Dear Sir,
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="font-size:15px; line-height:1.7; margin:0 0 18px 0;">
|
||||||
|
Please find attached the <strong>Daily Task Report</strong>
|
||||||
|
for your review and reference.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="font-size:15px; line-height:1.7; margin:0;">
|
||||||
|
The attached report contains the task details recorded
|
||||||
|
for the day. Kindly review the report and take any
|
||||||
|
necessary follow-up actions.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Task Table -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding:25px 35px 0 35px;">
|
||||||
|
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" border="0"
|
||||||
|
style="border-collapse:collapse;">
|
||||||
|
|
||||||
|
<thead>
|
||||||
|
<tr style="background:#1f4e78;">
|
||||||
|
|
||||||
|
<th style="padding:10px; color:#ffffff; font-size:13px; text-align:left; border:1px solid #1f4e78;">
|
||||||
|
S.No
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th style="padding:10px; color:#ffffff; font-size:13px; text-align:left; border:1px solid #1f4e78;">
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th style="padding:10px; color:#ffffff; font-size:13px; text-align:left; border:1px solid #1f4e78;">
|
||||||
|
Project Name
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th style="padding:10px; color:#ffffff; font-size:13px; text-align:left; border:1px solid #1f4e78;">
|
||||||
|
Task
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th style="padding:10px; color:#ffffff; font-size:13px; text-align:left; border:1px solid #1f4e78;">
|
||||||
|
Status
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th style="padding:10px; color:#ffffff; font-size:13px; text-align:left; border:1px solid #1f4e78;">
|
||||||
|
Completed(%)
|
||||||
|
</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
@forelse($rows as $row)
|
||||||
|
|
||||||
|
<tr style="background:{{ $loop->even ? '#f5f8fb' : '#ffffff' }};">
|
||||||
|
|
||||||
|
<td style="padding:10px; font-size:13px; border:1px solid #dce4ec;">
|
||||||
|
{{ $row['sno'] }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style="padding:10px; font-size:13px; border:1px solid #dce4ec; white-space:nowrap;">
|
||||||
|
{{ $row['name'] }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style="padding:10px; font-size:13px; border:1px solid #dce4ec; white-space:nowrap;">
|
||||||
|
{{ $row['project-name'] }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style="padding:10px; font-size:13px; border:1px solid #dce4ec; white-space:nowrap;">
|
||||||
|
{{ $row['task'] }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style="
|
||||||
|
padding:10px;
|
||||||
|
font-size:13px;
|
||||||
|
border:1px solid #dce4ec;
|
||||||
|
white-space:nowrap;
|
||||||
|
font-weight:bold;
|
||||||
|
color:{{ str_contains(strtolower($row['status']), 'progress')
|
||||||
|
? '#b8860b'
|
||||||
|
: (in_array(strtolower($row['status']), ['closed','done','resolved'])
|
||||||
|
? '#2e7d32'
|
||||||
|
: '#555555') }};
|
||||||
|
">
|
||||||
|
{{ $row['status'] }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style="padding:10px; font-size:13px; border:1px solid #dce4ec;">
|
||||||
|
{{ $row['completed'] }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@empty
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="5"
|
||||||
|
style="padding:10px; text-align:center; border:1px solid #dce4ec;">
|
||||||
|
No tasks found.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@endforelse
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Attachment Box -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding:25px 35px 0 35px;">
|
||||||
|
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" border="0"
|
||||||
|
style="background:#f5f8fb; border:1px solid #dce4ec; border-radius:7px;">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px;">
|
||||||
|
|
||||||
|
<div style="font-size:16px; font-weight:bold; color:#1f4e78; margin-bottom:8px;">
|
||||||
|
📎 Report Attached
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="font-size:14px; color:#555555; line-height:1.6;">
|
||||||
|
The daily task report PDF is attached to this email.
|
||||||
|
Please download and review the attached document.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Thank You -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding:25px 35px 25px 35px;">
|
||||||
|
|
||||||
|
<p style="font-size:15px; margin:0;">
|
||||||
|
Thank you
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Divider -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 35px;">
|
||||||
|
<div style="border-top:1px solid #e5e5e5;"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding:22px 35px; text-align:center;">
|
||||||
|
|
||||||
|
<div style="font-size:14px; font-weight:bold; color:#444444;">
|
||||||
|
CRI Digital Manufacturing IIOT Solutions
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="font-size:12px; color:#888888; margin-top:6px;">
|
||||||
|
This is an automated email. Please do not reply directly
|
||||||
|
to this message.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="font-size:12px; color:#aaaaaa; margin-top:10px;">
|
||||||
|
© {{ date('Y') }} CRI Digital Manufacturing IIOT. All rights reserved.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user