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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,124 @@
/**
* External dependencies
*/
import { map } from 'lodash'
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n'
import { withSelect } from '@wordpress/data'
import { InspectorControls } from '@wordpress/block-editor'
import { PanelBody, SelectControl, TextControl } from '@wordpress/components'
/**
* Format array of image sizes.
*
* @param {Array} imageSizes Array of image sizes.
* @return {Array} Formatted array.
*/
const getImageSizeOptions = ( imageSizes ) => {
return map( imageSizes, ( { name, slug } ) => ( {
value: slug,
label: name,
} ) )
}
/**
* Adds controls to the editor sidebar to control params.
*
* @param {Object} props This component's props.
*/
const Inspector = ( { imageSizes, attributes, setAttributes } ) => {
const imageSizeOptions = getImageSizeOptions( imageSizes )
return (
<InspectorControls key={ 'inspector' }>
<PanelBody title={ __( 'FAQ Options', 'rank-math' ) }>
<SelectControl
label={ __( 'List Style', 'rank-math' ) }
value={ attributes.listStyle }
options={ [
{
value: '',
label: __( 'None', 'rank-math' ),
},
{
value: 'numbered',
label: __( 'Numbered', 'rank-math' ),
},
{
value: 'unordered',
label: __( 'Unordered', 'rank-math' ),
},
] }
onChange={ ( listStyle ) => {
setAttributes( { listStyle } )
} }
/>
<SelectControl
label={ __( 'Title Wrapper', 'rank-math' ) }
value={ attributes.titleWrapper }
options={ [
{ value: 'h2', label: __( 'H2', 'rank-math' ) },
{ value: 'h3', label: __( 'H3', 'rank-math' ) },
{ value: 'h4', label: __( 'H4', 'rank-math' ) },
{ value: 'h5', label: __( 'H5', 'rank-math' ) },
{ value: 'h6', label: __( 'H6', 'rank-math' ) },
{ value: 'p', label: __( 'P', 'rank-math' ) },
{ value: 'div', label: __( 'DIV', 'rank-math' ) },
] }
onChange={ ( titleWrapper ) => {
setAttributes( { titleWrapper } )
} }
/>
<SelectControl
label={ __( 'Image Size', 'rank-math' ) }
value={ attributes.sizeSlug }
options={ imageSizeOptions }
onChange={ ( sizeSlug ) => {
setAttributes( { sizeSlug } )
} }
/>
</PanelBody>
<PanelBody title={ __( 'Styling Options', 'rank-math' ) }>
<TextControl
label={ __( 'Title Wrapper CSS Class(es)', 'rank-math' ) }
value={ attributes.titleCssClasses }
onChange={ ( titleCssClasses ) => {
setAttributes( { titleCssClasses } )
} }
/>
<TextControl
label={ __( 'Content Wrapper CSS Class(es)', 'rank-math' ) }
value={ attributes.contentCssClasses }
onChange={ ( contentCssClasses ) => {
setAttributes( { contentCssClasses } )
} }
/>
<TextControl
label={ __( 'List CSS Class(es)', 'rank-math' ) }
value={ attributes.listCssClasses }
onChange={ ( listCssClasses ) => {
setAttributes( { listCssClasses } )
} }
/>
</PanelBody>
</InspectorControls>
)
}
export default withSelect( ( select, props ) => {
const { getSettings } = select( 'core/block-editor' )
const { imageSizes } = getSettings()
return {
...props,
imageSizes,
}
} )( Inspector )

View File

