First Commit

This commit is contained in:
marco
2026-09-01 10:46:29 -03:00
commit bf96b77e9c
10000 changed files with 3508600 additions and 0 deletions
+321
View File
@@ -0,0 +1,321 @@
<?php
namespace Modules\Contact\Controllers;
use App\Controllers\BaseAdminController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Modules\Contact\Models\Contact;
class ContactController extends BaseAdminController
{
/**
* Display contact form for public
*/
public function index(Request $request): Response
{
$requireCaptcha = $this->getSetting('require_captcha_contact', true);
$pageContent = null;
$metaTitle = null;
$metaDescription = null;
$title = 'Contact Us';
if (class_exists('Modules\Configindex\Models\ConfigPage')) {
try {
$page = \Modules\Configindex\Models\ConfigPage::getByKey('contact', 1);
if ($page) {
$pageContent = $page->config_index_content;
if ($page->config_index_title) {
$title = $page->config_index_title;
}
$metaTitle = $page->config_index_meta_title;
$metaDescription = $page->config_index_meta_description;
}
} catch (\Exception $e) {
// Ignore if table not found or other error
}
}
return new Response($this->view->render('contact/views/index.twig', [
'title' => $title,
'page_content' => $pageContent,
'meta_title' => $metaTitle,
'meta_description' => $metaDescription,
'require_captcha_contact' => $requireCaptcha
]));
}
/**
* Process contact form submission
*/
public function submit(Request $request): Response
{
// Handle JSON request body
if ($request->getContentType() === 'json' || 0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
$data = json_decode($request->getContent(), true) ?? [];
} else {
$data = $request->request->all();
}
// Basic validation
if (empty($data['name']) || empty($data['email']) || empty($data['subject']) || empty($data['message'])) {
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => false,
'message' => 'Please fill in all required fields.'
], 400);
}
return new RedirectResponse(url('/contact?error=missing_fields'));
}
// Email format validation
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => false,
'message' => 'Please provide a valid email address.'
], 400);
}
return new RedirectResponse(url('/contact?error=invalid_email'));
}
// Captcha validation
$requireCaptcha = $this->getSetting('require_captcha_contact', true);
if ($requireCaptcha && empty($data['captcha_valid'])) {
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => false,
'message' => 'Invalid captcha. Please try again.'
], 400);
}
return new RedirectResponse(url('/contact?error=invalid_captcha'));
}
// Create contact record
$contact = new Contact();
$contact->contacts_name = $data['name'];
$contact->contacts_email = $data['email'];
$contact->contacts_phone = $data['phone'] ?? '';
$contact->contacts_subject = apply_filters('contact.subject_sanitize', $data['subject']);
$contact->contacts_message = apply_filters('contact.message_sanitize', $data['message']);
$contact->contacts_status = 'new';
$contact->contacts_ip_address = $request->getClientIp();
$contact->contacts_user_agent = $request->headers->get('User-Agent');
$contact->save();
do_action('contact.submitted', $contact);
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => true,
'message' => 'Your message has been sent successfully. We will get back to you soon!'
]);
}
return new RedirectResponse(url('/contact?success=true'));
}
/**
* Display list of contacts for admin
*/
public function list(Request $request): Response
{
$status = $request->query->get('status', 'all');
$contacts = Contact::query();
if ($status === 'unread') {
$contacts = $contacts->unread();
} elseif ($status === 'read') {
$contacts = $contacts->read();
} elseif ($status !== 'all') {
$contacts = $contacts->status($status);
}
$contacts = $contacts->orderBy('created_at', 'desc')->get();
return $this->renderAdminView('contact/views/admin/index.twig', [
'contacts' => $contacts,
'current_status' => $status,
'title' => 'Contact Messages Management'
]);
}
/**
* Display contact details
*/
public function show(Request $request, int $id): Response
{
$contact = Contact::find($id);
if (!$contact) {
return new RedirectResponse(url('admin/contacts?error=not_found'));
}
// Mark as read if it's unread
if (!$contact->isRead()) {
$contact->markAsRead();
}
return $this->renderAdminView('contact/views/admin/show.twig', [
'contact' => $contact,
'title' => 'Contact Message Details'
]);
}
/**
* Update contact status
*/
public function updateStatus(Request $request, int $id): Response
{
$contact = Contact::find($id);
if (!$contact) {
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => false,
'message' => 'Contact not found.'
], 404);
}
return new RedirectResponse(url('admin/contacts?error=not_found'));
}
$status = $request->request->get('status');
$validStatuses = ['new', 'in_progress', 'resolved', 'closed'];
if (!in_array($status, $validStatuses)) {
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => false,
'message' => 'Invalid status.'
], 400);
}
return new RedirectResponse(url('admin/contacts?error=invalid_status'));
}
$contact->contacts_status = $status;
$contact->save();
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => true,
'message' => 'Contact status updated successfully.'
]);
}
return new RedirectResponse(url('admin/contacts/' . $id));
}
/**
* Update contact response
*/
public function updateResponse(Request $request, int $id): Response
{
$contact = Contact::find($id);
if (!$contact) {
return new RedirectResponse(url('admin/contacts?error=not_found'));
}
$response = $request->request->get('response');
$contact->contacts_response = $response;
// Auto update status if it is 'new'
if ($contact->contacts_status === 'new' && !empty($response)) {
$contact->contacts_status = 'resolved';
}
$contact->save();
return new RedirectResponse(url('admin/contacts/' . $id . '?success=response_saved'));
}
/**
* Delete contact
*/
public function delete(Request $request, int $id): Response
{
$contact = Contact::find($id);
if (!$contact) {
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => false,
'message' => 'Contact not found.'
], 404);
}
return new RedirectResponse(url('admin/contacts?error=not_found'));
}
$contact->delete();
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => true,
'message' => 'Contact deleted successfully.'
]);
}
return new RedirectResponse(url('admin/contacts'));
}
/**
* Mark contact as read/unread
*/
public function toggleRead(Request $request, int $id): Response
{
$contact = Contact::find($id);
if (!$contact) {
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => false,
'message' => 'Contact not found.'
], 404);
}
return new RedirectResponse(url('admin/contacts?error=not_found'));
}
if ($contact->isRead()) {
$contact->markAsUnread();
$message = 'Contact marked as unread.';
} else {
$contact->markAsRead();
$message = 'Contact marked as read.';
}
if ($request->headers->get('Accept') === 'application/json') {
return new JsonResponse([
'success' => true,
'message' => $message,
'is_read' => $contact->isRead()
]);
}
return new RedirectResponse(url('admin/contacts/' . $id));
}
/**
* Get contact module setting
*/
private function getSetting(string $key, $default = null)
{
try {
$module = \App\Models\Module::byName('contact')->first();
if ($module) {
return $module->getSetting($key, $default);
}
} catch (\Throwable $e) {
}
return $default;
}
}
+152
View File
@@ -0,0 +1,152 @@
<?php
namespace Modules\Contact\Models;
use Illuminate\Database\Eloquent\Model;
class Contact extends Model
{
/**
* The table associated with model.
*
* @var string
*/
protected $table = 'contacts';
/**
* The primary key associated with table.
*
* @var string
*/
protected $primaryKey = 'contacts_id';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'contacts_name',
'contacts_email',
'contacts_phone',
'contacts_subject',
'contacts_message',
'contacts_response',
'contacts_status',
'contacts_ip_address',
'contacts_user_agent',
'contacts_read_at'
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'contacts_read_at' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime'
];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = [
'contacts_read_at',
'created_at',
'updated_at'
];
/**
* Get unread contacts only.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeUnread($query)
{
return $query->whereNull('contacts_read_at');
}
/**
* Get read contacts only.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeRead($query)
{
return $query->whereNotNull('contacts_read_at');
}
/**
* Get contacts by status.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $status
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeStatus($query, string $status)
{
return $query->where('contacts_status', $status);
}
/**
* Mark contact as read.
*
* @return bool
*/
public function markAsRead(): bool
{
if ($this->contacts_read_at) {
return true;
}
return $this->update(['contacts_read_at' => \Carbon\Carbon::now()]);
}
/**
* Mark contact as unread.
*
* @return bool
*/
public function markAsUnread(): bool
{
return $this->update(['contacts_read_at' => null]);
}
/**
* Check if contact is read.
*
* @return bool
*/
public function isRead(): bool
{
return !is_null($this->contacts_read_at);
}
/**
* Get formatted created date.
*
* @return string
*/
public function getFormattedCreatedAtAttribute(): string
{
return $this->created_at->format('d/m/Y H:i');
}
/**
* Get short message (100 characters).
*
* @return string
*/
public function getShortMessageAttribute(): string
{
return strlen($this->contacts_message) > 100
? substr($this->contacts_message, 0, 100) . '...'
: $this->contacts_message;
}
}
@@ -0,0 +1,80 @@
<?php
namespace Modules\Contact\Providers;
use Kernel\Application;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mime\Email;
use Modules\Contact\Models\Contact;
class ContactServiceProvider
{
public function register()
{
}
public function boot()
{
$app = Application::getInstance();
$app->addPublicPath('/contact*');
if (function_exists('add_filter')) {
add_filter('contact.subject_sanitize', function ($subject) {
$subject = is_string($subject) ? $subject : '';
$subject = strip_tags($subject);
$subject = preg_replace('/\s+/', ' ', trim($subject));
return $subject;
}, 10);
add_filter('contact.message_sanitize', function ($message) {
$message = is_string($message) ? $message : '';
$message = strip_tags($message);
$message = trim($message);
return $message;
}, 10);
}
if (function_exists('add_action')) {
add_action('contact.submitted', function (Contact $contact) {
$dsn = $_ENV['MAILER_DSN'] ?? '';
if (!$dsn) {
$transport = Transport::fromDsn(sprintf(
'smtp://%s:%s@%s:%s?encryption=%s',
$_ENV['MAIL_USERNAME'] ?? '',
$_ENV['MAIL_PASSWORD'] ?? '',
$_ENV['MAIL_HOST'] ?? 'localhost',
$_ENV['MAIL_PORT'] ?? '25',
$_ENV['MAIL_ENCRYPTION'] ?? ''
));
} else {
$transport = Transport::fromDsn($dsn);
}
$mailer = new Mailer($transport);
$to = $_ENV['MAIL_TO_ADDRESS'] ?? $_ENV['MAIL_FROM_ADDRESS'] ?? null;
if (!$to) {
return;
}
$email = (new Email())
->from($_ENV['MAIL_FROM_ADDRESS'] ?? 'no-reply@localhost')
->to($to)
->subject('Nova mensagem de contato: ' . ($contact->subject ?? ''))
->html(
'<h3>Nova mensagem de contato</h3>' .
'<p><strong>Nome:</strong> ' . htmlspecialchars($contact->name) . '</p>' .
'<p><strong>Email:</strong> ' . htmlspecialchars($contact->email) . '</p>' .
'<p><strong>Telefone:</strong> ' . htmlspecialchars((string)$contact->phone) . '</p>' .
'<p><strong>Assunto:</strong> ' . htmlspecialchars($contact->subject) . '</p>' .
'<p><strong>Mensagem:</strong><br>' . nl2br(htmlspecialchars($contact->message)) . '</p>'
);
try {
$mailer->send($email);
} catch (\Throwable $e) {
}
}, 10);
}
}
}