Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,534 @@
<?php
namespace FluentMail\App\Models;
use Exception;
use InvalidArgumentException;
use FluentMail\Includes\Support\Arr;
class Logger extends Model
{
const STATUS_PENDING = 'pending';
const STATUS_FAILED = 'failed';
const STATUS_SENT = 'sent';
protected $fillables = [
'to',
'from',
'subject',
'body',
'status',
'response',
'extra',
'created_at'
];
protected $searchables = [
'to',
'from',
'subject'
];
protected $table = null;
public function __construct()
{
parent::__construct();
$this->table = $this->db->prefix . FLUENT_MAIL_DB_PREFIX . 'email_logs';
}
public function get($data)
{
$db = $this->getDb();
$page = isset($data['page']) ? (int)$data['page'] : 1;
$perPage = isset($data['per_page']) ? (int)$data['per_page'] : 15;
$offset = ($page - 1) * $perPage;
$query = $db->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
->limit($perPage)
->offset($offset)
->orderBy('id', 'DESC');
if (!empty($data['status'])) {
$query->where('status', sanitize_text_field($data['status']));
}
if (!empty($data['date_range']) && is_array($data['date_range']) && count($data['date_range']) == 2) {
$dateRange = $data['date_range'];
$from = $dateRange[0] . ' 00:00:01';
$to = $dateRange[1] . ' 23:59:59';
$query->whereBetween('created_at', $from, $to);
}
if (!empty($data['search'])) {
$search = trim(sanitize_text_field($data['search']));
$query->where(function ($q) use ($search) {
$searchColumns = $this->searchables;
$columnSearch = false;
if (strpos($search, ':')) {
$searchArray = explode(':', $search);
$column = array_shift($searchArray);
if (in_array($column, $this->fillables)) {
$columnSearch = true;
$q->where($column, 'LIKE', '%' . trim(implode(':', $searchArray)) . '%');
}
}
if (!$columnSearch) {
$firstColumn = array_shift($searchColumns);
$q->where($firstColumn, 'LIKE', '%' . $search . '%');
foreach ($searchColumns as $column) {
$q->orWhere($column, 'LIKE', '%' . $search . '%');
}
}
});
}
$result = $query->paginate();
$result['data'] = $this->formatResult($result['data']);
return $result;
}
protected function buildWhere($data)
{
$where = [];
if (isset($data['filter_by_value'])) {
$where[$data['filter_by']] = $data['filter_by_value'];
}
if (isset($data['query'])) {
foreach ($this->searchables as $column) {
if (isset($where[$column])) {
$where[$column] .= '|' . $data['query'];
} else {
$where[$column] = $data['query'];
}
}
}
$args = [1];
$andWhere = $orWhere = '';
$whereClause = "WHERE 1 = '%d'";
foreach ($where as $key => $value) {
if (in_array($key, ['status', 'created_at'])) {
if ($key == 'created_at') {
if (is_array($value)) {
$args[] = $value[0];
$args[] = $value[1];
} else {
$args[] = $value;
$args[] = $value;
}
$andWhere .= " AND `{$key}` >= '%s' AND `{$key}` < '%s' + INTERVAL 1 DAY";
} else {
$args[] = $value;
$andWhere .= " AND `{$key}` = '%s'";
}
} else {
if (strpos($value, '|')) {
$nestedOr = '';
$values = explode('|', $value);
foreach ($values as $itemValue) {
$args[] = '%' . $this->db->esc_like($itemValue) . '%';
$nestedOr .= " OR `{$key}` LIKE '%s'";
}
$orWhere .= ' OR (' . trim($nestedOr, 'OR ') . ')';
} else {
$args[] = '%' . $this->db->esc_like($value) . '%';
$orWhere .= " OR `{$key}` LIKE '%s'";
}
}
}
if ($orWhere) {
$orWhere = 'AND (' . trim($orWhere, 'OR ') . ')';
}
$whereClause = implode(' ', [$whereClause, trim($andWhere), $orWhere]);
return [$whereClause, $args];
}
protected function formatResult($result)
{
$result = is_array($result) ? $result : func_get_args();
foreach ($result as $key => $row) {
$result[$key] = $this->maybeUnserialize((array)$row);
$result[$key]['id'] = (int)$result[$key]['id'];
$result[$key]['retries'] = (int)$result[$key]['retries'];
$result[$key]['from'] = htmlspecialchars($result[$key]['from']);
$result[$key]['subject'] = wp_kses_post(
wp_unslash($result[$key]['subject'])
);
}
return $result;
}
protected function maybeUnserialize(array $data)
{
foreach ($data as $key => $value) {
if ($this->isUnserializable($key)) {
$data[$key] = $this->unserialize($value);
}
}
return $data;
}
protected function isUnserializable($key)
{
$allowedFields = [
'to',
'headers',
'attachments',
'response',
'extra'
];
return in_array($key, $allowedFields);
}
protected function unserialize($data)
{
if (is_serialized($data)) {
if (preg_match('/(^|;)O:[0-9]+:/', $data)) {
return $data;
}
return unserialize(trim($data), ['allowed_classes' => false]);
}
return $data;
}
protected function formatHeaders($headers)
{
foreach ((array)$headers as $key => $header) {
if (is_array($header)) {
$header = $this->formatHeaders($header);
} else {
$header = htmlspecialchars($header);
}
$headers[$key] = $header;
}
return $headers;
}
public function add($data)
{
try {
$data = array_merge($data, [
'created_at' => current_time('mysql')
]);
return $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
->insert($data);
} catch (Exception $e) {
return $e;
}
}
public function delete(array $id)
{
if ($id && $id[0] == 'all') {
return $this->db->query("TRUNCATE TABLE {$this->table}");
}
$ids = array_filter($id, 'intval');
if ($ids) {
return $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
->whereIn('id', $ids)
->delete();
}
return false;
}
public function navigate($data)
{
$filterBy = Arr::get($data, 'filter_by');
foreach (['date', 'daterange', 'datetime', 'datetimerange'] as $field) {
if ($filterBy == $field) {
$data['filter_by'] = 'created_at';
}
}
$id = $data['id'];
$dir = isset($data['dir']) ? $data['dir'] : null;
list($where, $args) = $this->buildWhere($data);
$args = array_merge($args, [$id]);
$sqlNext = "SELECT * FROM {$this->table} {$where} AND `id` > '%d' ORDER BY id LIMIT 2";
$sqlPrev = "SELECT * FROM {$this->table} {$where} AND `id` < '%d' ORDER BY id DESC LIMIT 2";
if ($dir == 'next') {
$query = $this->db->prepare($sqlNext, $args);
} else if ($dir == 'prev') {
$query = $this->db->prepare($sqlPrev, $args);
} else {
foreach (['next' => $sqlNext, 'prev' => $sqlPrev] as $key => $sql) {
$keyResult = $this->db->get_results(
$this->db->prepare($sql, $args)
);
$result[$key] = $this->formatResult($keyResult);
}
return $result;
}
$result = $this->db->get_results($query);
if (count($result) > 1) {
$next = true;
$prev = true;
} else {
if ($dir == 'next') {
$next = false;
$prev = true;
} else {
$next = true;
$prev = false;
}
}
return [
'log' => $result ? $this->formatResult($result[0])[0] : null,
'next' => $next,
'prev' => $prev
];
}
public function find($id)
{
$row = $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
->where('id', $id)
->first();
$row->extra = $this->unserialize($row->extra);
$row->response = $this->unserialize($row->response);
return (array)$row;
}
public function resendEmailFromLog($id, $type = 'retry')
{
$email = $this->find($id);
$email['to'] = $this->unserialize($email['to']);
$email['headers'] = $this->unserialize($email['headers']);
$email['attachments'] = $this->unserialize($email['attachments']);
$email['extra'] = $this->unserialize($email['extra']);
// Convert PHPMailer attachment format to wp_mail format
$wpMailAttachments = [];
if (!empty($email['attachments']) && is_array($email['attachments'])) {
foreach ($email['attachments'] as $attachment) {
if (is_array($attachment)) {
// PHPMailer format: [path, filename, name, encoding, type, isString, disposition, cid]
if (isset($attachment[0]) && is_string($attachment[0])) {
$filePath = $attachment[0];
if (file_exists($filePath) && is_readable($filePath)) {
$wpMailAttachments[] = $filePath;
}
}
} elseif (is_string($attachment)) {
if (file_exists($attachment) && is_readable($attachment)) {
$wpMailAttachments[] = $attachment;
}
}
}
}
$headers = [];
foreach ($email['headers'] as $key => $value) {
if($key == 'content-type' && $value == 'multipart/alternative') {
$value = 'text/html';
}
if (is_array($value)) {
$values = [];
$value = array_filter($value);
foreach ($value as $v) {
if (is_array($v) && isset($v['email'])) {
$v = $v['email'];
}
$values[] = $v;
}
if ($values) {
$headers[] = "{$key}: " . implode(';', $values);
}
} else {
if ($value) {
$headers[] = "{$key}: $value";
}
}
}
$headers = array_merge($headers, [
'From: ' . $email['from']
]);
$to = [];
foreach ($email['to'] as $recipient) {
if (isset($recipient['name'])) {
$to[] = $recipient['name'] . ' <' . $recipient['email'] . '>';
} else {
$to[] = $recipient['email'];
}
}
try {
if (!defined('FLUENTMAIL_LOG_OFF')) {
define('FLUENTMAIL_LOG_OFF', true);
}
$result = wp_mail(
$to,
$email['subject'],
$email['body'],
$headers,
$wpMailAttachments // Use the converted attachment format
);
$updateData = [
'status' => 'sent',
'updated_at' => current_time('mysql'),
];
if (!$result && $type == 'check_realtime' && $email['status'] == 'failed') {
$updateData['status'] = 'failed';
}
if ($type == 'resend') {
$updateData['resent_count'] = intval($email['resent_count']) + 1;
} else {
$updateData['retries'] = intval($email['retries']) + 1;
}
if ($this->updateLog($updateData, ['id' => $id])) {
$email = $this->find($id);
$email['to'] = $this->unserialize($email['to']);
$email['headers'] = $this->unserialize($email['headers']);
$email['attachments'] = $this->unserialize($email['attachments']);
$email['extra'] = $this->unserialize($email['extra']);
return $email;
}
} catch (\PHPMailer\PHPMailer\Exception $e) {
throw $e;
}
}
public function updateLog($data, $where)
{
return $this->db->update($this->table, $data, $where);
}
public function getStats()
{
$succeeded = $this->db->get_var("select COUNT(id) from {$this->table} where status='sent'");
$failed = $this->db->get_var("select COUNT(id) from {$this->table} where status='failed'");
return [
'sent' => $succeeded,
'failed' => $failed
];
}
public function deleteLogsOlderThan($days)
{
try {
$date = gmdate('Y-m-d H:i:s', current_time('timestamp') - $days * DAY_IN_SECONDS);
$query = $this->db->prepare("DELETE FROM {$this->table} WHERE `created_at` < %s", $date);
return $this->db->query($query);
} catch (Exception $e) {
if (wp_get_environment_type() != 'production') {
error_log('Message: ' . $e->getMessage());
}
}
}
public function getTotalCountStat($status, $startDate, $endDate = false)
{
if ($endDate) {
$query = $this->db->prepare(
"SELECT COUNT(*)
FROM {$this->table}
WHERE status = %s
AND created_at >= %s
AND created_at <= %s",
$status,
$startDate,
$endDate
);
} else {
$query = $this->db->prepare(
"SELECT COUNT(*)
FROM {$this->table}
WHERE status = %s
AND created_at >= %s",
$status,
$startDate
);
}
return (int)$this->db->get_var($query);
}
public function getSubjectCountStat($status, $startDate, $endDate)
{
$query = $this->db->prepare(
"SELECT COUNT(DISTINCT(subject))
FROM {$this->table}
WHERE status = %s
AND created_at >= %s
AND created_at <= %s",
$status,
$startDate,
$endDate
);
return (int)$this->db->get_var($query);
}
public function getSubjectStat($status, $statDate, $endDate, $limit = 5)
{
$query = $this->db->prepare(
"SELECT subject,
COUNT(DISTINCT id) AS emails_sent
FROM {$this->table}
WHERE created_at >= %s
AND created_at <= %s
AND status = %s
GROUP BY subject
ORDER BY emails_sent DESC
LIMIT {$limit}",
$statDate,
$endDate,
$status
);
return $this->db->get_results($query, ARRAY_A);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace FluentMail\App\Models;
class Model
{
protected $db = null;
protected $app = null;
public function __construct()
{
$this->app = fluentMail();
$this->db = $GLOBALS['wpdb'];
}
public function getTable()
{
return $this->table;
}
public function __call($method, $params)
{
return call_user_func_array([$this->db, $method], $params);
}
public function getDb()
{
return fluentMailDb();
}
}

View File

@@ -0,0 +1,257 @@
<?php
namespace FluentMail\App\Models;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Models\Traits\SendTestEmailTrait;
class Settings
{
use SendTestEmailTrait;
protected $optionName = FLUENTMAIL . '-settings';
public function get()
{
return fluentMailGetSettings();
}
public function getSettings()
{
return $this->get();
}
public function store($inputs)
{
$settings = $this->getSettings();
$mappings = $this->getMappings($settings);
$connections = $this->getConnections($settings);
$email = Arr::get($inputs, 'connection.sender_email');
$key = $inputs['connection_key'];
if (isset($connections[$key])) {
$mappings = array_filter($mappings, function ($mappingKey) use ($key) {
return $mappingKey != $key;
});
unset($connections[$key]);
}
$primaryEmails = [];
foreach ($connections as $connection) {
$primaryEmails[] = $connection['provider_settings']['sender_email'];
}
$uniqueKey = $this->generateUniqueKey($email);
$extraMappings = $inputs['valid_senders'];
foreach ($extraMappings as $emailIndex => $email) {
if (in_array($email, $primaryEmails)) {
unset($extraMappings[$emailIndex]);
}
}
$extraMappings[] = $email;
$extraMappings = array_unique($extraMappings);
$extraMappings = array_fill_keys($extraMappings, $uniqueKey);
$mappings = array_merge($mappings, $extraMappings);
$providers = fluentMail(Manager::class)->getConfig('providers');
$title = $providers[$inputs['connection']['provider']]['title'];
$connections[$uniqueKey] = [
'title' => $title,
'provider_settings' => $inputs['connection']
];
$settings['mappings'] = $mappings;
$settings['connections'] = $connections;
if ($settings['mappings'] && $settings['connections']) {
$validMappings = array_keys(Arr::get($settings, 'connections', []));
$settings['mappings'] = array_filter($settings['mappings'], function ($key) use ($validMappings) {
return in_array($key, $validMappings);
});
}
$misc = $this->getMisc();
if (!$misc) {
$misc = [
'log_emails' => 'yes',
'log_saved_interval_days' => '14',
'disable_fluentcrm_logs' => 'no',
'default_connection' => ''
];
}
if (empty($misc['default_connection']) || $misc['default_connection'] == $key) {
$misc['default_connection'] = $uniqueKey;
$settings['misc'] = $misc;
}
fluentMailSetSettings($settings);
return $settings;
}
public function generateUniqueKey($email)
{
return md5($email);
}
public function saveGlobalSettings($data)
{
return fluentMailSetSettings($data);
}
public function delete($key)
{
$settings = $this->getSettings();
$mappings = $settings['mappings'];
$connections = $settings['connections'];
unset($connections[$key]);
foreach ($mappings as $mapKey => $mapValue) {
if ($mapValue == $key) {
unset($mappings[$mapKey]);
}
}
$settings['mappings'] = $mappings;
$settings['connections'] = $connections;
if (Arr::get($settings, 'misc.default_connection') == $key) {
$default = Arr::get($settings, 'mappings', []);
$default = reset($default);
Arr::set($settings, 'misc.default_connection', $default ?: '');
}
if (Arr::get($settings, 'misc.fallback_connection') == $key) {
Arr::set($settings, 'misc.fallback_connection', '');
}
fluentMailSetSettings($settings);
return $settings;
}
public function getDefaults()
{
$url = str_replace(
['http://', 'http://www.', 'www.'],
'',
get_bloginfo('wpurl')
);
return [
'sender_name' => $url,
'sender_email' => get_option('admin_email')
];
}
public function getVerifiedEmails()
{
$optionName = FLUENTMAIL . '-ses-verified-emails';
return get_option($optionName, []);
}
public function saveVerifiedEmails($verifiedEmails)
{
$optionName = FLUENTMAIL . '-ses-verified-emails';
$emails = get_option($optionName, []);
update_option($optionName, array_unique(array_merge(
$emails, $verifiedEmails
)));
}
public function getConnections($settings = null)
{
$settings = $settings ?: $this->getSettings();
return Arr::get($settings, 'connections', []);
}
public function getMappings($settings = null)
{
$settings = $settings ?: $this->getSettings();
return Arr::get($settings, 'mappings', []);
}
public function getMisc($settings = null)
{
$settings = $settings ?: $this->getSettings();
return Arr::get($settings, 'misc', []);
}
public function getConnection($email)
{
$settings = $this->getSettings();
$mappings = $this->getMappings($settings);
$connections = $this->getConnections($settings);
if (isset($mappings[$email])) {
if (isset($connections[$mappings[$email]])) {
return $connections[$mappings[$email]];
}
}
return [];
}
public function updateMiscSettings($misc)
{
$settings = $this->get();
$settings['misc'] = $misc;
$this->saveGlobalSettings($settings);
}
public function updateConnection($fromEmail, $connection)
{
$key = $this->generateUniqueKey($fromEmail);
$settings = $this->getSettings();
$settings['connections'][$key]['provider_settings'] = $connection;
$this->saveGlobalSettings($settings);
}
public function notificationSettings()
{
$defaults = [
'enabled' => 'no',
'notify_email' => '{site_admin}',
'notify_days' => ['Mon'],
'active_channel' => '',
'telegram' => [
'status' => 'no',
'token' => ''
],
'slack' => [
'status' => 'no',
'token' => '',
'webhook_url' => ''
],
'discord' => [
'status' => 'no',
'channel_name' => '',
'webhook_url' => ''
],
];
$settings = get_option('_fluent_smtp_notify_settings', []);
return wp_parse_args($settings, $defaults);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FluentMail\App\Models\Traits;
trait SendTestEmailTrait
{
public function sendTestEmail($data, $settings)
{
if (empty($settings) || empty($data)) return;
$to = $data['email'];
$subject = sprintf(__('Fluent SMTP: Test Email - %s', 'fluent-smtp'), get_bloginfo('name'));
if ($data['isHtml'] == 'true') {
$headers[] = 'Content-Type: text/html; charset=UTF-8';
$body = (string) fluentMail('view')->make('admin.email_html');
$subject .= ' - HTML Version';
} else {
$headers[] = 'Content-Type: text/plain; charset=UTF-8';
$body = (string) fluentMail('view')->make('admin.email_text');
$subject .= ' - Text Version';
}
if (!empty($data['from'])) {
$headers[] = 'From: ' . $data['from'];
}
if(!defined('FLUENTMAIL_TEST_EMAIL')) {
define('FLUENTMAIL_TEST_EMAIL', true);
}
return wp_mail($to, $subject, $body, $headers);
}
}