@@ -0,0 +1,155 @@
/**
* External dependencies
*/
import classnames from 'classnames'
/**
* Internal dependencies
*/
import MediaUploader from '@blocks/shared/MediaUploader'
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n'
import { applyFilters } from '@wordpress/hooks'
import { Button } from '@wordpress/components'
import { Component } from '@wordpress/element'
import { RichText, MediaUpload } from '@wordpress/block-editor'
/**
* A Question and answer pair within FAQ block.
*/
class Question extends Component {
/**
* Renders the component.
*
* @return {Component} Question editor.
*/
render() {
const {
title,
content,
visible,
imageID,
sizeSlug,
titleWrapper,
titleCssClasses,
contentCssClasses,
} = this.props
const wrapperClasses = classnames( 'rank-math-question-wrapper', {
'question-not-visible': ! visible,
} )
return (
<div className={ wrapperClasses }>
<div className="rank-math-item-header">
<RichText
tagName={ titleWrapper }
className={
'rank-math-faq-question rank-math-block-title' +
titleCssClasses
}
value={ title }
onChange={ ( newTitle ) => {
this.setQuestionProp( 'title', newTitle )
} }
placeholder={ __( 'Question…', 'rank-math' ) }
/>
<div className="rank-math-block-actions">
{ applyFilters( 'rank_math_block_faq_actions', '', this.props, this ) }
<Button
className="rank-math-item-visbility"
icon={ visible ? 'visibility' : 'hidden' }
onClick={ this.toggleVisibility }
label={ __( 'Hide Question', 'rank-math' ) }
showTooltip={ true }
/>
<Button
icon="trash"
className="rank-math-item-delete"
onClick={ this.deleteQuestion }
label={ __( 'Delete Question', 'rank-math' ) }
showTooltip={ true }
/>
</div>
</div>
<div className="rank-math-item-content">
<RichText
tagName="div"
className={ 'rank-math-faq-answer ' + contentCssClasses }
value={ content }
onChange={ ( newContent ) => {
this.setQuestionProp( 'content', newContent )
} }
placeholder={ __(
'Enter the answer to the question',
'rank-math'
) }
/>
<MediaUpload
allowedTypes={ [ 'image' ] }
multiple={ false }
value={ imageID }
render={ ( { open } ) => (
<MediaUploader
imageID={ imageID }
sizeSlug={ sizeSlug }
open={ open }
removeImage={ () => {
this.setQuestionProp( 'imageID', 0 )
} }
/>
) }
onSelect={ ( image ) => {
this.setQuestionProp( 'imageID', image.id )
} }
/>
</div>
</div>
)
}
/**
* Update question properties.
*
* @param {string} prop Poperty name.
* @param {string} value Property value.
*/
setQuestionProp( prop, value ) {
const { setAttributes, index } = this.props
const questions = [ ...this.props.questions ]
questions[ index ][ prop ] = value
setAttributes( { questions } )
}
/**
* Toggle question visibility.
*/
toggleVisibility = () => {
const { setAttributes, index } = this.props
const questions = [ ...this.props.questions ]
questions[ index ].visible = ! this.props.visible
setAttributes( { questions } )
}
/**
* Delete question from block.
*/
deleteQuestion = () => {
const { setAttributes, index } = this.props
const questions = [ ...this.props.questions ]
questions.splice( index, 1 )
setAttributes( { questions } )
}
}
export default Question

View File

@@ -0,0 +1,141 @@
/**
* External dependencies
*/
import { isEmpty } from 'lodash'
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n'
import { Fragment } from '@wordpress/element'
import { Button, Dashicon } from '@wordpress/components'
import { BlockControls, AlignmentToolbar, useBlockProps } from '@wordpress/block-editor'
/**
* Internal dependencies
*/
import getLink from '@helpers/getLink'
import Inspector from './components/Inspector'
import Question from './components/Question'
import generateId from '@helpers/generateId'
/**
* Render Quetion component.
*
* @param {Object} props Block attributes
*
* @return {Array} Array of question editor.
*/
const renderQuestions = ( props ) => {
const {
sizeSlug,
titleWrapper,
titleCssClasses,
contentCssClasses,
} = props.attributes
let { questions } = props.attributes
if ( isEmpty( questions ) ) {
questions = [
{
id: generateId( 'faq-question' ),
title: '',
content: '',
visible: true,
},
]
props.setAttributes( { questions } )
}
return questions.map( ( question, index ) => {
return (
<li key={ question.id }>
<Question
{ ...question }
index={ index }
key={ question.id + '-question' }
questions={ questions }
setAttributes={ props.setAttributes }
sizeSlug={ sizeSlug }
titleWrapper={ titleWrapper }
titleCssClasses={ titleCssClasses }
contentCssClasses={ contentCssClasses }
/>
</li>
)
} )
}
/**
* Add an empty Question into block.
*
* @param {Object} props Block props.
*/
const addNew = ( props ) => {
const questions = [ ...props.attributes.questions ]
questions.push( {
id: generateId( 'faq-question' ),
title: '',
content: '',
visible: true,
} )
props.setAttributes( { questions } )
}
/**
* FAQ block edit component.
*
* @param {Object} props Block props.
*/
export default ( props ) => {
const { className, isSelected } = props
const { textAlign } = props.attributes
const blockProps = useBlockProps()
return (
<div { ...blockProps }>
<div
id="rank-math-faq"
className={ 'rank-math-block ' + className }
>
{ isSelected && <Inspector { ...props } /> }
{ isSelected && (
<Fragment>
<BlockControls>
<AlignmentToolbar
value={ textAlign }
onChange={ ( nextTextAlignment ) =>
props.setAttributes( {
textAlign: nextTextAlignment,
} )
}
/>
</BlockControls>
</Fragment>
) }
<ul style={ { textAlign } }>{ renderQuestions( props ) }</ul>
<Button
variant="primary"
onClick={ () => {
addNew( props )
} }
>
{ __( 'Add New FAQ', 'rank-math' ) }
</Button>
<a
href={ getLink( 'faq-schema-block', 'Add New FAQ' ) }
rel="noopener noreferrer"
target="_blank"
title={ __( 'More Info', 'rank-math' ) }
className={ 'rank-math-block-info' }
>
<Dashicon icon="info" />
</a>
</div>
</div>
)
}

