Hotel Raxa - Advanced Booking System Implementation

🏨 Hotel Booking Enhancements:
- Implemented Eagle Booking Advanced Pricing add-on
- Added Booking.com-style rate management system
- Created professional calendar interface for pricing
- Integrated deals and discounts functionality

💰 Advanced Pricing Features:
- Dynamic pricing models (per room, per person, per adult)
- Base rates, adult rates, and child rates management
- Length of stay discounts and early bird deals
- Mobile rates and secret deals implementation
- Seasonal promotions and flash sales

📅 Availability Management:
- Real-time availability tracking
- Stop sell and restriction controls
- Closed to arrival/departure functionality
- Minimum/maximum stay requirements
- Automatic sold-out management

💳 Payment Integration:
- Maintained Redsys payment gateway integration
- Seamless integration with existing Eagle Booking
- No modifications to core Eagle Booking plugin

🛠️ Technical Implementation:
- Custom database tables for advanced pricing
- WordPress hooks and filters integration
- AJAX-powered admin interface
- Data migration from existing Eagle Booking
- Professional calendar view for revenue management

📊 Admin Interface:
- Booking.com-style management dashboard
- Visual rate and availability calendar
- Bulk operations for date ranges
- Statistics and analytics dashboard
- Modal dialogs for quick editing

