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,37 @@
<?php
namespace WPDRMS\ASP\Modal\Callbacks;
use WPDRMS\ASP\Utils\Server;
use WPDRMS\ASP\Misc\PluginLicense;
class ModalCallbacks {
/**
* Determines whether to show the 'activate_license' modal.
*
* @return bool
*/
public static function shouldShowActivateLicense(): bool {
return !ASP_DEMO
&& !Server::isLocalEnvironment()
&& !PluginLicense::isActivated();
}
/**
* Determines whether to show the 'take_survey' modal.
*
* @return bool
*/
public static function shouldShowTakeSurvey(): bool {
return !ASP_DEMO;
}
/**
* When used, the modal will be displayed every time
*
* @return bool
*/
public static function shouldShowEveryTime(): bool {
return true;
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace WPDRMS\ASP\Modal\Factories;
use Exception;
use InvalidArgumentException;
use WPDRMS\ASP\Modal\Services\TimedModalService;
/**
* Builder class for creating individual modal configurations.
*
* @phpstan-import-type TimedModalData from TimedModalService
*/
class ModalBuilder {
/** @var TimedModalData|array{} */
private array $modal = array();
/**
* Sets the heading of the modal.
*
* @param string $heading
* @return self
*/
public function setHeading( string $heading ): self {
$this->modal['heading'] = $heading;
return $this;
}
/**
* Sets the content of the modal.
*
* @param string $content
* @return self
*/
public function setContent( string $content ): self {
$this->modal['content'] = $content;
return $this;
}
/**
* Sets the maximum number of times the modal can be shown.
*
* @param int $max_times_shown
* @return self
*/
public function setMaxTimesShown( int $max_times_shown ): self {
$this->modal['max_times_shown'] = $max_times_shown;
return $this;
}
/**
* Sets the initial delay before showing the modal.
*
* @param int $first_delay
* @return self
*/
public function setFirstDelay( int $first_delay ): self {
$this->modal['first_delay'] = $first_delay;
return $this;
}
/**
* Sets the subsequent delay between showing the modal.
*
* @param int $delay
* @return self
*/
public function setDelay( int $delay ): self {
$this->modal['delay'] = $delay;
return $this;
}
/**
* Sets whether the modal should close when clicking the background.
*
* @param bool $close_on_background_click
* @return self
*/
public function setCloseOnBackgroundClick( bool $close_on_background_click ): self {
$this->modal['close_on_background_click'] = $close_on_background_click;
return $this;
}
/**
* Sets the callback to determine if the modal should be shown.
*
* @param callable $callback
* @return self
*/
public function setCallback( callable $callback ): self {
$this->modal['callback'] = $callback;
return $this;
}
/**
* Sets the type of the modal ('info' or 'warning').
*
* @param 'info'|'warning' $type
* @return self
* @throws InvalidArgumentException
*/
public function setType( string $type ): self {
if ( !in_array($type, array( 'info', 'warning' ), true) ) {
throw new InvalidArgumentException("Type must be 'info' or 'warning'.");
}
$this->modal['type'] = $type;
return $this;
}
/**
* Sets the buttons configuration for the modal.
*
* @param array<string, array<string, mixed>> $buttons
* @return self
*/
public function setButtons( array $buttons ): self {
$this->modal['buttons'] = $buttons;
return $this;
}
/**
* Builds and returns the modal configuration array.
*
* @return TimedModalData
*
* @throws InvalidArgumentException If required fields are missing.
* @throws Exception
*/
public function build(): array {
$required_fields = array(
'heading',
'content',
'max_times_shown',
'first_delay',
'delay',
'callback',
'type',
);
if ( empty($this->modal) ) {
throw new Exception('Modal data is empty.'); // @phpcs:ignore
}
foreach ( $required_fields as $field ) {
if ( !array_key_exists($field, $this->modal) ) {
throw new InvalidArgumentException("Missing required field: {$field}"); // @phpcs:ignore
}
}
return $this->modal;
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace WPDRMS\ASP\Modal\Factories;
use Exception;
use WPDRMS\ASP\Modal\Callbacks\ModalCallbacks;
use WPDRMS\ASP\Modal\Services\TimedModalService;
/**
* Factory class for generating TimedModalData.
*
* @phpstan-import-type TimedModalData from TimedModalService
*/
class ModalFactory implements ModalFactoryInterface {
/**
* Generates and returns the complete TimedModalData array.
*
* @return Array<string, TimedModalData>
* @throws Exception
*/
public function createModals(): array {
$modals = array();
// Activate License Modal
$modals['activate_license'] = ( new ModalBuilder() )
->setHeading('Please verify your license!')
->setContent(
'
Verifying the license ensures that the plugin is secure and automatically up to date.<br>
Don\'t worry, it takes only a minute!
'
)
->setMaxTimesShown(1000)
->setFirstDelay(0)
->setDelay(0)
->setCloseOnBackgroundClick(false)
->setCallback(array( ModalCallbacks::class, 'shouldShowActivateLicense' ))
->setType('info')
->setButtons(
array(
'okay' => array(
'type' => 'okay',
'text' => 'Let\'s go!',
'href' => '/wp-admin/admin.php?page=asp_updates_help',
),
)
)
->build();
// Take Survey Modal
/*
$modals['take_survey'] = ( new ModalBuilder() )
->setHeading('Help us improve Ajax Search Pro!')
->setContent('Please take a super quick survey, it will help us tremendously. Thank you!')
->setMaxTimesShown(2)
->setFirstDelay(2 *3600)
->setDelay(3600 *24)
->setCallback(array( ModalCallbacks::class, 'shouldShowTakeSurvey' ))
->setType('info')
->setButtons(
array(
'okay' => array(
'type' => 'okay',
'text' => 'Sure! Let\'s go!',
'href' => 'https://us9.list-manage.com/survey?u=370663b5e3df02747aa5673ed&id=7040339d37&attribution=false',
'target' => '_blank',
'dismmisses_forever' => true,
),
'cancel' => array(
'type' => 'cancel',
'text' => 'Remind me Later',
),
'never' => array(
'type' => 'secondary',
'text' => 'No thank you!',
'dismmisses_forever' => true,
),
)
)
->build();
*/
return $modals;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace WPDRMS\ASP\Modal\Factories;
use WPDRMS\ASP\Modal\Services\TimedModalService;
/**
* Interface ModalFactoryInterface
*
* Defines the contract for creating modal configurations.
*
* @phpstan-import-type TimedModalData from TimedModalService
*/
interface ModalFactoryInterface {
/**
* Generates and returns the complete TimedModalData array.
*
* @return Array<string, TimedModalData>
*/
public function createModals(): array;
}

View File

@@ -0,0 +1,57 @@
<?php
namespace WPDRMS\ASP\Modal\Models;
/**
* @phpstan-type ModalButtons Array<string, array{
* text: string,
* href: string,
* type?: 'okay'|'cancel'|string,
* target?: '_blank',
* dismmisses_forever?: boolean
* }>
*/
class TimedModal {
public string $type;
public string $name;
public string $displayed_cookie_name;
public string $clicked_okay_cookie_name;
public string $heading;
public string $content;
public bool $close_on_background_click;
/**
* @var ModalButtons
*/
public ?array $buttons;
/**
* @param string $type
* @param string $name
* @param string $displayed_cookie_name
* @param string $clicked_okay_cookie_name
* @param string $heading
* @param string $content
* @param bool $close_on_background_click
* @param ModalButtons $buttons
*/
public function __construct(
string $type,
string $name,
string $displayed_cookie_name,
string $clicked_okay_cookie_name,
string $heading,
string $content,
bool $close_on_background_click = true,
?array $buttons = null
) {
$this->type = $type;
$this->name = $name;
$this->displayed_cookie_name = $displayed_cookie_name;
$this->clicked_okay_cookie_name = $clicked_okay_cookie_name;
$this->heading = $heading;
$this->content = $content;
$this->close_on_background_click = $close_on_background_click;
$this->buttons = $buttons;
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace WPDRMS\ASP\Modal\Services;
use WPDRMS\ASP\Modal\Factories\ModalFactoryInterface;
use WPDRMS\ASP\Modal\Models\TimedModal;
use WPDRMS\ASP\Patterns\SingletonTrait;
/**
* @phpstan-import-type ModalButtons from TimedModal
* @phpstan-type TimedModalData array{
* heading: string,
* content: string,
* max_times_shown: int,
* first_delay: int,
* delay: int,
* close_on_background_click?: boolean,
* callback: Array<string, string>,
* type: 'info'|'warning',
* buttons?: ModalButtons,
* }
*
* @phpstan-type StoredTimedModalData array{
* last_shown: int,
* times_shown: int
* }
*/
class TimedModalService {
use SingletonTrait;
const MODAL_OPTION_NAME = '_asp_timed_modal_data';
const DISPLAYED_COOKIE_PREFIX = '_asp_timed_modal_';
const CLICKED_OKAY_COOKIE_PREFIX = '_asp_timed_modal_okay_';
/**
* @var Array<string, TimedModalData>
*/
private array $modals;
/**
* @var ModalFactoryInterface
*/
private ModalFactoryInterface $modal_factory;
/**
* Constructor with dependency injection.
*
* @param ModalFactoryInterface $modal_factory
*/
public function __construct( ModalFactoryInterface $modal_factory ) {
$this->modal_factory = $modal_factory;
// Use the injected ModalFactory to create modals
$this->modals = $this->modal_factory->createModals();
}
/**
* Initializes the modal queue, removes modals that no longer should be in queue
*
* @return void
*/
public function init(): void {
$modal_data = get_site_option(self::MODAL_OPTION_NAME);
$options_changed = false;
if ( $modal_data === false ) {
$modal_data = array();
}
foreach ( $this->modals as $name => $data ) {
if ( isset($modal_data[ $name ]) ) {
continue;
}
$modal_data[ $name ] = array(
'last_shown' => time(),
'times_shown' => 0,
);
$options_changed = true;
}
foreach ( $this->modals as $name => $data ) {
if ( isset($_COOKIE[ self::CLICKED_OKAY_COOKIE_PREFIX . $name ]) ) { // Clicked okay, don't show again
$modal_data[ $name ]['times_shown'] = $this->modals[ $name ]['max_times_shown'];
$modal_data[ $name ]['last_shown'] = time();
$options_changed = true;
setcookie(self::CLICKED_OKAY_COOKIE_PREFIX . $name, '', -1, '/');
setcookie(self::DISPLAYED_COOKIE_PREFIX . $name, '', -1, '/');
} elseif ( isset($_COOKIE[ self::DISPLAYED_COOKIE_PREFIX . $name ]) ) { // Dismissed, increase counter
++$modal_data[ $name ]['times_shown'];
$modal_data[ $name ]['last_shown'] = time();
$options_changed = true;
setcookie(self::DISPLAYED_COOKIE_PREFIX . $name, '', -1, '/');
}
}
if ( $options_changed ) {
$modal_data = array_intersect_key($modal_data, $this->modals);
update_site_option(self::MODAL_OPTION_NAME, $modal_data);
}
}
/**
* Gets the next modal that should be displayed from the queue.
*
* @return TimedModal|null
*/
public function get(): ?TimedModal {
/**
* @var Array<string, StoredTimedModalData> $modals
*/
$modals = get_site_option(self::MODAL_OPTION_NAME, array());
foreach ( $modals as $name => $data ) {
if ( !isset($this->modals[ $name ]) ) {
continue;
}
$delay = $data['times_shown'] === 0 ? $this->modals[ $name ]['first_delay'] : $this->modals[ $name ]['delay'];
if (
$data['times_shown'] < $this->modals[ $name ]['max_times_shown'] &&
is_callable($this->modals[ $name ]['callback']) &&
call_user_func($this->modals[ $name ]['callback']) &&
( $delay + $data['last_shown'] ) < time()
) {
return apply_filters(
'asp/timed_modal/get',
new TimedModal(
$this->modals[ $name ]['type'],
$name,
self::DISPLAYED_COOKIE_PREFIX . $name,
self::CLICKED_OKAY_COOKIE_PREFIX . $name,
$this->modals[ $name ]['heading'],
$this->modals[ $name ]['content'],
$this->modals[ $name ]['close_on_background_click'] ?? true,
$this->modals[ $name ]['buttons'] ?? null,
)
);
}
}
return apply_filters('asp/timed_modal/get', null);
}
/**
* Wipes out the modal data from the database
*
* @return void
*/
public function wipe(): void {
delete_option(self::MODAL_OPTION_NAME);
}
}