View File

@@ -0,0 +1,18 @@
/**
* FAQ eample for in block preview pane.
*
* @type {Object}
*/
export default {
attributes: {
questions: [
{
visible: true,
titleWrapper: 'div',
title: 'Question',
content:
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
],
},
}

View File

@@ -0,0 +1,25 @@
/**
* WordPress dependencies
*/
import { registerBlockType } from '@wordpress/blocks'
/**
* Internal dependencies
*/
import example from './example'
import edit from './edit'
import transforms from './transforms'
import save from './save'
/**
* Register FAQ block.
*/
registerBlockType(
'rank-math/faq-block',
{
example,
edit,
save,
transforms,
}
)

View File

@@ -0,0 +1,52 @@
/**
* External dependencies
*/
import { isEmpty } from 'lodash'
/**
* WordPress dependencies
*/
import { RichText, useBlockProps } from '@wordpress/block-editor'
/**
* Save block for display on front
*
* @param {Object} props This component's props.
*/
export default ( props ) => {
const { questions, titleWrapper } = props.attributes
if ( isEmpty( questions ) ) {
return null
}
return (
<div { ...useBlockProps.save() }>
{ questions.map( ( question, index ) => {
if (
isEmpty( question.title ) ||
isEmpty( question.content ) ||
false === question.visible
) {
return null
}
return (
<div className="rank-math-faq-item" key={ index }>
<RichText.Content
tagName={ titleWrapper }
value={ question.title }
className="rank-math-question"
/>
<RichText.Content
tagName="div"
value={ question.content }
className="rank-math-answer"
/>
</div>
)
} ) }
</div>
)
}

View File

@@ -0,0 +1,35 @@
/**
* WordPress dependencies
*/
import { createBlock } from '@wordpress/blocks'
/**
* Transform yoast faq block.
*
* @type {Array}
*/
export default {
from: [
{
type: 'block',
blocks: [ 'yoast/faq-block' ],
transform: ( yoast ) => {
const questions = yoast.questions.map( ( question ) => {
return {
title: question.jsonQuestion,
content: question.jsonAnswer,
visible: true,
}
} )
const attributes = {
titleWrapper: 'h3',
questions,
className: yoast.className,
}
return createBlock( 'rank-math/faq-block', attributes )
},
},
],
}

View File

@@ -0,0 +1,52 @@
{
"apiVersion": 3,
"title": "FAQ by Rank Math",
"description": "Easily add Schema-ready, SEO-friendly, Frequently Asked Questions to your content.",
"name": "rank-math/faq-block",
"category": "rank-math-blocks",
"icon": "editor-ul",
"textdomain": "rank-math",
"keywords": [ "FAQ", "Frequently Asked Questions", "Schema", "SEO", "Structured Data", "Yoast", "Rank Math", "Block", "Markup", "Rich Snippet" ],
"editorScript": [
"lodash",
"file:./assets/js/index.js"
],
"editorStyle": "rank-math-block-admin",
"attributes": {
"listStyle": {
"type": "string",
"default": ""
},
"titleWrapper": {
"type": "string",
"default": "h3"
},
"sizeSlug": {
"type": "string",
"default": "thumbnail"
},
"questions": {
"type": "array",
"default": [],
"items": {
"type": "object"
}
},
"listCssClasses": {
"type": "string",
"default": ""
},
"titleCssClasses": {
"type": "string",
"default": ""
},
"contentCssClasses": {
"type": "string",
"default": ""
},
"textAlign": {
"type": "string",
"default": "left"
}
}
}

View File

@@ -0,0 +1,210 @@
<?php
/**
* The Faq Block
*
* @since 1.0.233
* @package RankMath
* @subpackage RankMath\Faq
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Schema;
use WP_Block_Type_Registry;
use RankMath\Traits\Hooker;
defined( 'ABSPATH' ) || exit;
/**
* Faq Block class.
*/
class Block_FAQ extends Block {
use Hooker;
/**
* Block type name.
*
* @var string
*/
private $block_type = 'rank-math/faq-block';
/**
* The single instance of the class.
*
* @var Block_FAQ
*/
protected static $instance = null;
/**
* Retrieve main Block_FAQ instance.
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Block_FAQ
*/
public static function get() {
if ( is_null( self::$instance ) && ! ( self::$instance instanceof Block_FAQ ) ) {
self::$instance = new Block_FAQ();
}
return self::$instance;
}
/**
* The Constructor.
*/
public function __construct() {
if ( WP_Block_Type_Registry::get_instance()->is_registered( $this->block_type ) ) {
return;
}
register_block_type(
RANK_MATH_PATH . 'includes/modules/schema/blocks/faq/block.json',
[
'render_callback' => [ $this, 'render' ],
]
);
add_filter( 'rank_math/schema/block/faq-block', [ $this, 'add_graph' ], 10, 2 );
}
/**
* Add FAQ schema data in JSON-LD array.
*
* @param array $data Array of JSON-LD data.
* @param array $block JsonLD Instance.
*
* @return array
*/
public function add_graph( $data, $block ) {
// Early bail.
if ( ! $this->has_questions( $block['attrs'] ) ) {
return $data;
}
if ( ! isset( $data['faqs'] ) ) {
$data['faqs'] = [
'@type' => 'FAQPage',
'mainEntity' => [],
];
}
$permalink = get_permalink() . '#';
foreach ( $block['attrs']['questions'] as $question ) {
if ( empty( $question['title'] ) || empty( $question['content'] ) || empty( $question['visible'] ) ) {
continue;
}
$question['title'] = do_shortcode( $question['title'] );
$question['content'] = do_shortcode( $question['content'] );
if ( empty( $question['id'] ) ) {
$question['id'] = 'rm-faq-' . md5( $question['title'] );
}
$data['faqs']['mainEntity'][] = [
'@type' => 'Question',
'url' => esc_url( $permalink . $question['id'] ),
'name' => wp_strip_all_tags( $question['title'] ),
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => $this->clean_text( $question['content'] ),
],
];
}
return $data;
}
/**
* Render block content.
*
* @param array $attributes Array of atributes.
* @return string
*/
public static function markup( $attributes = [] ) {
$list_tag = self::get()->get_list_style( $attributes['listStyle'] );
$item_tag = self::get()->get_list_item_style( $attributes['listStyle'] );
$class = 'rank-math-block';
if ( ! empty( $attributes['className'] ) ) {
$class .= ' ' . esc_attr( $attributes['className'] );
}
// HTML.
$out = [];
$out[] = sprintf( '<div id="rank-math-faq" class="%1$s"%2$s>', $class, self::get()->get_styles( $attributes ) );
$out[] = sprintf( '<%1$s class="rank-math-list %2$s">', $list_tag, esc_attr( $attributes['listCssClasses'] ) );
// Questions.
foreach ( $attributes['questions'] as $question ) {
if ( empty( $question['title'] ) || empty( $question['content'] ) || empty( $question['visible'] ) ) {
continue;
}
if ( empty( $question['id'] ) ) {
$question['id'] = 'rm-faq-' . md5( $question['title'] );
}
$out[] = sprintf( '<%1$s id="%2$s" class="rank-math-list-item">', $item_tag, esc_attr( $question['id'] ) );
$out[] = sprintf(
'<%1$s class="rank-math-question %2$s">%3$s</%1$s>',
self::get()->get_title_wrapper( $attributes['titleWrapper'] ),
esc_attr( $attributes['titleCssClasses'] ),
wp_kses_post( $question['title'] )
);
$out[] = '<div class="rank-math-answer ' . esc_attr( $attributes['contentCssClasses'] ) . '">';
if ( ! empty( $question['imageUrl'] ) ) {
$out[] = '<img src="' . esc_url( $question['imageUrl'] ) . '" />';
} else {
$out[] = self::get()->get_image( $question, $attributes['sizeSlug'] );
}
$out[] = self::get()->normalize_text( $question['content'], 'faq' );
$out[] = '</div>';
$out[] = sprintf( '</%1$s>', $item_tag );
}
$out[] = sprintf( '</%1$s>', $list_tag );
$out[] = '</div>';
return apply_filters(
'rank_math/schema/block/faq/content',
wp_kses_post( join( "\n", $out ) ),
$out,
$attributes
);
}
/**
* Render block content
*
* @param array $attributes Array of atributes.
*
* @return string
*/
public function render( $attributes ) {
// Early bail.
if ( ! $this->has_questions( $attributes ) ) {
return '';
}
return self::markup( $attributes );
}
/**
* Check if FAQ block has questions data.
*
* @param array $attributes Array of attributes.
*
* @return boolean
*/
private function has_questions( $attributes ) {
return ! isset( $attributes['questions'] ) || empty( $attributes['questions'] ) ? false : true;
}
}