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,50 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\VideoReporting;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
class Main {
public static function init() {
static::includes();
static::hooks();
Video::register_post_type();
}
public static function includes() {
require_once __DIR__ . '/class-rest-api.php';
require_once __DIR__ . '/trait-has-post-type.php';
require_once __DIR__ . '/class-video.php';
}
public static function hooks() {
add_action( 'tcb_ajax_save_post', [ __CLASS__, 'save_video_data' ] );
add_action( 'rest_api_init', [ Rest_API::class, 'register_routes' ] );
}
public static function save_video_data() {
if ( ! empty( $_REQUEST['video_reporting_data'][0] ) ) {
/* at some point when we will be saving more video data batches at the same time, we can iterate on this array instead */
$video_data = $_REQUEST['video_reporting_data'][0];
if ( ! empty( $video_data['url'] ) ) {
/* the URL is used to identify videos that were already added */
$existing_post_id = Video::get_post_id_by_video_url( $video_data['url'] );
if ( empty( $existing_post_id ) ) {
Video::insert_post( $video_data );
} else {
Video::get_instance_with_id( $existing_post_id )->update_post( $video_data );
}
}
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\VideoReporting;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
class Rest_API {
const REST_NAMESPACE = 'tcb/v1';
const REST_ROUTE = 'video-reporting/';
public static function register_routes() {
$int_parameter_validator = [
'type' => 'int',
'required' => true,
'validate_callback' => static function ( $param ) {
return is_numeric( $param );
},
];
register_rest_route( static::REST_NAMESPACE, static::REST_ROUTE . 'video_started', [
[
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [ __CLASS__, 'video_start' ],
'permission_callback' => '__return_true',
'args' => [
'video_url' => [
'type' => 'string',
'required' => true,
],
'user_id' => $int_parameter_validator,
'post_id' => $int_parameter_validator,
],
],
] );
register_rest_route( static::REST_NAMESPACE, static::REST_ROUTE . 'save_range', [
[
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [ __CLASS__, 'save_range' ],
'permission_callback' => '__return_true',
'args' => [
'video_url' => [
'type' => 'string',
'required' => true,
],
'user_id' => $int_parameter_validator,
'post_id' => $int_parameter_validator,
'range_start' => $int_parameter_validator,
'range_end' => $int_parameter_validator,
],
],
] );
}
public static function video_start( $request ) {
$video_id = Video::get_post_id_by_video_url( $request->get_param( 'video_url' ) );
if ( empty( $video_id ) ) {
return new \WP_REST_Response( 'Error on triggering the video start', 500 );
}
Video::get_instance_with_id( $video_id )->on_video_start( $request->get_param( 'user_id' ), $request->get_param( 'post_id' ) );
return new \WP_REST_Response( $video_id );
}
/**
* @return \WP_REST_Response
*/
public static function save_range( $request ) {
$video_id = Video::get_post_id_by_video_url( $request->get_param( 'video_url' ) );
if ( empty( $video_id ) ) {
return new \WP_REST_Response( 'Error on saving the new range', 500 );
}
Video::get_instance_with_id( $video_id )
->save_range(
$request->get_param( 'user_id' ),
$request->get_param( 'post_id' ),
(int) $request->get_param( 'range_start' ),
(int) $request->get_param( 'range_end' )
);
/**
* Send data to the reporting module
* Allow people to hook into this action and send the data back to the reporting module
*/
return new \WP_REST_Response( apply_filters( 'thrive_video_save_range_response', [ 'video_id' => $video_id ], $request ) );
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\VideoReporting;
use TCB\Traits\Is_Singleton;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
class Video {
use Is_Singleton;
use Has_Post_Type;
/**
* @var int
*/
private $ID;
/**
* @param $id
*/
public function __construct( $id = null ) {
$this->ID = $id;
}
public function on_video_start( $user_id, $post_id ) {
do_action( 'thrive_video_start', [
'item_id' => $this->ID,
'user_id' => $user_id,
'post_id' => $post_id,
] );
}
public function save_range( $user_id, $post_id, $range_start, $range_end ) {
do_action( 'thrive_video_update_watch_data', [
'item_id' => $this->ID,
'user_id' => $user_id,
'post_id' => $post_id,
'range_start' => $range_start,
'range_end' => $range_end,
] );
}
public function is_completed( $current_duration ) {
$percentage_to_complete = $this->get_percentage_to_complete();
if ( ! $percentage_to_complete ) {
$percentage_to_complete = 100;
}
$duration_to_complete = (int) $this->get_full_duration() * (int) $percentage_to_complete / 100;
// If current duration is less than 5 seconds of the duration_to_complete, consider it as completed.
return $current_duration >= $duration_to_complete - 5;
}
}

View File

@@ -0,0 +1,118 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
namespace TCB\VideoReporting;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
trait Has_Post_Type {
/**
* @var string[]
*/
public static $meta_key_list = [ 'url', 'title', 'provider', 'percentage_to_complete', 'duration' ];
/**
* @return string
*/
public static function get_post_type_name() {
return 'tve_video_data';
}
/**
* @return string|void
*/
public static function get_post_title() {
return __( 'Video reporting', 'thrive-cb' );
}
public static function register_post_type() {
register_post_type( static::get_post_type_name(), [
'public' => false,
'publicly_queryable' => is_user_logged_in(),
'query_var' => false,
'exclude_from_search' => true,
'rewrite' => false,
'_edit_link' => 'post.php?post=%d',
'map_meta_cap' => true,
'label' => static::get_post_title(),
'capabilities' => [
'edit_others_posts' => 'tve-edit-cpt',
'edit_published_posts' => 'tve-edit-cpt',
],
'show_in_nav_menus' => false,
'show_in_menu' => false,
'show_in_rest' => true,
'has_archive' => false,
] );
}
/**
* @param $video_url
*
* @return int[]|\WP_Post
*/
public static function get_post_id_by_video_url( $video_url ) {
if ( empty( $video_url ) ) {
return null;
}
$videos = get_posts( [
'posts_per_page' => - 1,
'post_type' => static::get_post_type_name(),
'meta_key' => 'url',
'meta_value' => $video_url,
'fields' => 'ids',
] );
return empty( $videos ) ? null : $videos[0];
}
/**
* @param array $data
*
* @return int|\WP_Error
*/
public static function insert_post( $data ) {
$meta_input = [];
foreach ( static::$meta_key_list as $meta_key ) {
$meta_input[ $meta_key ] = $data[ $meta_key ];
}
return wp_insert_post( [
'post_title' => static::get_post_title(),
'post_type' => static::get_post_type_name(),
'post_status' => 'publish',
'meta_input' => $meta_input,
] );
}
/**
* @param $data
*/
public function update_post( $data ) {
foreach ( static::$meta_key_list as $meta_key ) {
if ( isset( $data[ $meta_key ] ) ) {
update_post_meta( (int) $this->ID, $meta_key, $data[ $meta_key ] );
}
}
}
public function get_title() {
return get_post_meta( (int) $this->ID, 'title', true );
}
public function get_full_duration() {
return get_post_meta( (int) $this->ID, 'duration', true );
}
public function get_percentage_to_complete() {
return get_post_meta( (int) $this->ID, 'percentage_to_complete', true );
}
}