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,115 @@
<?php
namespace WPDRMS\ASP\Options\Data;
use Exception;
use JsonSerializable;
use WPDRMS\ASP\Options\Factories\OptionFactory;
use WPDRMS\ASP\Options\Models\Option;
/**
* Handles option generation on the fly from DB stored values.
*
* @see .phpstorm.meta.php to specify Option types for get() method
*/
abstract class AbstractOptionData implements OptionData, JsonSerializable {
protected const OPTIONS = array(
'sample_option_name1' => array(
'type' => 'border',
'default_args' => array(
'width' => 2,
'color' => 'blue',
),
),
'sample_option_name2' => array(
'type' => 'shadow',
),
);
/**
* @var Array<string, string>
*/
protected array $data;
/**
* @var Array<string, Option>
*/
protected array $options;
/**
* @param Array<string, string> $data
*/
public function __construct( array $data ) {
$this->setData($data);
}
public function setData( array $data ): void {
$this->data = $data;
$this->options = array();
}
/**
* @param string $option_name
* @return Option
* @throws Exception
*/
public function get( string $option_name ): Option {
if ( isset($this->options[ $option_name ]) ) {
return $this->options[ $option_name ];
}
if ( !isset(static::OPTIONS[ $option_name ]) ) {
throw new Exception('Option key invalid!');
}
if ( isset($this->data[ $option_name ]) ) {
if ( is_string($this->data[ $option_name ]) ) {
$args = json_decode($this->data[ $option_name ]);
} else {
$args = $this->data[ $option_name ];
}
} elseif ( isset(static::OPTIONS[ $option_name ]['default_args']) ) {
$args = static::OPTIONS[ $option_name ]['default_args'];
} else {
$args = array();
}
$this->options[ $option_name ] = OptionFactory::instance()->create(
static::OPTIONS[ $option_name ]['type'],
$args
);
return $this->options[ $option_name ];
}
/**
* @return Option[]
* @throws Exception
*/
public function getAll(): array {
foreach ( static::OPTIONS as $key => $option ) {
$this->get( $key );
}
return $this->options;
}
public function jsonSerialize(): array {
try {
return $this->getAll();
} catch ( Exception $e ) {
return array();
}
}
public function toJson(): string {
try {
$res = wp_json_encode($this->getAll());
} catch ( Exception $e ) {
return '{}';
}
return $res === false ? '{}' : $res;
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace WPDRMS\ASP\Options\Data;
/**
* Search instance Option Group
*/
final class IndexTableOptions extends AbstractOptionData {
protected const OPTIONS = array(
'attachment_exclude_directories' => array(
'type' => 'directory_list',
),
'attachment_include_directories' => array(
'type' => 'directory_list',
),
);
}

View File

@@ -0,0 +1,26 @@
<?php
namespace WPDRMS\ASP\Options\Data;
use WPDRMS\ASP\Options\Models\Option;
/**
* Base interface for Option Group Storage
*/
interface OptionData {
/**
* Reset the options and the data with new
*
* Ex.: when used in previews etc..
*
* @param Array<string, string> $data
*/
public function setData( array $data ): void;
public function get( string $option_name ): Option;
/**
* @return Option[]
*/
public function getAll(): array;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace WPDRMS\ASP\Options\Data;
/**
* Search instance Option Group
*/
final class SearchOptions extends AbstractOptionData {
protected const OPTIONS = array(
'attachment_exclude_directories' => array(
'type' => 'directory_list',
),
'attachment_include_directories' => array(
'type' => 'directory_list',
),
);
}

View File

@@ -0,0 +1,47 @@
<?php
namespace WPDRMS\ASP\Options\Factories;
use WPDRMS\ASP\Options\Models\BorderOption;
use WPDRMS\ASP\Options\Models\BoxShadowOption;
use WPDRMS\ASP\Options\Models\DirectoryListOption;
use WPDRMS\ASP\Options\Models\Option;
use WPDRMS\ASP\Patterns\SingletonTrait;
use InvalidArgumentException;
class OptionFactory {
use SingletonTrait;
/**
* @var array<string, class-string>
*/
private const TYPES = array(
'border' => BorderOption::class,
'box_shadow' => BoxShadowOption::class,
'directory_list' => DirectoryListOption::class,
);
/**
* @param string $type
* @param mixed ...$args
*
* @return Option
* @throws InvalidArgumentException
*/
public function create( string $type, ...$args ): Option {
if ( !isset(self::TYPES[ $type ]) ) {
throw new InvalidArgumentException("Invalid option type: $type");
}
$class = self::TYPES[ $type ];
/**
* Unfortunately there is no better way for now to intelliJ to recognize
* type hints based on the return value.
* A proper solution would be: https://phpstan.org/r/a01e1e49-6f05-43a8-aac7-aded770cd88a
* But in that case OptionFactory::instance()->create("className")->attr type hint is not working
* Maybe in the future of IntelliJ?
*/
return new $class(...$args);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace WPDRMS\ASP\Options\Models;
use JsonSerializable;
use WPDRMS\ASP\Patterns\JsonSerializablePublicProperties;
use WPDRMS\ASP\Utils\ArrayUtils;
/**
* @template T of Array<string, mixed>
*/
abstract class AbstractOption implements Option, JsonSerializable {
use JsonSerializablePublicProperties;
/**
* @var T
*/
protected array $defaults = array();
/**
* The arguments from the constructor, merged and corrected with the defaults
*
* @var T
*/
protected array $args = array();
/**
* @param Array<string, mixed> $args
*/
public function __construct( array $args ) {
$this->args = ArrayUtils::arrayMergeRecursiveDistinct($this->defaults, $args);
}
public function value(): array {
return $this->publicProperties();
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace WPDRMS\ASP\Options\Models;
/**
* @phpstan-type BorderOptionArgs array{
* width: int,
* color: 'red'|'blue'
* }
* @extends AbstractOption<BorderOptionArgs>
*/
class BorderOption extends AbstractOption {
protected array $defaults = array(
'width' => 1,
'color' => 'red',
);
public int $width = 0;
public string $color = 'red';
public function __construct( array $args ) {
parent::__construct($args);
$this->width = $this->args['width'];
$this->color = $this->args['color'];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace WPDRMS\ASP\Options\Models;
/**
* @phpstan-type BoxShadowOptionArgs array{
* left: int,
* right: int
* }
* @extends AbstractOption<BoxShadowOptionArgs>
*/
class BoxShadowOption extends AbstractOption {
protected array $defaults = array(
'left' => 2,
'right' => 3,
);
public int $left = 2;
public int $right = 3;
public function __construct( array $args ) {
parent::__construct($args);
$this->left = $this->args['left'];
$this->right = $this->args['right'];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace WPDRMS\ASP\Options\Models;
/**
* @phpstan-type DirectoryListOptionArgs array{
* directories: string[],
* }
* @extends AbstractOption<DirectoryListOptionArgs>
*/
class DirectoryListOption extends AbstractOption {
protected array $defaults = array(
'directories' => array(),
);
/**
* @var string[]
*/
public array $directories;
public function __construct( array $args ) {
parent::__construct($args);
$this->directories = array_filter($this->args['directories']);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace WPDRMS\ASP\Options\Models;
interface Option {
/**
* Returns all public properties from the option
*
* @return Array<string, mixed>
*/
public function value(): array;
}

View File

@@ -0,0 +1,38 @@
<?php
namespace WPDRMS\ASP\Options;
use WPDRMS\ASP\Asset\AssetInterface;
use WPDRMS\ASP\Patterns\SingletonTrait;
class OptionAssets implements AssetInterface {
use SingletonTrait;
/**
* @var string[]
*/
private array $registered = array();
public function register(): void {
if ( wd_asp()->manager->getContext() !== 'backend' ) {
return;
}
$metadata = require_once ASP_PATH . '/build/js/admin-global.asset.php'; // @phpstan-ignore-line
wp_enqueue_script(
'wdo-asp-global-backend',
ASP_URL_NP . 'build/js/admin-global.js',
$metadata['dependencies'],
$metadata['version'],
array(
'in_footer' => true,
)
);
do_action('asp/asset/js/wdo-asp-global-backend');
}
public function deregister(): void {
foreach ( $this->registered as $handle ) {
wp_dequeue_script($handle);
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace WPDRMS\ASP\Options;
use WPDRMS\ASP\Core\Instances;
use WPDRMS\ASP\Core\Models\SearchInstance;
use WPDRMS\ASP\Options\Data\SearchOptions;
use WPDRMS\ASP\Options\Factories\OptionFactory;
use WPDRMS\ASP\Options\Models\BorderOption;
use WPDRMS\ASP\Options\Models\BoxShadowOption;
use WPDRMS\Backend\Options\Option;
class OptionsController {
public function __construct() {
$f = OptionFactory::instance();
// $x = $f->create(BoxShadowOption::class);
// echo $x->left; // Type hint yay!
//
// $x = $f->create(BorderOption::class);
// echo $x->left; // Correctly saying property doesn't exist
// $y = $f->create('border');
$x = Instances::getInstance()->get(1);
$xx = $x->options->get('box_border');
foreach ( $x as $inst ) {
$box_border = $inst->options->get('box_border');
}
$test = new SearchOptions(
array(
'border' => '{}',
)
);
$y = $test->get('box_border');
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace WPDRMS\ASP\Options\Routes;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WPDRMS\ASP\Rest\AbstractRest;
use WPDRMS\ASP\Utils\FileManager;
class DirectoriesRoute extends AbstractRest {
public function registerRoutes(): void {
register_rest_route(
ASP_DIR,
'options/directories/get',
array(
'methods' => 'GET',
'callback' => array(
$this,
'getDirectories',
),
'permission_callback' => array(
$this,
'allowOnlyAdmins',
),
)
);
}
/**
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function getDirectories( WP_REST_Request $request ): WP_REST_Response {
try {
$source = $request->get_param('source');
switch ( $source ) {
case 'root':
$source = ABSPATH;
break;
case 'wp-content':
$source = WP_CONTENT_DIR;
break;
case 'uploads':
$uploads = wp_get_upload_dir();
if ( false === $uploads['error'] ) {
$source = $uploads['basedir'];
} else {
$source = WP_CONTENT_DIR;
}
break;
default:
$source = WP_CONTENT_DIR;
}
$directories = FileManager::instance()->safeListDirectories($source);
return new WP_REST_Response(
$directories,
is_wp_error($directories) ? 500 : 200
);
} catch ( \Exception $e ) {
return new WP_REST_Response(
new WP_Error('asp_directories_get', $e->getMessage()),
400
);
}
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace WPDRMS\ASP\Options\Routes;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WPDRMS\ASP\Rest\AbstractRest;
class IndexTableOptionsRoute extends AbstractRest {
public function registerRoutes(): void {
register_rest_route(
ASP_DIR,
'options/index_table/get',
array(
'methods' => 'GET',
'callback' => array(
$this,
'getIndexTableOptions',
),
'permission_callback' => array(
$this,
'allowOnlyAdmins',
),
)
);
}
/**
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function getIndexTableOptions( WP_REST_Request $request ): WP_REST_Response {
try {
$data = wd_asp()->o['asp_it_options'];
return new WP_REST_Response(
$data,
200
);
} catch ( \Exception $e ) {
return new WP_REST_Response(
new WP_Error('asp_it_options_get', $e->getMessage()),
400
);
}
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace WPDRMS\ASP\Options\Routes;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WPDRMS\ASP\Rest\AbstractRest;
class SearchOptionsRoute extends AbstractRest {
public function registerRoutes(): void {
register_rest_route(
ASP_DIR,
'options/search_instance/get',
array(
'methods' => 'GET',
'callback' => array(
$this,
'getSearchInstanceOptions',
),
'permission_callback' => array(
$this,
'allowOnlyAdmins',
),
)
);
/**
* List all search instances without options
*/
register_rest_route(
ASP_DIR,
'options/search_instance/list',
array(
'methods' => 'GET',
'callback' => array(
$this,
'listSearchInstances',
),
'permission_callback' => array(
$this,
'allowOnlyAdmins',
),
)
);
}
/**
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function getSearchInstanceOptions( WP_REST_Request $request ): WP_REST_Response {
try {
$id = $request->get_param('id') ?? 0;
if ( !wd_asp()->instances->exists($id) ) {
return new WP_REST_Response(
new WP_Error('search_instantes_get', 'Search instance does not exist.'),
400
);
}
$data = wd_asp()->instances->get($id)['data'];
$data['advtitlefield'] = stripcslashes($data['advtitlefield']);
$data['advdescriptionfield'] = stripcslashes($data['advdescriptionfield']);
$data['user_search_advanced_title_field'] = stripcslashes($data['user_search_advanced_title_field']);
$data['user_search_advanced_description_field'] =
stripcslashes($data['user_search_advanced_description_field']);
return new WP_REST_Response(
$data,
200
);
} catch ( \Exception $e ) {
return new WP_REST_Response(
new WP_Error('taxonomy_terms_get', $e->getMessage()),
400
);
}
}
/**
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function listSearchInstances( WP_REST_Request $request ): WP_REST_Response {
try {
$data = wd_asp()->instances->getWithoutData() ?? array();
return new WP_REST_Response(
array_values($data),
200
);
} catch ( \Exception $e ) {
return new WP_REST_Response(
new WP_Error('taxonomy_terms_get', $e->getMessage()),
400
);
}
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace WPDRMS\ASP\Options\Routes;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WPDRMS\ASP\Rest\AbstractRest;
class TaxonomyTermsRoute extends AbstractRest {
public function registerRoutes(): void {
register_rest_route(
ASP_DIR,
'options/taxonomies/get',
array(
'methods' => 'GET',
'callback' => array(
$this,
'getTaxonomies',
),
'permission_callback' => array(
$this,
'allowOnlyAdmins',
),
)
);
register_rest_route(
ASP_DIR,
'options/taxonomy_terms/get',
array(
'methods' => 'GET',
'callback' => array(
$this,
'getTerms',
),
'permission_callback' => array(
$this,
'allowOnlyAdmins',
),
)
);
}
/**
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function getTaxonomies( WP_REST_Request $request ): WP_REST_Response {
try {
$taxonomies = get_taxonomies(array(), 'objects');
return new WP_REST_Response(
$taxonomies,
is_wp_error($taxonomies) ? 500 : 200
);
} catch ( \Exception $e ) {
return new WP_REST_Response(
new WP_Error('taxonomies_get', $e->getMessage()),
400
);
}
}
/**
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function getTerms( WP_REST_Request $request ): WP_REST_Response {
try {
$taxonomy = $request->get_param('taxonomy');
$term_ids = $request->get_param('term_ids');
$terms = get_terms(
array(
'taxonomy' => $taxonomy,
'include' => $term_ids,
'hide_empty' => false,
)
);
return new WP_REST_Response(
$terms,
is_wp_error($terms) ? 500 : 200
);
} catch ( \Exception $e ) {
return new WP_REST_Response(
new WP_Error('taxonomy_terms_get', $e->getMessage()),
400
);
}
}
}