🔧 Code Quality:
- WordPress coding standards compliance
- Secure database operations with prepared statements
- Proper input validation and sanitization
- Error handling and logging
- Responsive admin interface

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Hotel Raxa Dev
2025-07-11 07:43:22 +02:00
commit 5b1e2453c7
9816 changed files with 2784509 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
<?php
/**
* Redux Multi Media Extension Class
*
* @package Redux
* @author Kevin Provance <kevin.provance@gmail.com>
* @class Redux_Extension_Multi_Media
*
* @version 4.4.1
*/
defined( 'ABSPATH' ) || exit;
// Don't duplicate me!
if ( ! class_exists( 'Redux_Extension_Multi_Media' ) ) {
/**
* Main Redux_Extension_multi_media extension class
*
* @since 1.0.0
*/
class Redux_Extension_Multi_Media extends Redux_Extension_Abstract {
/**
* Extension version.
*
* @var string
*/
public static $version = '4.4.1';
/**
* Extension name.
*
* @var string
*/
public $extension_name = 'Multi Media';
/**
* Class Constructor. Defines the args for the extensions class
*
* @since 1.0.0
* @access public
*
* @param ReduxFramework $redux Parent settings.
*
* @return void
*/
public function __construct( $redux ) {
parent::__construct( $redux, __FILE__ );
$this->add_field( 'multi_media' );
}
/**
* Get extended image data.
*
* @param int|string $id image ID.
*
* @return array
* @depreacted Remove camelCase function name.
*/
public static function getExtendedData( $id ) {
_deprecated_function( 'getExtendedData', '4.3.15', 'Redux_Extension_Multi_Media::get_extended_data( $id )' );
return self::get_extended_data( $id );
}
/**
* Get extended image data.
*
* @param int|string $id image ID.
*
* @return array|void
*/
public static function get_extended_data( $id ) {
if ( '' !== $id && is_numeric( $id ) ) {
return wp_prepare_attachment_for_js( $id );
}
}
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* Silence is golden.
*
* @package Redux Framework
*/
echo null;

View File

@@ -0,0 +1,287 @@
<?php
/**
* Redux Multi Media Field Class
*
* @package Redux
* @author Kevin Provance <kevin.provance@gmail.com>
* @class Redux_Multi_Media
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'Redux_Multi_Media' ) ) {
/**
* Main ReduxFramework_multi_media class
*
* @since 1.0.0
*/
class Redux_Multi_Media extends Redux_Field {
/**
* Field Render Function.
* Takes the vars and outputs the HTML for the field in the settings
*
* @since 1.0.0
* @access public
* @return void
*/
public function render() {
$field_id = $this->field['id'];
$button_text = $this->field['labels']['button'] ?? esc_html__( 'Add or Upload File(s)', 'redux-framework' );
$max_file_count = $this->field['max_file_upload'] ?? 0;
// Set library filter data, if it's set.
if ( ! isset( $this->field['library_filter'] ) ) {
$lib_filter = '';
} else {
if ( ! is_array( $this->field['library_filter'] ) ) {
$this->field['library_filter'] = array( $this->field['library_filter'] );
}
$mime_types = get_allowed_mime_types();
$lib_array = $this->field['library_filter'];
$json_arr = array();
// Enum mime types.
foreach ( $mime_types as $ext => $type ) {
if ( strpos( $ext, '|' ) ) {
$exp_arr = explode( '|', $ext );
foreach ( $exp_arr as $ext ) {
if ( in_array( $ext, $lib_array, true ) ) {
$json_arr[ $ext ] = $type;
}
}
} elseif ( in_array( $ext, $lib_array, true ) ) {
$json_arr[ $ext ] = $type;
}
}
// Encode for transit to JS.
$lib_filter = rawurlencode( wp_json_encode( $json_arr ) );
}
// primary container.
echo '<div
class="redux-multi-media-container' . esc_attr( $this->field['class'] ) . '"
id="' . esc_attr( $field_id ) . '"
data-max-file-upload="' . intval( $max_file_count ) . '"
data-id="' . esc_attr( $field_id ) . '">';
// Library filter.
echo '<input type="hidden" class="library-filter" data-lib-filter="' . $lib_filter . '" />'; // phpcs:ignore WordPress.Security.EscapeOutput
// Hidden inout for file(s).
echo '<input
name="' . esc_attr( $this->field['name'] . $this->field['name_suffix'] ) . '"
id="' . esc_attr( $field_id ) . '-multi-media"
class="redux_upload_file redux_upload_list"
type="hidden"
value=""
size="45" />';
// Upload button.
echo '<input
type="button"
class="redux_upload_button button redux_upload_list"
name=""
id="' . esc_attr( $field_id ) . '-multi-media-upload"
value="' . esc_attr( $button_text ) . '" />';
// list container.
echo '<ul id="' . esc_attr( $this->parent->args['opt_name'] ) . '_' . esc_attr( $field_id ) . '_status" class="redux_media_status attach_list">';
$file_arr = array();
$img_arr = array();
$all_arr = array();
// Check for file entries in array format.
if ( $this->value && is_array( $this->value ) ) {
// Enum existing file entries.
foreach ( $this->value as $id => $url ) {
// hidden ID input.
$id_input = '<input
type="hidden"
value="' . $url . '"
name="' . esc_attr( $this->field['name'] . $this->field['name_suffix'] ) . '[' . intval( $id ) . ']"
id="filelist-' . $id . '"
class="" />';
// Check for a valid image extension.
if ( $this->is_valid_img_ext( $url ) ) {
// Add image to array.
$html = '<li class="img_status">';
$html .= wp_get_attachment_image( $id, array( 50, 50 ) );
$html .= '<p class="redux_remove_wrapper">';
$html .= '<a href="#" class="redux_remove_file_button">' . esc_html__( 'Remove Image', 'redux-framework' ) . '</a>';
$html .= '</p>';
$html .= $id_input;
$html .= '</li>';
$img_arr[] = $html;
// No image? Output standard file info.
} else {
// Get parts of URL.
$parts = explode( '/', $url );
// Get the filename.
$title = '';
$part_count = count( $parts );
for ( $i = 0; $i < $part_count; ++$i ) {
$title = $parts[ $i ];
}
// Add file to array.
$html = '<li>';
$html .= esc_html__( 'File: ', 'redux-framework' );
$html .= '<strong>' . $title . '</strong>&nbsp;&nbsp;&nbsp;';
$html .= '(<a href="' . $url . '" target="_blank" rel="external">' . esc_html__( 'Download', 'redux-framework' ) . '</a> / <a href="#" class="redux_remove_file_button">' . __( 'Remove', 'redux-framework' ) . '</a>)';
$html .= $id_input;
$html .= '</li>';
$file_arr[] = $html;
}
}
}
// Push images onto array stack.
if ( ! empty( $img_arr ) ) {
foreach ( $img_arr as $html ) {
$all_arr[] = $html;
}
}
// Push files onto array stack.
if ( ! empty( $file_arr ) ) {
foreach ( $file_arr as $html ) {
$all_arr[] = $html;
}
}
// Output array to page.
if ( ! empty( $all_arr ) ) {
foreach ( $all_arr as $html ) {
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput
}
}
// Close the list.
echo '</ul>';
// Close container.
echo '</div>';
}
/**
* Determine a file's extension
*
* @param string $file File url.
*
* @return string|false File extension or false
* @since 1.0.0
*/
private function get_file_ext( string $file ) {
$parsed = wp_parse_url( $file, PHP_URL_PATH );
return $parsed ? strtolower( pathinfo( $parsed, PATHINFO_EXTENSION ) ) : false;
}
/**
* Determines if a file has a valid image extension
*
* @param string $file File url.
*
* @return bool Whether the file has a valid image extension
* @since 1.0.0
*/
private function is_valid_img_ext( string $file ): bool {
$file_ext = $this->get_file_ext( $file );
$ext_arr = array( 'jpg', 'jpeg', 'png', 'gif', 'ico', 'icon' );
$valid = empty( $valid ) ? (array) apply_filters( 'redux_valid_img_types', $ext_arr ) : $valid;
return ( $file_ext && in_array( $file_ext, $valid, true ) );
}
/**
* Enqueue Function.
* If this field requires any scripts, or css define this function and register/enqueue the scripts/css
*
* @since 1.0.0
* @access public
* @return void
*/
public function enqueue() {
// Get labels for localization.
$upload_file = $this->field['labels']['upload_file'] ?? esc_html__( 'Select File(s)', 'redux-framework' );
$remove_image = $this->field['labels']['remove_image'] ?? esc_html__( 'Remove Image', 'redux-framework' );
$remove_file = $this->field['labels']['remove_file'] ?? esc_html__( 'Remove', 'redux-framework' );
$file_label = $this->field['labels']['file'] ?? esc_html__( 'File: ', 'redux-framework' );
$download_label = $this->field['labels']['download'] ?? esc_html__( 'Download', 'redux-framework' );
$media_title = $this->field['labels']['title'] ?? 'Title';
// translators: %s: Filename.
$dup_warn = $this->field['labels']['duplicate'] ?? esc_html__( '%s already exists in your file queue.', 'redux-framework' );
// translators: %s: Upload limit.
$max_warn = $this->field['labels']['max_limit'] ?? esc_html__( 'Maximum upload limit of %s reached/exceeded.', 'redux-framework' );
// Set up min files for dev_mode = false.
$min = Redux_Functions::isMin();
if ( function_exists( 'wp_enqueue_media' ) ) {
wp_enqueue_media();
} else {
wp_enqueue_script( 'media-upload' );
}
// Field dependent JS.
wp_enqueue_script(
'redux-field-multi-media',
$this->url . 'redux-multi-media' . $min . '.js',
array( 'jquery', 'redux-js' ),
Redux_Extension_Multi_Media::$version,
true
);
if ( $this->parent->args['dev_mode'] ) {
wp_enqueue_style(
'redux-field-multi-media',
$this->url . 'redux-multi-media.css',
array(),
Redux_Extension_Multi_Media::$version
);
}
// Localization.
$data_arr = array(
'upload_file' => $upload_file,
'remove_image' => $remove_image,
'remove_file' => $remove_file,
'file' => $file_label,
'download' => $download_label,
'title' => $media_title,
'dup_warn' => $dup_warn,
'max_warn' => $max_warn,
);
wp_localize_script(
'redux-field-multi-media',
'redux_multi_media_l10',
apply_filters( 'redux_multi_media_localized_data', $data_arr )
);
}
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* Silence is golden.
*
* @package Redux Framework
*/
echo null;

View File

@@ -0,0 +1,23 @@
.redux-container-multi_media .redux_media_status { margin: 10px 0 0 0; }
.redux-container-multi_media .redux_media_status .redux-file-exists, .redux-container-multi_media .redux_media_status .redux-max-limit { color: red; }
.redux-container-multi_media .redux_media_status .img_status img { width: 50px !important; height: 50px !important; }
.redux-container-multi_media .redux_media_status .img_status img, .redux-container-multi_media .redux_media_status .embed_status { border: 1px solid #DFDFDF; background: #FAFAFA; max-width: 350px; padding: 5px; border-radius: 2px; }
.redux-container-multi_media .redux_media_status .img_status { clear: none; float: left; display: inline-block; margin-right: 10px; width: auto; }
.redux-container-multi_media .redux_media_status .embed_status { float: left; max-width: 800px; }
.redux-container-multi_media .redux_media_status .img_status, .redux-container-multi_media .redux_media_status .embed_status { position: relative; }
.redux-container-multi_media .redux_media_status .img_status .redux_remove_file_button, .redux-container-multi_media .redux_media_status .embed_status .redux_remove_file_button { text-indent: -9999px; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==); width: 16px; height: 16px; position: absolute; top: -5px; left: -5px; }
.redux-container-multi_media .attach_list li { clear: both; display: inline-block; margin-bottom: 25px; width: 100%; }
.redux-container-multi_media .attach_list li img { float: left; margin-right: 10px; }
/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkdXgtbXVsdGktbWVkaWEuY3NzIiwic291cmNlcyI6WyJyZWR1eC1tdWx0aS1tZWRpYS5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLEFBQ0ksNEJBRHdCLENBQ3hCLG1CQUFtQixDQUFDLEVBQ2hCLE1BQU0sRUFBRSxVQUFVLEdBZ0RyQjs7QUFsREwsQUFJUSw0QkFKb0IsQ0FDeEIsbUJBQW1CLENBR2Ysa0JBQWtCLEVBSjFCLDRCQUE0QixDQUN4QixtQkFBbUIsQ0FJZixnQkFBZ0IsQ0FBQyxFQUNiLEtBQUssRUFBQyxHQUFHLEdBQ1o7O0FBUFQsQUFTUSw0QkFUb0IsQ0FDeEIsbUJBQW1CLENBUWYsV0FBVyxDQUFDLEdBQUcsQ0FBQyxFQUNaLEtBQUssRUFBRSxJQUFJLENBQUEsVUFBVSxFQUNyQixNQUFNLEVBQUUsSUFBSSxDQUFBLFVBQVUsR0FDekI7O0FBWlQsQUFjUSw0QkFkb0IsQ0FDeEIsbUJBQW1CLENBYWYsV0FBVyxDQUFDLEdBQUcsRUFkdkIsNEJBQTRCLENBQ3hCLG1CQUFtQixDQWFFLGFBQWEsQ0FBQyxFQUMzQixNQUFNLEVBQUMsaUJBQWlCLEVBQ3hCLFVBQVUsRUFBRSxPQUFPLEVBQ25CLFNBQVMsRUFBQyxLQUFLLEVBQ2YsT0FBTyxFQUFFLEdBQUcsRUFDWixrQkFBa0IsRUFBRSxHQUFHLEVBQ3ZCLGFBQWEsRUFBRSxHQUFHLEdBQ3JCOztBQXJCVCxBQXVCUSw0QkF2Qm9CLENBQ3hCLG1CQUFtQixDQXNCZixXQUFXLENBQUMsRUFDUixLQUFLLEVBQUUsSUFBSSxFQUNYLEtBQUssRUFBRSxJQUFJLEVBQ1gsT0FBTyxFQUFFLFlBQVksRUFDckIsWUFBWSxFQUFFLElBQUksRUFDbEIsS0FBSyxFQUFFLElBQUksR0FDZDs7QUE3QlQsQUErQlEsNEJBL0JvQixDQUN4QixtQkFBbUIsQ0E4QmYsYUFBYSxDQUFDLEVBQ1YsS0FBSyxFQUFFLElBQUksRUFDWCxTQUFTLEVBQUMsS0FBSyxHQUNsQjs7QUFsQ1QsQUFvQ1EsNEJBcENvQixDQUN4QixtQkFBbUIsQ0FtQ2YsV0FBVyxFQXBDbkIsNEJBQTRCLENBQ3hCLG1CQUFtQixDQW9DZixhQUFhLENBQUMsRUFDVixRQUFRLEVBQUUsUUFBUSxHQVdyQjs7QUFqRFQsQUF3Q1ksNEJBeENnQixDQUN4QixtQkFBbUIsQ0FtQ2YsV0FBVyxDQUlQLHlCQUF5QixFQXhDckMsNEJBQTRCLENBQ3hCLG1CQUFtQixDQW9DZixhQUFhLENBR1QseUJBQXlCLENBQUMsRUFDdEIsV0FBVyxFQUFFLE9BQU8sRUFDcEIsVUFBVSxFQUFFLHU5QkFBdTlCLEVBQ24rQixLQUFLLEVBQUUsSUFBSSxFQUNYLE1BQU0sRUFBRSxJQUFJLEVBQ1osUUFBUSxFQUFFLFFBQVEsRUFDbEIsR0FBRyxFQUFFLElBQUksRUFDVCxJQUFJLEVBQUUsSUFBSSxHQUNiOztBQWhEYixBQW9ESSw0QkFwRHdCLENBb0R4QixZQUFZLENBQUMsRUFBRSxDQUFDLEVBQ1osS0FBSyxFQUFFLElBQUksRUFDWCxPQUFPLEVBQUUsWUFBWSxFQUNyQixhQUFhLEVBQUUsSUFBSSxFQUNuQixLQUFLLEVBQUUsSUFBSSxHQUNkOztBQXpETCxBQTJESSw0QkEzRHdCLENBMkR4QixZQUFZLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUNoQixLQUFLLEVBQUUsSUFBSSxFQUNYLFlBQVksRUFBRSxJQUFJLEdBQ3JCIn0= */
/*# sourceMappingURL=redux-multi-media.css.map */

View File

@@ -0,0 +1,412 @@
/* global redux, redux_multi_media_l10, wp, redux_change */
/**
* Multi Media Selector library
*
* @author Kevin Provance (kprovance)
*/
( function( $ ) {
'use strict';
var l10n;
redux.field_objects = redux.field_objects || {};
redux.field_objects.multi_media = redux.field_objects.multi_media || {};
/*******************************************************************************
* Function: init
*
* Runs when the library is loaded.
******************************************************************************/
redux.field_objects.multi_media.init = function( selector ) {
// If no selector is passed, grab one from the HTML.
if ( ! selector ) {
selector = $( document ).find( '.redux-group-tab:visible' ).find( '.redux-container-multi_media:visible' );
}
// Enum instances of our object.
$( selector ).each(
function() {
var el = $( this );
var parent = el;
if ( ! el.hasClass( 'redux-field-container' ) ) {
parent = el.parents( '.redux-field-container:first' );
}
if ( parent.is( ':hidden' ) ) {
return;
}
if ( parent.hasClass( 'redux-field-init' ) ) {
parent.removeClass( 'redux-field-init' );
} else {
return;
}
// Handle clicking of the delete button.
redux.field_objects.multi_media.bindDelete( el );
// Handle clicking of the upload icon.
el.find( '.redux_upload_button' ).off().on(
'click',
function( event ) {
redux.field_objects.multi_media.addFile( event, $( this ).parents( 'fieldset.redux-field:first' ), $( this ) );
}
);
// Init module level code.
redux.field_objects.multi_media.modInit( el );
}
);
};
/*******************************************************************************
* Function: bindDelete
*
* Force DOM to recognize new delete button instances.
******************************************************************************/
redux.field_objects.multi_media.bindDelete = function( el ) {
el.find( '.redux_remove_file_button' ).off( 'click' ).on(
'click',
function( event ) {
redux.field_objects.multi_media.removeFile( event, $( this ).parents( 'fieldset.redux-field:first' ), $( this ) );
}
);
};
/*******************************************************************************
* Function: modInit
*
* Module level init
******************************************************************************/
redux.field_objects.multi_media.modInit = function() {
// Localization variable.
l10n = redux_multi_media_l10;
};
/*******************************************************************************
* Function: removeErrMsgs
*
* Removes all error messages after clicking upload
* button.
******************************************************************************/
// Removes error message(s) when clicking the Upload button.
redux.field_objects.multi_media.removeErrMsgs = function( mainID ) {
// Enumerate and remove existing 'file exists' messages.
$( '#' + mainID + ' .attach_list li.redux-file-exists' ).each(
function( idx, li ) {
idx = null;
$( li ).remove();
}
);
// Enumerate and remove existing 'max upload' messages.
$( '#' + mainID + ' .attach_list li.redux-max-limit' ).each(
function( idx, li ) {
idx = null;
$( li ).remove();
}
);
};
// Checks for duplicate after file selection.
redux.field_objects.multi_media.selExists = function( mainID, item ) {
var len;
var val = false;
// Enumerate existing files.
$( '#' + mainID + ' .attach_list li' ).each(
function( idx, li ) {
idx = null;
// Check for duplicate based on ID.
len = $( li ).find( 'input#filelist-' + item );
// If it exists, exit .each.
if ( 0 !== len.length ) {
val = true;
return false;
}
}
);
// Return value.
return val;
};
/*******************************************************************************
* Function: addFile
*
* Runs when upload button is clicked.
******************************************************************************/
redux.field_objects.multi_media.addFile = function( event, selector, self ) {
// Variables.
var frame;
var libFilter;
var filter;
var maxFileUpload;
var isList = true;
var uploadStatus = true;
// Get input ID.
var inputID = self.prev( 'input' ).attr( 'id' );
// Make form field ID.
var $formfield = $( '#' + inputID );
// Get form name.
var formName = $formfield.attr( 'name' );
var mainID = selector.attr( 'data-id' );
// Prevent default action.
event.preventDefault();
// If the media frame already exists, reopen it.
if ( frame ) {
frame.open();
return;
}
// Remove existing error messages.
redux.field_objects.multi_media.removeErrMsgs( mainID );
// Get library filter data.
filter = $( selector ).find( '.library-filter' ).data( 'lib-filter' );
// Get max file upload number.
maxFileUpload = $( selector ).find( '.redux-multi-media-container' ).data( 'max-file-upload' );
// Library filter MUST exist to do decoding.
if ( undefined !== filter ) {
if ( '' !== filter ) {
libFilter = [];
filter = decodeURIComponent( filter );
filter = JSON.parse( filter );
// Enum file extensions.
$.each(
filter,
function( index, value ) {
index = null;
libFilter.push( value );
}
);
}
}
// Create the media frame.
frame = wp.media(
{
multiple: isList,
title: l10n.title,
library: {
type: libFilter
},
button: {
text: l10n.upload_file
}
}
);
// When an image is selected, run a callback.
frame.on(
'select',
function() {
var addCount = 0;
var doChange;
// Set up our fileGroup array.
var fileGroup = [];
var fileArr = [];
var imgArr = [];
var msgArr = [];
// Grab the selected attachment.
var selection = frame.state().get( 'selection' );
// Get all of our selected files.
var attachment = selection.toJSON();
// Get existing file count.
var childCount = $( '#' + mainID + ' .attach_list' ).children().length;
$formfield.val( attachment.url );
$( '#' + inputID + '_id' ).val( attachment.id );
// Enum through each attachment.
$( attachment ).each(
function() {
var dupMsg;
var maxMsg;
// Respect max upload limit.
if ( maxFileUpload <= 0 || ( addCount + childCount ) < maxFileUpload ) {
// Check for duplicates and format duplicate message.
if ( redux.field_objects.multi_media.selExists( mainID, this.id ) ) {
dupMsg = l10n.dup_warn;
dupMsg = dupMsg.replace( '%s', '<strong>' + this.filename + '</strong>' );
uploadStatus = '<li class="redux-file-exists">' + dupMsg + '</li>';
msgArr.push( uploadStatus );
// If only file, then don't ask to save changes.
doChange = false;
// Continue equivalent.
return true;
}
// Handle images.
if ( this.type && 'image' === this.type ) {
// Image preview.
/* jscs:disable maximumLineLength */
uploadStatus = '<li class="img_status"><img width="50" height="50" src="' + this.url + '" class="attachment-50x50" alt="' + this.filename + '"><p><a href="#" class="redux_remove_file_button" rel="' + inputID + '[' + this.id + ']">' + l10n.remove_image + '</a></p><input type="hidden" id="filelist-' + this.id + '" name="' + formName + '[' + this.id + ']" value="' + this.url + '"></li>';
// Add our file to our fileGroup array.
imgArr.push( uploadStatus );
// Set change flag.
doChange = true;
// Handle everything else.
} else {
// Standard generic output if it's not an image.
uploadStatus = '<li>' + l10n.file + ' <strong>' + this.filename + '</strong>&nbsp;&nbsp;&nbsp; (<a href="' + this.url + '" target="_blank" rel="external">' + l10n.download + '</a> / <a href="#" class="redux_remove_file_button" rel="' + inputID + '[' + this.id + ']">' + l10n.remove_file + '</a>)<input type="hidden" id="filelist-' + this.id + '" name="' + formName + '[' + this.id + ']" value="' + this.url + '"></li>';
fileArr.push( uploadStatus );
}
// Increment count of added files.
addCount++; // += 1;
// If max file upload reached, generate error message.
} else {
maxMsg = l10n.max_warn;
maxMsg = maxMsg.replace( '%s', '<strong>' + maxFileUpload + '</strong>' );
uploadStatus = '<li class="redux-max-limit">' + maxMsg + '</li>';
msgArr.push( uploadStatus );
// Bail out of .each for good!
return false;
}
}
);
// Push images files onto end of stack.
if ( ! $.isEmptyObject( imgArr ) ) {
$( imgArr ).each(
function( idx, val ) {
idx = null;
fileGroup.push( val );
doChange = true;
}
);
}
// Push none image files onto end of stack.
if ( ! $.isEmptyObject( fileArr ) ) {
$( fileArr ).each(
function( idx, val ) {
idx = null;
fileGroup.push( val );
doChange = true;
}
);
}
// Push errors onto end of stack.
if ( ! $.isEmptyObject( msgArr ) ) {
$( msgArr ).each(
function( idx, val ) {
idx = null;
fileGroup.push( val );
}
);
}
// Append each item from our fileGroup array to .redux_media_status.
$( fileGroup ).each(
function() {
$formfield.siblings( '.redux_media_status' ).slideDown().append( this );
}
);
// Close media frame.
frame.close();
// Prompt for save changes, if necessary.
if ( true === doChange ) {
redux.field_objects.multi_media.bindDelete( selector );
redux_change( $( selector ).find( '.redux_media_status' ) );
}
}
);
// Finally, open the modal.
frame.open();
};
/*******************************************************************************
* Function: removeFile Function
*
* Runs when the delete icon or remove link is clicked.
******************************************************************************/
redux.field_objects.multi_media.removeFile = function( event, selector, self ) {
var inputID;
var $container;
var $self = self;
// Prevent default action.
event.preventDefault();
// If delete icon is clicked.
if ( $self.is( '.attach_list .redux_remove_file_button' ) ) {
// Remove image from page.
$self.parents( 'li' ).remove();
// Prompt for save changes.
redux_change( $( selector ).find( '.redux_media_status' ) );
// Bail out.
return false;
}
// Remove file link from page.
inputID = $self.attr( 'rel' );
$container = $self.parents( '.img_status' );
selector.find( 'input#' + inputID ).val( '' );
selector.find( 'input#' + inputID + '_id' ).val( '' );
if ( ! $container.length ) {
$self.parents( '.redux_media_status' ).html( '' );
} else {
$container.html( '' );
}
return false;
};
})( jQuery );

View File

@@ -0,0 +1 @@
.redux-container-multi_media .redux_media_status{margin:10px 0 0 0}.redux-container-multi_media .redux_media_status .redux-file-exists,.redux-container-multi_media .redux_media_status .redux-max-limit{color:red}.redux-container-multi_media .redux_media_status .img_status img{width:50px !important;height:50px !important}.redux-container-multi_media .redux_media_status .img_status img,.redux-container-multi_media .redux_media_status .embed_status{border:1px solid #dfdfdf;background:#fafafa;max-width:350px;padding:5px;border-radius:2px}.redux-container-multi_media .redux_media_status .img_status{clear:none;float:left;display:inline-block;margin-right:10px;width:auto}.redux-container-multi_media .redux_media_status .embed_status{float:left;max-width:800px}.redux-container-multi_media .redux_media_status .img_status,.redux-container-multi_media .redux_media_status .embed_status{position:relative}.redux-container-multi_media .redux_media_status .img_status .redux_remove_file_button,.redux-container-multi_media .redux_media_status .embed_status .redux_remove_file_button{text-indent:-9999px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==);width:16px;height:16px;position:absolute;top:-5px;left:-5px}.redux-container-multi_media .attach_list li{clear:both;display:inline-block;margin-bottom:25px;width:100%}.redux-container-multi_media .attach_list li img{float:left;margin-right:10px}

View File

@@ -0,0 +1 @@
!function(x){"use strict";var p;redux.field_objects=redux.field_objects||{},redux.field_objects.multi_media=redux.field_objects.multi_media||{},redux.field_objects.multi_media.init=function(e){e=e||x(document).find(".redux-group-tab:visible").find(".redux-container-multi_media:visible"),x(e).each(function(){var e=x(this),i=e;(i=e.hasClass("redux-field-container")?i:e.parents(".redux-field-container:first")).is(":hidden")||i.hasClass("redux-field-init")&&(i.removeClass("redux-field-init"),redux.field_objects.multi_media.bindDelete(e),e.find(".redux_upload_button").off().on("click",function(e){redux.field_objects.multi_media.addFile(e,x(this).parents("fieldset.redux-field:first"),x(this))}),redux.field_objects.multi_media.modInit(e))})},redux.field_objects.multi_media.bindDelete=function(e){e.find(".redux_remove_file_button").off("click").on("click",function(e){redux.field_objects.multi_media.removeFile(e,x(this).parents("fieldset.redux-field:first"),x(this))})},redux.field_objects.multi_media.modInit=function(){p=redux_multi_media_l10},redux.field_objects.multi_media.removeErrMsgs=function(e){x("#"+e+" .attach_list li.redux-file-exists").each(function(e,i){x(i).remove()}),x("#"+e+" .attach_list li.redux-max-limit").each(function(e,i){x(i).remove()})},redux.field_objects.multi_media.selExists=function(e,t){var l=!1;return x("#"+e+" .attach_list li").each(function(e,i){if(0!==x(i).find("input#filelist-"+t).length)return!(l=!0)}),l},redux.field_objects.multi_media.addFile=function(e,r,i){var u,t,o,f=!0,c=i.prev("input").attr("id"),m=x("#"+c),_=m.attr("name"),h=r.attr("data-id");e.preventDefault(),redux.field_objects.multi_media.removeErrMsgs(h),i=x(r).find(".library-filter").data("lib-filter"),o=x(r).find(".redux-multi-media-container").data("max-file-upload"),void 0!==i&&""!==i&&(t=[],i=decodeURIComponent(i),i=JSON.parse(i),x.each(i,function(e,i){t.push(i)})),(u=wp.media({multiple:!0,title:p.title,library:{type:t},button:{text:p.upload_file}})).on("select",function(){var t,i=0,l=[],d=[],s=[],a=[],e=u.state().get("selection").toJSON(),n=x("#"+h+" .attach_list").children().length;m.val(e.url),x("#"+c+"_id").val(e.id),x(e).each(function(){var e;return o<=0||i+n<o?redux.field_objects.multi_media.selExists(h,this.id)?(e=(e=p.dup_warn).replace("%s","<strong>"+this.filename+"</strong>"),f='<li class="redux-file-exists">'+e+"</li>",a.push(f),!(t=!1)):(this.type&&"image"===this.type?(f='<li class="img_status"><img width="50" height="50" src="'+this.url+'" class="attachment-50x50" alt="'+this.filename+'"><p><a href="#" class="redux_remove_file_button" rel="'+c+"["+this.id+']">'+p.remove_image+'</a></p><input type="hidden" id="filelist-'+this.id+'" name="'+_+"["+this.id+']" value="'+this.url+'"></li>',s.push(f),t=!0):(f="<li>"+p.file+" <strong>"+this.filename+'</strong>&nbsp;&nbsp;&nbsp; (<a href="'+this.url+'" target="_blank" rel="external">'+p.download+'</a> / <a href="#" class="redux_remove_file_button" rel="'+c+"["+this.id+']">'+p.remove_file+'</a>)<input type="hidden" id="filelist-'+this.id+'" name="'+_+"["+this.id+']" value="'+this.url+'"></li>',d.push(f)),void i++):(e=(e=p.max_warn).replace("%s","<strong>"+o+"</strong>"),f='<li class="redux-max-limit">'+e+"</li>",a.push(f),!1)}),x.isEmptyObject(s)||x(s).each(function(e,i){l.push(i),t=!0}),x.isEmptyObject(d)||x(d).each(function(e,i){l.push(i),t=!0}),x.isEmptyObject(a)||x(a).each(function(e,i){l.push(i)}),x(l).each(function(){m.siblings(".redux_media_status").slideDown().append(this)}),u.close(),!0===t&&(redux.field_objects.multi_media.bindDelete(r),redux_change(x(r).find(".redux_media_status")))}),u.open()},redux.field_objects.multi_media.removeFile=function(e,i,t){var l;return e.preventDefault(),t.is(".attach_list .redux_remove_file_button")?(t.parents("li").remove(),redux_change(x(i).find(".redux_media_status"))):(e=t.attr("rel"),l=t.parents(".img_status"),i.find("input#"+e).val(""),i.find("input#"+e+"_id").val(""),(l.length?l:t.parents(".redux_media_status")).html("")),!1}}(jQuery);

View File

@@ -0,0 +1,64 @@
.redux-container-multi_media {
.redux_media_status {
margin: 10px 0 0 0;
.redux-file-exists,
.redux-max-limit {
color:red;
}
.img_status img {
width: 50px!important;
height: 50px!important;
}
.img_status img, .embed_status {
border:1px solid #DFDFDF;
background: #FAFAFA;
max-width:350px;
padding: 5px;
-moz-border-radius: 2px;
border-radius: 2px;
}
.img_status {
clear: none;
float: left;
display: inline-block;
margin-right: 10px;
width: auto;
}
.embed_status {
float: left;
max-width:800px;
}
.img_status,
.embed_status {
position: relative;
.redux_remove_file_button {
text-indent: -9999px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==);
width: 16px;
height: 16px;
position: absolute;
top: -5px;
left: -5px;
}
}
}
.attach_list li {
clear: both;
display: inline-block;
margin-bottom: 25px;
width: 100%;
}
.attach_list li img {
float: left;
margin-right: 10px;
}
}