ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-kirki-controls-repeater-control.php (19659B)


      1 <?php
      2 /**
      3  * Customizer Control: repeater.
      4  *
      5  * @package     Kirki
      6  * @subpackage  Controls
      7  * @copyright   Copyright (c) 2016, Aristeides Stathopoulos
      8  * @license     http://opensource.org/licenses/https://opensource.org/licenses/MIT
      9  * @since       2.0
     10  */
     11 
     12 // Exit if accessed directly.
     13 if ( ! defined( 'ABSPATH' ) ) {
     14 	exit;
     15 }
     16 
     17 if ( ! class_exists( 'Kirki_Controls_Repeater_Control' ) ) {
     18 
     19 	/**
     20 	 * Repeater control
     21 	 */
     22 	class Kirki_Controls_Repeater_Control extends Kirki_Customize_Control {
     23 
     24 		/**
     25 		 * The control type.
     26 		 *
     27 		 * @access public
     28 		 * @var string
     29 		 */
     30 		public $type = 'repeater';
     31 
     32 		/**
     33 		 * The fields that each container row will contain.
     34 		 *
     35 		 * @access public
     36 		 * @var array
     37 		 */
     38 		public $fields = array();
     39 
     40 		/**
     41 		 * Will store a filtered version of value for advenced fields (like images).
     42 		 *
     43 		 * @access protected
     44 		 * @var array
     45 		 */
     46 		protected $filtered_value = array();
     47 
     48 		/**
     49 		 * The row label
     50 		 *
     51 		 * @access public
     52 		 * @var array
     53 		 */
     54 		public $row_label = array();
     55 
     56 		/**
     57 		 * Constructor.
     58 		 * Supplied `$args` override class property defaults.
     59 		 * If `$args['settings']` is not defined, use the $id as the setting ID.
     60 		 *
     61 		 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
     62 		 * @param string               $id      Control ID.
     63 		 * @param array                $args    {@see WP_Customize_Control::__construct}.
     64 		 */
     65 		public function __construct( $manager, $id, $args = array() ) {
     66 
     67 			parent::__construct( $manager, $id, $args );
     68 
     69 			// Set up defaults for row labels.
     70 			$this->row_label = array(
     71 				'type' => 'text',
     72 				'value' => $this->l10n['row'],
     73 				'field' => false,
     74 			);
     75 
     76 			// Validating args for row labels.
     77 			if ( isset( $args['row_label'] ) && is_array( $args['row_label'] ) && ! empty( $args['row_label'] ) ) {
     78 
     79 				// Validating row label type.
     80 				if ( isset( $args['row_label']['type'] ) && ( 'text' === $args['row_label']['type'] || 'field' === $args['row_label']['type'] ) ) {
     81 					$this->row_label['type'] = $args['row_label']['type'];
     82 				}
     83 
     84 				// Validating row label type.
     85 				if ( isset( $args['row_label']['value'] ) && ! empty( $args['row_label']['value'] ) ) {
     86 					$this->row_label['value'] = esc_attr( $args['row_label']['value'] );
     87 				}
     88 
     89 				// Validating row label field.
     90 				if ( isset( $args['row_label']['field'] ) && ! empty( $args['row_label']['field'] ) && isset( $args['fields'][ esc_attr( $args['row_label']['field'] ) ] ) ) {
     91 					$this->row_label['field'] = esc_attr( $args['row_label']['field'] );
     92 				} else {
     93 
     94 					// If from field is not set correctly, making sure standard is set as the type.
     95 					$this->row_label['type'] = 'text';
     96 				}
     97 			}
     98 
     99 			if ( empty( $this->button_label ) ) {
    100 				$this->button_label = $this->l10n['add-new'] . ' ' . $this->row_label['value'];
    101 			}
    102 
    103 			if ( empty( $args['fields'] ) || ! is_array( $args['fields'] ) ) {
    104 				$args['fields'] = array();
    105 			}
    106 
    107 			// An array to store keys of fields that need to be filtered.
    108 			$media_fields_to_filter = array();
    109 
    110 			foreach ( $args['fields'] as $key => $value ) {
    111 				if ( ! isset( $value['default'] ) ) {
    112 					$args['fields'][ $key ]['default'] = '';
    113 				}
    114 
    115 				if ( ! isset( $value['label'] ) ) {
    116 					$args['fields'][ $key ]['label'] = '';
    117 				}
    118 				$args['fields'][ $key ]['id'] = $key;
    119 
    120 				// We check if the filed is an uploaded media ( image , file, video, etc.. ).
    121 				if ( isset( $value['type'] ) && ( 'image' === $value['type'] || 'cropped_image' === $value['type'] || 'upload' === $value['type'] ) ) {
    122 
    123 					// We add it to the list of fields that need some extra filtering/processing.
    124 					$media_fields_to_filter[ $key ] = true;
    125 				}
    126 
    127 				// If the field is a dropdown-pages field then add it to args.
    128 				if ( isset( $value['type'] ) && ( 'dropdown-pages' === $value['type'] ) ) {
    129 
    130 					$dropdown = wp_dropdown_pages(
    131 						array(
    132 							'name'              => '',
    133 							'echo'              => 0,
    134 							'show_option_none'  => esc_attr( $this->l10n['select-page'] ),
    135 							'option_none_value' => '0',
    136 							'selected'          => '',
    137 						)
    138 					);
    139 
    140 					// Hackily add in the data link parameter.
    141 					$dropdown = str_replace( '<select', '<select data-field="' . esc_attr( $args['fields'][ $key ]['id'] ) . '"' . $this->get_link(), $dropdown );
    142 
    143 					$args['fields'][ $key ]['dropdown'] = $dropdown;
    144 				}
    145 			}
    146 
    147 			$this->fields = $args['fields'];
    148 
    149 			// Now we are going to filter the fields.
    150 			// First we create a copy of the value that would be used otherwise.
    151 			$this->filtered_value = $this->value();
    152 
    153 			if ( is_array( $this->filtered_value ) && ! empty( $this->filtered_value ) ) {
    154 
    155 				// We iterate over the list of fields.
    156 				foreach ( $this->filtered_value as &$filtered_value_field ) {
    157 
    158 					if ( is_array( $filtered_value_field ) && ! empty( $filtered_value_field ) ) {
    159 
    160 						// We iterate over the list of properties for this field.
    161 						foreach ( $filtered_value_field as $key => &$value ) {
    162 
    163 							// We check if this field was marked as requiring extra filtering (in this case image, cropped_images, upload).
    164 							if ( array_key_exists( $key, $media_fields_to_filter ) ) {
    165 
    166 								// What follows was made this way to preserve backward compatibility.
    167 								// The repeater control use to store the URL for images instead of the attachment ID.
    168 								// We check if the value look like an ID (otherwise it's probably a URL so don't filter it).
    169 								if ( is_numeric( $value ) ) {
    170 
    171 									// "sanitize" the value.
    172 									$attachment_id = (int) $value;
    173 
    174 									// Try to get the attachment_url.
    175 									$url = wp_get_attachment_url( $attachment_id );
    176 
    177 									$filename = basename( get_attached_file( $attachment_id ) );
    178 
    179 									// If we got a URL.
    180 									if ( $url ) {
    181 
    182 										// 'id' is needed for form hidden value, URL is needed to display the image.
    183 										$value = array(
    184 											'id'  => $attachment_id,
    185 											'url' => $url,
    186 											'filename' => $filename,
    187 										);
    188 									}
    189 								}
    190 							}
    191 						}
    192 					}
    193 				}
    194 			}
    195 		}
    196 
    197 		/**
    198 		 * Refresh the parameters passed to the JavaScript via JSON.
    199 		 *
    200 		 * @access public
    201 		 */
    202 		public function to_json() {
    203 			parent::to_json();
    204 
    205 			$fields = $this->fields;
    206 
    207 			$this->json['fields'] = $fields;
    208 			$this->json['row_label'] = $this->row_label;
    209 
    210 			// If filtered_value has been set and is not empty we use it instead of the actual value.
    211 			if ( is_array( $this->filtered_value ) && ! empty( $this->filtered_value ) ) {
    212 				$this->json['value'] = $this->filtered_value;
    213 			}
    214 		}
    215 
    216 		/**
    217 		 * Enqueue control related scripts/styles.
    218 		 *
    219 		 * @access public
    220 		 */
    221 		public function enqueue() {
    222 
    223 			// If we have a color picker field we need to enqueue the Wordpress Color Picker style and script.
    224 			if ( is_array( $this->fields ) && ! empty( $this->fields ) ) {
    225 				foreach ( $this->fields as $field ) {
    226 					if ( isset( $field['type'] ) && 'color' === $field['type'] ) {
    227 						wp_enqueue_script( 'wp-color-picker' );
    228 						wp_enqueue_style( 'wp-color-picker' );
    229 						break;
    230 					}
    231 				}
    232 
    233 				foreach ( $this->fields as $field ) {
    234 					if ( isset( $field['type'] ) && 'dropdown-pages' === $field['type'] ) {
    235 						wp_enqueue_script( 'kirki-dropdown-pages' );
    236 						break;
    237 					}
    238 				}
    239 			}
    240 
    241 			wp_enqueue_script( 'kirki-repeater' );
    242 		}
    243 
    244 		/**
    245 		 * Render the control's content.
    246 		 * Allows the content to be overriden without having to rewrite the wrapper in $this->render().
    247 		 *
    248 		 * @access protected
    249 		 */
    250 		protected function render_content() {
    251 			?>
    252 			<?php if ( '' !== $this->tooltip ) : ?>
    253 				<a href="#" class="tooltip hint--left" data-hint="<?php echo esc_html( $this->tooltip ); ?>"><span class='dashicons dashicons-info'></span></a>
    254 			<?php endif; ?>
    255 			<label>
    256 				<?php if ( ! empty( $this->label ) ) : ?>
    257 					<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
    258 				<?php endif; ?>
    259 				<?php if ( ! empty( $this->description ) ) : ?>
    260 					<span class="description customize-control-description"><?php echo wp_kses_post( $this->description ); ?></span>
    261 				<?php endif; ?>
    262 				<input type="hidden" {{{ data.inputAttrs }}} value="" <?php echo wp_kses_post( $this->get_link() ); ?> />
    263 			</label>
    264 
    265 			<ul class="repeater-fields"></ul>
    266 
    267 			<?php if ( 0 && isset( $this->choices['limit'] ) ) : ?>
    268 				<p class="limit"><?php printf( esc_html( $this->l10n['limit-rows'] ), esc_html( $this->choices['limit'] ) ); ?></p>
    269 			<?php endif; ?>
    270 			<button class="button-secondary repeater-add"><?php echo esc_html( $this->button_label ); ?></button>
    271 
    272 			<?php
    273 
    274 			$this->repeater_js_template();
    275 
    276 		}
    277 
    278 		/**
    279 		 * An Underscore (JS) template for this control's content (but not its container).
    280 		 * Class variables for this control class are available in the `data` JS object.
    281 		 *
    282 		 * @access public
    283 		 */
    284 		public function repeater_js_template() {
    285 			?>
    286 			<script type="text/html" class="customize-control-repeater-content">
    287 				<# var field; var index = data.index; #>
    288 				<# data.kirki_config = '<?php echo esc_attr( $this->kirki_config ); ?>'; #>
    289 
    290 				<li class="repeater-row minimized" data-row="{{{ index }}}">
    291 
    292 					<div class="repeater-row-header">
    293 						<span class="repeater-row-label"></span>
    294 						<i class="dashicons dashicons-arrow-down repeater-minimize"></i>
    295 					</div>
    296 					<div class="repeater-row-content">
    297 						<# _.each( data, function( field, i ) { #>
    298 
    299 							<div class="repeater-field repeater-field-{{{ field.type }}}">
    300 
    301 								<# if ( 'text' === field.type || 'url' === field.type || 'link' === field.type || 'email' === field.type || 'tel' === field.type || 'date' === field.type ) { #>
    302 
    303 									<# if ( 'link' === field.type ) { #>
    304 										<# field.type = 'url' #>
    305 									<# } #>
    306 
    307 									<label>
    308 										<# if ( field.label ) { #>
    309 											<span class="customize-control-title">{{ field.label }}</span>
    310 										<# } #>
    311 										<# if ( field.description ) { #>
    312 											<span class="description customize-control-description">{{ field.description }}</span>
    313 										<# } #>
    314 										<input type="{{field.type}}" name="" value="{{{ field.default }}}" data-field="{{{ field.id }}}">
    315 									</label>
    316 
    317 								<# } else if ( 'hidden' === field.type ) { #>
    318 
    319 									<input type="hidden" data-field="{{{ field.id }}}" <# if ( field.default ) { #> value="{{ field.default }}" <# } #> />
    320 
    321 								<# } else if ( 'checkbox' === field.type ) { #>
    322 
    323 									<label>
    324 										<input type="checkbox" value="true" data-field="{{{ field.id }}}" <# if ( field.default ) { #> checked="checked" <# } #> /> {{ field.label }}
    325 										<# if ( field.description ) { #>
    326 											{{ field.description }}
    327 										<# } #>
    328 									</label>
    329 
    330 								<# } else if ( 'select' === field.type ) { #>
    331 
    332 									<label>
    333 										<# if ( field.label ) { #>
    334 											<span class="customize-control-title">{{ field.label }}</span>
    335 										<# } #>
    336 										<# if ( field.description ) { #>
    337 											<span class="description customize-control-description">{{ field.description }}</span>
    338 										<# } #>
    339 										<select data-field="{{{ field.id }}}">
    340 											<# _.each( field.choices, function( choice, i ) { #>
    341 												<option value="{{{ i }}}" <# if ( field.default == i ) { #> selected="selected" <# } #>>{{ choice }}</option>
    342 											<# }); #>
    343 										</select>
    344 									</label>
    345 
    346 								<# } else if ( 'dropdown-pages' === field.type ) { #>
    347 
    348 									<label>
    349 										<# if ( field.label ) { #>
    350 											<span class="customize-control-title">{{{ data.label }}}</span>
    351 										<# } #>
    352 										<# if ( field.description ) { #>
    353 											<span class="description customize-control-description">{{{ field.description }}}</span>
    354 										<# } #>
    355 										<div class="customize-control-content repeater-dropdown-pages">{{{ field.dropdown }}}</div>
    356 									</label>
    357 
    358 								<# } else if ( 'radio' === field.type ) { #>
    359 
    360 									<label>
    361 										<# if ( field.label ) { #>
    362 											<span class="customize-control-title">{{ field.label }}</span>
    363 										<# } #>
    364 										<# if ( field.description ) { #>
    365 											<span class="description customize-control-description">{{ field.description }}</span>
    366 										<# } #>
    367 
    368 										<# _.each( field.choices, function( choice, i ) { #>
    369 											<label>
    370 												<input type="radio" name="{{{ field.id }}}{{ index }}" data-field="{{{ field.id }}}" value="{{{ i }}}" <# if ( field.default == i ) { #> checked="checked" <# } #>> {{ choice }} <br/>
    371 											</label>
    372 										<# }); #>
    373 									</label>
    374 
    375 								<# } else if ( 'radio-image' === field.type ) { #>
    376 
    377 									<label>
    378 										<# if ( field.label ) { #>
    379 											<span class="customize-control-title">{{ field.label }}</span>
    380 										<# } #>
    381 										<# if ( field.description ) { #>
    382 											<span class="description customize-control-description">{{ field.description }}</span>
    383 										<# } #>
    384 
    385 										<# _.each( field.choices, function( choice, i ) { #>
    386 											<input type="radio" id="{{{ field.id }}}_{{ index }}_{{{ i }}}" name="{{{ field.id }}}{{ index }}" data-field="{{{ field.id }}}" value="{{{ i }}}" <# if ( field.default == i ) { #> checked="checked" <# } #>>
    387 												<label for="{{{ field.id }}}_{{ index }}_{{{ i }}}">
    388 													<img src="{{ choice }}">
    389 												</label>
    390 											</input>
    391 										<# }); #>
    392 									</label>
    393 
    394 								<# } else if ( 'color' === field.type ) { #>
    395 
    396 									<# var defaultValue = '';
    397 							        if ( field.default ) {
    398 							            if ( '#' !== field.default.substring( 0, 1 ) ) {
    399 							                defaultValue = '#' + field.default;
    400 							            } else {
    401 							                defaultValue = field.default;
    402 							            }
    403 							            defaultValue = ' data-default-color=' + defaultValue; // Quotes added automatically.
    404 							        } #>
    405 							        <label>
    406 							            <# if ( field.label ) { #>
    407 							                <span class="customize-control-title">{{{ field.label }}}</span>
    408 							            <# } #>
    409 							            <# if ( field.description ) { #>
    410 							                <span class="description customize-control-description">{{{ field.description }}}</span>
    411 							            <# } #>
    412 							            <input data-alpha="{{{ field.alpha }}}" class="color-picker-hex" type="text" maxlength="7" placeholder="<?php echo esc_attr( $this->l10n['hex-value'] ); ?>"  value="{{{ field.default }}}" data-field="{{{ field.id }}}" {{ defaultValue }} />
    413 
    414 							        </label>
    415 
    416 								<# } else if ( 'textarea' === field.type ) { #>
    417 
    418 									<# if ( field.label ) { #>
    419 										<span class="customize-control-title">{{ field.label }}</span>
    420 									<# } #>
    421 									<# if ( field.description ) { #>
    422 										<span class="description customize-control-description">{{ field.description }}</span>
    423 									<# } #>
    424 									<textarea rows="5" data-field="{{{ field.id }}}">{{ field.default }}</textarea>
    425 
    426 								<# } else if ( field.type === 'image' || field.type === 'cropped_image' ) { #>
    427 
    428 									<label>
    429 										<# if ( field.label ) { #>
    430 											<span class="customize-control-title">{{ field.label }}</span>
    431 										<# } #>
    432 										<# if ( field.description ) { #>
    433 											<span class="description customize-control-description">{{ field.description }}</span>
    434 										<# } #>
    435 									</label>
    436 
    437 									<figure class="kirki-image-attachment" data-placeholder="<?php echo esc_attr( $this->l10n['no-image-selected'] ); ?>" >
    438 										<# if ( field.default ) { #>
    439 											<# var defaultImageURL = ( field.default.url ) ? field.default.url : field.default; #>
    440 											<img src="{{{ defaultImageURL }}}">
    441 										<# } else { #>
    442 											<?php echo esc_attr( $this->l10n['no-image-selected'] ); ?>
    443 										<# } #>
    444 									</figure>
    445 
    446 									<div class="actions">
    447 										<button type="button" class="button remove-button<# if ( ! field.default ) { #> hidden<# } #>"><?php echo esc_attr( $this->l10n['remove'] ); ?></button>
    448 										<button type="button" class="button upload-button" data-label=" <?php echo esc_attr( $this->l10n['add-image'] ); ?>" data-alt-label="<?php echo esc_attr( $this->l10n['change-image'] ); ?>" >
    449 											<# if ( field.default ) { #>
    450 												<?php echo esc_attr( $this->l10n['change-image'] ); ?>
    451 											<# } else { #>
    452 												<?php echo esc_attr( $this->l10n['add-image'] ); ?>
    453 											<# } #>
    454 										</button>
    455 										<# if ( field.default.id ) { #>
    456 											<input type="hidden" class="hidden-field" value="{{{ field.default.id }}}" data-field="{{{ field.id }}}" >
    457 										<# } else { #>
    458 											<input type="hidden" class="hidden-field" value="{{{ field.default }}}" data-field="{{{ field.id }}}" >
    459 										<# } #>
    460 									</div>
    461 
    462 								<# } else if ( field.type === 'upload' ) { #>
    463 
    464 									<label>
    465 										<# if ( field.label ) { #>
    466 											<span class="customize-control-title">{{ field.label }}</span>
    467 										<# } #>
    468 										<# if ( field.description ) { #>
    469 											<span class="description customize-control-description">{{ field.description }}</span>
    470 										<# } #>
    471 									</label>
    472 
    473 									<figure class="kirki-file-attachment" data-placeholder="<?php echo esc_attr( $this->l10n['no-file-selected'] ); ?>" >
    474 										<# if ( field.default ) { #>
    475 											<# var defaultFilename = ( field.default.filename ) ? field.default.filename : field.default; #>
    476 											<span class="file"><span class="dashicons dashicons-media-default"></span> {{ defaultFilename }}</span>
    477 										<# } else { #>
    478 											<?php echo esc_attr( $this->l10n['no-file-selected'] ); ?>
    479 										<# } #>
    480 									</figure>
    481 
    482 									<div class="actions">
    483 										<button type="button" class="button remove-button<# if ( ! field.default ) { #> hidden<# } #>"></button>
    484 										<button type="button" class="button upload-button" data-label="<?php echo esc_attr( $this->l10n['add-file'] ); ?>" data-alt-label="<?php echo esc_attr( $this->l10n['change-file'] ); ?>" >
    485 											<# if ( field.default ) { #>
    486 												<?php echo esc_attr( $this->l10n['change-file'] ); ?>
    487 											<# } else { #>
    488 												<?php echo esc_attr( $this->l10n['add-file'] ); ?>
    489 											<# } #>
    490 										</button>
    491 										<# if ( field.default.id ) { #>
    492 											<input type="hidden" class="hidden-field" value="{{{ field.default.id }}}" data-field="{{{ field.id }}}" >
    493 										<# } else { #>
    494 											<input type="hidden" class="hidden-field" value="{{{ field.default }}}" data-field="{{{ field.id }}}" >
    495 										<# } #>
    496 									</div>
    497 
    498 								<# } else if ( 'custom' === field.type ) { #>
    499 
    500 									<# if ( field.label ) { #>
    501 										<span class="customize-control-title">{{ field.label }}</span>
    502 									<# } #>
    503 									<# if ( field.description ) { #>
    504 										<span class="description customize-control-description">{{ field.description }}</span>
    505 									<# } #>
    506 									<div data-field="{{{ field.id }}}">{{{ field.default }}}</div>
    507 
    508 								<# } else if( 'sectionseparator' === field.type ) { #>
    509 									<div class="one-page-express-separator">
    510 										<label>
    511 											<# if ( field.label ) { #>
    512 								                <span class="customize-control-title">{{ field.label }}</span>
    513 											<# } #>
    514 											<# if ( field.description ) { #>
    515 												<span class="description customize-control-description">{{ field.description }}</span>
    516 											<# } #>
    517 										</label>
    518 							        </div>
    519 								<# } #>
    520 
    521 							</div>
    522 						<# }); #>
    523 						<button type="button" class="button-link repeater-row-remove"><?php echo esc_attr( $this->l10n['remove'] ); ?></button>
    524 					</div>
    525 				</li>
    526 			</script>
    527 			<?php
    528 		}
    529 	}
    530 }