Overview

Classes

  • Papi_Admin
  • Papi_Admin_Ajax
  • Papi_Admin_Assets
  • Papi_Admin_Management_Pages
  • Papi_Admin_Menu
  • Papi_Admin_Meta_Box
  • Papi_Admin_Meta_Box_Tabs
  • Papi_Admin_Option_Handler
  • Papi_Admin_Post_Handler
  • Papi_Admin_View
  • Papi_Attachment_Type
  • Papi_Conditional_Rules
  • Papi_Container
  • Papi_Core_Autoload
  • Papi_Core_Conditional
  • Papi_Core_Conditional_Rule
  • Papi_Core_Data_Handler
  • Papi_Core_Page
  • Papi_Core_Property
  • Papi_Core_Type
  • Papi_Loader
  • Papi_Option_Page
  • Papi_Option_Type
  • Papi_Page_Type
  • Papi_Page_Type_Meta
  • Papi_Porter
  • Papi_Porter_Driver
  • Papi_Porter_Driver_Core
  • Papi_Post_Page
  • Papi_Property
  • Papi_Property_Bool
  • Papi_Property_Checkbox
  • Papi_Property_Color
  • Papi_Property_Datetime
  • Papi_Property_Divider
  • Papi_Property_Dropdown
  • Papi_Property_Editor
  • Papi_Property_Email
  • Papi_Property_File
  • Papi_Property_Flexible
  • Papi_Property_Gallery
  • Papi_Property_Hidden
  • Papi_Property_Html
  • Papi_Property_Image
  • Papi_Property_Link
  • Papi_Property_Number
  • Papi_Property_Post
  • Papi_Property_Radio
  • Papi_Property_Reference
  • Papi_Property_Relationship
  • Papi_Property_Repeater
  • Papi_Property_String
  • Papi_Property_Term
  • Papi_Property_Text
  • Papi_Property_Url
  • Papi_Property_User

Functions

  • current_page
  • papi
  • papi_action_delete_value
  • papi_action_include
  • papi_append_post_type_query
  • papi_body_class
  • papi_cache_delete
  • papi_cache_get
  • papi_cache_key
  • papi_cache_set
  • papi_camel_case
  • papi_cast_string_value
  • papi_convert_to_string
  • papi_current_user_is_allowed
  • papi_dashify
  • papi_delete_field
  • papi_delete_option
  • papi_delete_property_meta_value
  • papi_display_page_type
  • papi_doing_ajax
  • papi_esc_html
  • papi_f
  • papi_field
  • papi_field_shortcode
  • papi_field_value
  • papi_fields
  • papi_filter_conditional_rule_allowed
  • papi_filter_core_load_one_type_on
  • papi_filter_format_value
  • papi_filter_load_value
  • papi_filter_settings_directories
  • papi_filter_settings_only_page_type
  • papi_filter_settings_page_type_column_title
  • papi_filter_settings_show_page_type
  • papi_filter_settings_show_standard_page_type
  • papi_filter_settings_show_standard_page_type_in_filter
  • papi_filter_settings_sort_order
  • papi_filter_settings_standard_page_description
  • papi_filter_settings_standard_page_name
  • papi_filter_settings_standard_page_thumbnail
  • papi_filter_update_value
  • papi_from_property_array_slugs
  • papi_get_all_files_in_directory
  • papi_get_all_page_type_files
  • papi_get_all_page_types
  • papi_get_box_property
  • papi_get_class_name
  • papi_get_field
  • papi_get_file_path
  • papi_get_number_of_pages
  • papi_get_only_objects
  • papi_get_option
  • papi_get_options_and_properties
  • papi_get_or_post
  • papi_get_page
  • papi_get_page_new_url
  • papi_get_page_query_strings
  • papi_get_page_type
  • papi_get_page_type_base_path
  • papi_get_page_type_by_id
  • papi_get_page_type_by_post_id
  • papi_get_page_type_id
  • papi_get_page_type_key
  • papi_get_page_type_meta_value
  • papi_get_page_type_name
  • papi_get_page_type_template
  • papi_get_parent_post_id
  • papi_get_post_id
  • papi_get_post_type
  • papi_get_post_type_label
  • papi_get_post_types
  • papi_get_property_class_name
  • papi_get_property_meta_value
  • papi_get_property_options
  • papi_get_property_type
  • papi_get_property_type_key
  • papi_get_property_type_key_f
  • papi_get_qs
  • papi_get_sanitized_post
  • papi_get_slugs
  • papi_get_tab_options
  • papi_html_name
  • papi_html_tag
  • papi_include_template
  • papi_is_empty
  • papi_is_json
  • papi_is_method
  • papi_is_option_page
  • papi_is_option_type
  • papi_is_page_type
  • papi_is_property
  • papi_is_property_type_key
  • papi_is_rule
  • papi_management_page_type_render_box
  • papi_maybe_convert_to_array
  • papi_maybe_convert_to_object
  • papi_maybe_get_callable_value
  • papi_maybe_json_decode
  • papi_maybe_json_encode
  • papi_nl2br
  • papi_option_shortcode
  • papi_option_type_exists
  • papi_page_type_exists
  • papi_populate_properties
  • papi_property
  • papi_remove_papi
  • papi_remove_trailing_quotes
  • papi_render_html_tag
  • papi_render_properties
  • papi_render_property
  • papi_require_text
  • papi_required_html
  • papi_rule
  • papi_santize_data
  • papi_set_page_type_id
  • papi_setup_tabs
  • papi_slugify
  • papi_sort_order
  • papi_tab
  • papi_template
  • papi_template_include
  • papi_to_array
  • papi_to_property_array_slugs
  • papi_translate_keys
  • papi_underscorify
  • papi_update_field
  • papi_update_option
  • papi_update_property_meta_value
  • papi_with
  • papify
  • the_papi_field
  • the_papi_option
  • the_papi_page_type_name
  • Overview
  • Class
  1: <?php
  2: 
  3: /**
  4:  * Flexible repeater property that can repeat multiple properties
  5:  * with different properties per layout.
  6:  */
  7: class Papi_Property_Flexible extends Papi_Property_Repeater {
  8: 
  9:     /**
 10:      * The convert type.
 11:      *
 12:      * @var string
 13:      */
 14:     public $convert_type = 'array';
 15: 
 16:     /**
 17:      * Flexible repeater counter number.
 18:      *
 19:      * @var int
 20:      */
 21:     protected $counter = 0;
 22: 
 23:     /**
 24:      * The default value.
 25:      *
 26:      * @var array
 27:      */
 28:     public $default_value = [];
 29: 
 30:     /**
 31:      * Exclude properties that is not allowed in a repeater.
 32:      *
 33:      * @var array
 34:      */
 35:     protected $exclude_properties = ['flexible', 'repeater'];
 36: 
 37:     /**
 38:      * The layout key.
 39:      *
 40:      * @var string
 41:      */
 42:     protected $layout_key = '_layout';
 43: 
 44:     /**
 45:      * Layout prefix regex.
 46:      *
 47:      * @var string
 48:      */
 49:     private $layout_prefix_regex = '/^\_flexible\_layout\_/';
 50: 
 51:     /**
 52:      * Delete value from the database.
 53:      *
 54:      * @param  string $slug
 55:      * @param  int    $post_id
 56:      * @param  string $type
 57:      *
 58:      * @return bool
 59:      */
 60:     public function delete_value( $slug, $post_id, $type ) {
 61:         $rows   = intval( papi_get_property_meta_value( $post_id, $slug ) );
 62:         $value  = $this->load_value( $rows, $slug, $post_id );
 63:         $value  = papi_to_property_array_slugs( $value, $slug );
 64:         $result = true;
 65: 
 66:         foreach ( $value as $key => $value ) {
 67:             $out    = papi_delete_property_meta_value( $post_id, $key, $type );
 68:             $result = $out ? $result : $out;
 69:         }
 70: 
 71:         return $result;
 72:     }
 73: 
 74:     /**
 75:      * Format the value of the property before it's returned
 76:      * to WordPress admin or the site.
 77:      *
 78:      * @param  mixed  $values
 79:      * @param  string $repeater_slug
 80:      * @param  int    $post_id
 81:      *
 82:      * @return array
 83:      */
 84:     public function format_value( $values, $repeater_slug, $post_id ) {
 85:         if ( ! is_array( $values ) ) {
 86:             return [];
 87:         }
 88: 
 89:         foreach ( $values as $index => $layout ) {
 90:             foreach ( $layout as $slug => $value ) {
 91:                 if ( is_string( $value ) && preg_match( $this->layout_prefix_regex, $value ) ) {
 92:                     if ( isset( $values[$index][$this->layout_key] ) ) {
 93:                         unset( $values[$index][$slug] );
 94:                         continue;
 95:                     }
 96: 
 97:                     $values[$index][$this->layout_key] = $value;
 98:                     unset( $values[$index][$slug] );
 99: 
100:                     continue;
101:                 }
102: 
103:                 if ( papi_is_property_type_key( $slug ) ) {
104:                     continue;
105:                 }
106: 
107:                 $property_type_slug = papi_get_property_type_key_f( $slug );
108: 
109:                 if ( ! isset( $values[$index][$property_type_slug] ) ) {
110:                     continue;
111:                 }
112: 
113:                 $property_type_value = $values[$index][$property_type_slug];
114:                 $property_type = papi_get_property_type( $property_type_value );
115: 
116:                 if ( ! is_object( $property_type ) ) {
117:                     continue;
118:                 }
119: 
120:                 // Get property child slug.
121:                 $child_slug = $this->get_child_slug( $repeater_slug, $slug );
122: 
123:                 // Load the value.
124:                 $values[$index][$slug] = $property_type->load_value(
125:                     $value,
126:                     $child_slug,
127:                     $post_id
128:                 );
129: 
130:                 $values[$index][$slug] = papi_filter_load_value(
131:                     $property_type->type,
132:                     $values[$index][$slug],
133:                     $child_slug,
134:                     $post_id
135:                 );
136: 
137:                 // Format the value from the property class.
138:                 $values[$index][$slug] = $property_type->format_value(
139:                     $values[$index][$slug],
140:                     $child_slug,
141:                     $post_id
142:                 );
143: 
144:                 if ( ! is_admin() ) {
145:                     $values[$index][$slug] = papi_filter_format_value(
146:                         $property_type->type,
147:                         $values[$index][$slug],
148:                         $child_slug,
149:                         $post_id
150:                     );
151:                 }
152: 
153:                 $values[$index][$property_type_slug] = $property_type_value;
154:             }
155:         }
156: 
157:         if ( ! is_admin() ) {
158:             foreach ( $values as $index => $row ) {
159:                 foreach ( $row as $slug => $value ) {
160:                     if ( is_string( $value ) && preg_match( $this->layout_prefix_regex, $value ) ) {
161:                         $values[$index][$slug] = preg_replace(
162:                             $this->layout_prefix_regex,
163:                             '',
164:                             $value
165:                         );
166:                     }
167: 
168:                     if ( papi_is_property_type_key( $slug ) ) {
169:                         unset( $values[$index][$slug] );
170:                     }
171:                 }
172:             }
173:         }
174: 
175:         return $values;
176:     }
177: 
178:     /**
179:      * Check if the given key is a valid layout key.
180:      *
181:      * @param  string $key
182:      *
183:      * @return bool
184:      */
185:     protected function is_layout_key( $key ) {
186:         return is_string( $key ) && $this->layout_key === $key;
187:     }
188: 
189:     /**
190:      * Generate layout slug.
191:      *
192:      * @param  string $key
193:      * @param  string $extra
194:      *
195:      * @return string
196:      */
197:     protected function get_json_id( $key, $extra = '' ) {
198:         return $this->get_slug() . '_' . papi_slugify( $key ) . (
199:             empty( $extra ) ? '' : '_' . $extra
200:         );
201:     }
202: 
203:     /**
204:      * Get layout by slug.
205:      *
206:      * @param  string $slug
207:      *
208:      * @return string
209:      */
210:     protected function get_layout( $slug ) {
211:         $layouts = $this->get_settings_layouts();
212: 
213:         foreach ( $layouts as $layout ) {
214:             if ( $layout['slug'] === $slug ) {
215:                 return $layout;
216:             }
217:         }
218:     }
219: 
220:     /**
221:      * Get layout value.
222:      *
223:      * @param  string $prefix
224:      * @param  string $name
225:      *
226:      * @return string
227:      */
228:     protected function get_layout_value( $prefix, $name ) {
229:         return sprintf( '_flexible_%s_%s', $prefix, $name );
230:     }
231: 
232:     /**
233:      * Get results from the database.
234:      *
235:      * @param  int    $value
236:      * @param  string $repeater_slug
237:      * @param  int    $post_id
238:      *
239:      * @return array
240:      */
241:     protected function get_results( $value, $repeater_slug, $post_id ) {
242:         global $wpdb;
243: 
244:         $option_page = $this->is_option_page();
245: 
246:         if ( $option_page ) {
247:             $table = $wpdb->prefix . 'options';
248:             $query = $wpdb->prepare(
249:                 "SELECT * FROM `$table` WHERE `option_name` LIKE '%s' ORDER BY `option_id` ASC",
250:                 $repeater_slug . '_%'
251:             );
252:         } else {
253:             $table = $wpdb->prefix . 'postmeta';
254:             $query = $wpdb->prepare(
255:                 "SELECT * FROM `$table` WHERE `meta_key` LIKE '%s' AND `post_id` = %s ORDER BY `meta_id` ASC", $repeater_slug . '_%',
256:                 $post_id
257:             );
258:         }
259: 
260:         $dbresults = $wpdb->get_results( $query );
261:         $value     = intval( $value );
262: 
263:         // Do not proceed with empty value or columns.
264:         if ( empty( $value ) ) {
265:             return [[], []];
266:         }
267: 
268:         $values  = [];
269:         $results = [];
270:         $trash   = [];
271: 
272:         // Get row results.
273:         $rows = $this->get_row_results( $dbresults );
274: 
275:         // Get columns, divde all items with two.
276:         $columns = array_map( function ( $row ) {
277:             return count( $row ) / 2;
278:         }, $rows );
279: 
280:         $rows = array_values( $rows );
281: 
282:         // Add repeater slug with number of rows to the values array.
283:         $values[$repeater_slug] = $value;
284: 
285:         for ( $i = 0; $i < $value; $i++ ) {
286: 
287:             $no_trash = [];
288: 
289:             if ( ! isset( $columns[$i] ) || ! isset( $rows[$i] ) ) {
290:                 continue;
291:             }
292: 
293:             foreach ( $rows[$i] as $slug => $meta ) {
294:                 if ( ! is_string( $slug ) || ! isset( $rows[$i][$slug] ) ) {
295:                     continue;
296:                 }
297: 
298:                 // Do not deal with layout meta object here since the property meta object will deal with it later.
299:                 if ( is_string( $meta->meta_value ) && preg_match( $this->layout_prefix_regex, $meta->meta_value ) ) {
300:                     if ( ! isset( $values[$slug] ) ) {
301:                         $values[$slug] = $meta->meta_value;
302:                     }
303: 
304:                     continue;
305:                 }
306: 
307:                 // Add meta object to the no trash array.
308:                 // so it won't be deleted.
309:                 $no_trash[$slug] = $meta;
310: 
311:                 // Get property type key and value.
312:                 $property_type_key   = papi_get_property_type_key_f(
313:                     $meta->meta_key
314:                 );
315:                 $property_type_value = papi_get_property_meta_value(
316:                     $post_id,
317:                     $property_type_key
318:                 );
319: 
320:                 // Serialize value if needed.
321:                 $meta->meta_value = papi_maybe_json_decode(
322:                     maybe_unserialize( $meta->meta_value )
323:                 );
324: 
325:                 // Add property value and property type value.
326:                 $values[$meta->meta_key] = $meta->meta_value;
327:                 $values[$property_type_key] = $property_type_value;
328: 
329:                 // Add the flexible layout for the property.
330:                 if ( ! preg_match( '/\_layout$/', $slug ) && is_string( $rows[$i][$slug]->meta_value ) && ! preg_match( $this->layout_prefix_regex, $rows[$i][$slug]->meta_value ) ) {
331:                     $slug .= '_layout';
332:                 }
333: 
334:                 if ( isset( $rows[$i][$slug] ) ) {
335:                     // Add the meta value.
336:                     $values[$slug] = $rows[$i][$slug]->meta_value;
337:                 }
338:             }
339: 
340:             // Get the meta keys to delete.
341:             $trash_diff = array_diff(
342:                 array_keys( $rows[$i] ),
343:                 array_keys( $no_trash )
344:             );
345: 
346:             if ( ! empty( $trash_diff ) ) {
347:                 // Find all trash meta objects from results array.
348:                 foreach ( $trash_diff as $slug ) {
349:                     if ( ! isset( $results[$i] ) || ! isset( $rows[$i][$slug] ) ) {
350:                         continue;
351:                     }
352: 
353:                     $trash[$results[$i][$slug]->meta_key] = $rows[$i][$slug];
354:                 }
355:             }
356:         }
357: 
358:         $dblayouts = [];
359: 
360:         // Fetch one layout per row.
361:         foreach ( array_keys( $values ) as $slug ) {
362:             if ( preg_match( '/slug\\' . $this->layout_key . '$/', $slug ) ) {
363:                 $num = str_replace( $repeater_slug . '_', '', $slug );
364:                 $num  = intval( $num[0] );
365: 
366:                 if ( ! isset( $dblayouts[$num] ) ) {
367:                     $dblayouts[$num] = $num . $values[$slug];
368:                 }
369:             }
370:         }
371: 
372:         $layouts = $this->get_settings_layouts();
373: 
374:         // Add empty rows that isn't saved to database.
375:         for ( $i = 0; $i < $value; $i++ ) {
376:             foreach ( $layouts as $layout ) {
377:                 if ( in_array( $i . $layout['slug'], $dblayouts ) ) {
378:                     foreach ( $layout['items'] as $prop ) {
379:                         $slug = sprintf( '%s_%d_%s', $repeater_slug, $i, papi_remove_papi( $prop->slug ) );
380: 
381:                         if ( ! isset( $values[$slug] ) ) {
382:                             $values[$slug] = null;
383:                         }
384:                     }
385:                 }
386:             }
387:         }
388: 
389:         return [$values, $trash];
390:     }
391: 
392:     /**
393:      * Get layouts.
394:      *
395:      * @return array
396:      */
397:     protected function get_settings_layouts() {
398:         $settings = $this->get_settings();
399:         return $this->prepare_properties( papi_to_array( $settings->items ) );
400:     }
401: 
402:     /**
403:      * Change value after it's loaded from the database
404:      * and populate every property in the flexible with the right property type.
405:      *
406:      * @param mixed  $value
407:      * @param string $repeater_slug
408:      * @param int    $post_id
409:      *
410:      * @return array
411:      */
412:     public function load_value( $value, $repeater_slug, $post_id ) {
413:         if ( is_array( $value ) ) {
414:             return $value;
415:         }
416: 
417:         list( $results, $trash ) = $this->get_results( $value, $repeater_slug, $post_id );
418: 
419:         // Will not need this array.
420:         unset( $trash );
421: 
422:         $page    = $this->get_page();
423:         $results = papi_from_property_array_slugs(
424:             $results,
425:             papi_remove_papi( $repeater_slug )
426:         );
427: 
428:         if ( is_null( $page ) ) {
429:             return $this->default_value;
430:         }
431: 
432:         foreach ( $results as $index => $row ) {
433:             foreach ( $row as $slug => $value ) {
434:                 if ( papi_is_property_type_key( $slug ) ) {
435:                     continue;
436:                 }
437: 
438:                 if ( $property = $page->get_property( $repeater_slug, $slug ) ) {
439:                     $type_key = papi_get_property_type_key_f( $slug );
440:                     $results[$index][$type_key] = $property;
441:                 }
442:             }
443:         }
444: 
445:         return $results;
446:     }
447: 
448:     /**
449:      * Prepare properties.
450:      *
451:      * Not the best name for this function, but since
452:      * property repeater using this we can't rename it.
453:      *
454:      * @param  array $layouts
455:      *
456:      * @return array
457:      */
458:     protected function prepare_properties( $layouts ) {
459:         $layouts = array_map( function ( $layout ) {
460:             return (array) $layout;
461:         }, $layouts );
462: 
463:         foreach ( $layouts as $index => $layout ) {
464:             if ( ! $this->valid_layout( $layout ) ) {
465:                 if ( is_array( $layout ) ) {
466:                     unset( $layout[$index] );
467:                 } else {
468:                     unset( $layouts[$index] );
469:                 }
470: 
471:                 continue;
472:             }
473: 
474:             if ( ! isset( $layout['slug'] ) ) {
475:                 $layout['slug'] = $layout['title'];
476:             }
477: 
478:             $layouts[$index]['slug']  = papi_slugify( $layout['slug'] );
479:             $layouts[$index]['slug']  = $this->get_layout_value(
480:                 'layout',
481:                 $layouts[$index]['slug']
482:             );
483:             $layouts[$index]['items'] = parent::prepare_properties(
484:                 $layout['items']
485:             );
486:         }
487: 
488:         return array_filter( $layouts );
489:     }
490: 
491:     /**
492:      * Render AJAX request.
493:      */
494:     public function render_ajax_request() {
495:         $items   = null;
496:         $layouts = $this->get_settings_layouts();
497: 
498:         if ( defined( 'DOING_PAPI_AJAX' ) && DOING_PAPI_AJAX ) {
499:             $counter = papi_get_qs( 'counter' );
500:             $this->counter  = intval( $counter );
501:             $flexible_layout = papi_get_qs( 'flexible_layout' );
502:             foreach ( $layouts as $layout ) {
503:                 if ( $layout['slug'] === $flexible_layout ) {
504:                     $items = $layout;
505:                     break;
506:                 }
507:             }
508:         }
509: 
510:         if ( ! empty( $items ) ) {
511:             $this->render_properties( $items, false );
512:         }
513:     }
514: 
515:     /**
516:      * Render layout JSON template.
517:      *
518:      * @param string $slug
519:      */
520:     protected function render_json_template( $slug ) {
521:         $options = $this->get_options();
522: 
523:         foreach ( $options->settings->items as $key => $value ) {
524:             if ( ! isset( $value['items'] ) ) {
525:                 continue;
526:             }
527: 
528:             foreach ( $value['items'] as $index => $property ) {
529:                 // Don't show the property if it's disabled.
530:                 if ( $property->disabled() ) {
531:                     unset( $options->settings->items[$key]['items'][$index] );
532:                     continue;
533:                 }
534: 
535:                 $options->settings->items[$key]['items'][$index] = clone $property->get_options();
536:             }
537:         }
538:         ?>
539:         <script type="application/json" data-papi-json="<?php echo $slug; ?>_repeater_json">
540:             <?php echo json_encode( [$options] ); ?>
541:         </script>
542:         <?php
543:     }
544: 
545:     /**
546:      * Render layout input.
547:      *
548:      * @param string $slug
549:      * @param string $value
550:      */
551:     protected function render_layout_input( $slug, $value ) {
552:         // Creating a fake hidden property to generate right slug.
553:         $slug = $this->html_name( papi_property( [
554:             'type' => 'hidden',
555:             'slug' => $slug . $this->layout_key
556:         ] ), $this->counter );
557:         ?>
558:         <input type="hidden" name="<?php echo $slug; ?>" value="<?php echo $value; ?>" />
559:         <?php
560:     }
561: 
562:     /**
563:      * Render properties.
564:      *
565:      * @param array $row
566:      * @param array|bool $value
567:      */
568:     protected function render_properties( $row, $value ) {
569:         $has_value     = $value !== false;
570:         $layout_slug   = isset( $row['slug'] ) ? $row['slug'] : $value['_layout'];
571:         $render_layout = $this->get_setting( 'layout' );
572:         $row           = isset( $row['items'] ) ? $row['items'] : $row;
573:         ?>
574:             <td class="repeater-column flexible-column <?php echo $render_layout === 'table' ? 'flexible-layout-table' : 'flexible-layout-row'; ?>">
575:                 <div class="repeater-content-open">
576:                     <table class="<?php echo $render_layout === 'table' ? 'flexible-table' : 'papi-table'; ?>">
577:                         <?php
578:                         if ( $render_layout === 'table' ):
579:                             echo '<thead>';
580:                             for ( $i = 0, $l = count( $row ); $i < $l; $i++ ) {
581:                                 // Don't show the property if it's disabled.
582:                                 if ( $row[$i]->disabled() ) {
583:                                     continue;
584:                                 }
585: 
586:                                 echo '<th class="' . ( $row[$i]->display() ? '' : 'papi-hide' ) . '">';
587:                                 echo sprintf(
588:                                     '<label for="%s">%s</label>',
589:                                     $this->html_id( $row[$i], $this->counter ),
590:                                     $row[$i]->title
591:                                 );
592:                                 echo '</th>';
593:                             }
594:                             echo '</thead>';
595:                         endif;
596: 
597:                         echo '<tbody>';
598: 
599:                         if ( $render_layout === 'table' ):
600:                             echo '<tr>';
601:                         endif;
602: 
603:                         for ( $i = 0, $l = count( $row ); $i < $l; $i++ ) {
604:                             // Don't show the property if it's disabled.
605:                             if ( $row[$i]->disabled() ) {
606:                                 continue;
607:                             }
608: 
609:                             $render_property = clone $row[$i]->get_options();
610:                             $value_slug      = $row[$i]->get_slug( true );
611: 
612:                             if ( $has_value ) {
613:                                 if ( array_key_exists( $value_slug, $value ) ) {
614:                                     $render_property->value = $value[$value_slug];
615:                                 } else {
616:                                     if ( array_key_exists( $row[$i]->get_slug(), $value ) ) {
617:                                         $render_property->value = $row[$i]->default_value;
618:                                     } else {
619:                                         continue;
620:                                     }
621:                                 }
622:                             }
623: 
624:                             $render_property->slug  = $this->html_name(
625:                                 $render_property,
626:                                 $this->counter
627:                             );
628:                             $render_property->raw   = $render_layout === 'table';
629: 
630:                             if ( $render_layout === 'table' ) {
631:                                 echo '<td class="' . ( $row[$i]->display() ? '' : 'papi-hide' ) . '">';
632:                             }
633: 
634:                             $layout_value = isset( $layout_slug ) ?
635:                                 $layout_slug : $value[$this->layout_key];
636: 
637:                             $this->render_layout_input(
638:                                 $value_slug,
639:                                 $layout_value
640:                             );
641: 
642:                             papi_render_property( $render_property );
643: 
644:                             if ( $render_layout === 'table' ) {
645:                                 echo '</td>';
646:                             }
647:                         }
648: 
649:                         if ( $render_layout === 'table' ):
650:                             echo '</tr>';
651:                         endif;
652: 
653:                         echo '</tbody>';
654:                         ?>
655:                     </table>
656:                 </div>
657:                 <div class="repeater-content-closed">
658:                     <?php
659:                     if ( $layout = $this->get_layout( $layout_slug ) ) {
660:                         echo $layout['title'];
661:                     }
662:                     ?>
663:                 </div>
664:             </td>
665:         <?php
666:     }
667: 
668:     /**
669:      * Render repeater html.
670:      *
671:      * @param object $options
672:      */
673:     protected function render_repeater( $options ) {
674:         $layouts = $this->get_settings_layouts();
675:         ?>
676:         <div class="papi-property-flexible papi-property-repeater-top" data-limit="<?php echo $this->get_setting( 'limit' ); ?>">
677:             <table class="papi-table">
678:                 <tbody class="repeater-tbody flexible-tbody">
679:                     <?php $this->render_repeater_row(); ?>
680:                 </tbody>
681:             </table>
682: 
683:             <div class="bottom">
684:                 <div class="flexible-layouts-btn-wrap">
685:                     <div class="flexible-layouts papi-hide">
686:                         <div class="flexible-layouts-arrow"></div>
687:                         <ul>
688:                             <?php
689:                             foreach ( $layouts as $layout ) {
690:                                 papi_render_html_tag( 'li', [
691:                                     papi_html_tag( 'a', [
692:                                         'data-layout'    => $layout['slug'],
693:                                         'data-papi-json' => sprintf( '%s_repeater_json', $options->slug ),
694:                                         'href'           => '#',
695:                                         'role'           => 'button',
696:                                         'tabindex'       => 0,
697:                                         $layout['title']
698:                                     ] )
699:                                 ] );
700:                             }
701:                             ?>
702:                         </ul>
703:                     </div>
704: 
705:                     <?php
706:                     papi_render_html_tag( 'button', [
707:                         'class' => 'button button-primary',
708:                         'type'  => 'button',
709:                         esc_html( $this->get_setting( 'add_new_label' ) )
710:                     ] );
711:                     ?>
712:                 </div>
713:             </div>
714: 
715:             <?php /* Default repeater value */ ?>
716: 
717:             <input type="hidden" data-papi-rule="<?php echo $options->slug; ?>" name="<?php echo $this->get_slug(); ?>[]" />
718:         </div>
719:         <?php
720:     }
721: 
722:     /**
723:      * Render repeater row.
724:      */
725:     protected function render_repeater_row() {
726:         $layouts = $this->get_settings_layouts();
727:         $values  = $this->get_value();
728: 
729:         // Fetch all slugs in all layouts.
730:         $slugs = [];
731:         foreach ( $layouts as $index => $layout ) {
732:             foreach ( $layout['items'] as $item ) {
733:                 $slugs[] = papi_remove_papi( $item->slug );
734:             }
735:         }
736: 
737:         // Remove values that don't exists in the slugs array.
738:         foreach ( $values as $index => $row ) {
739:             $keys = array_keys( $row );
740: 
741:             foreach ( $row as $slug => $value ) {
742:                 if ( in_array( $slug, $keys ) || papi_is_property_type_key( $slug ) || $this->is_layout_key( $slug ) ) {
743:                     continue;
744:                 }
745: 
746:                 unset( $values[$index][$slug] );
747:             }
748:         }
749: 
750:         $values = array_filter( $values );
751:         $closed_rows = $this->get_setting( 'closed_rows', true );
752: 
753:         foreach ( $values as $index => $row ):
754:             ?>
755: 
756:             <tr <?php echo $closed_rows ? 'class="closed"' : ''; ?>>
757:                 <td class="handle">
758:                     <span class="toggle"></span>
759:                     <span class="count"><?php echo $this->counter + 1; ?></span>
760:                 </td>
761:                 <?php
762:                 foreach ( $layouts as $layout ) {
763:                     // Don't render layouts that don't have a valid value in the database.
764:                     if ( ! isset( $row[$this->layout_key] ) || $layout['slug'] !== $row[$this->layout_key] ) {
765:                         continue;
766:                     }
767: 
768:                     // Render all properties in the layout
769:                     $this->render_properties( $layout['items'], $row );
770:                 }
771: 
772:                 $this->counter++;
773:                 ?>
774:                 <td class="last">
775:                     <span>
776:                         <a title="<?php _e( 'Remove', 'papi' ); ?>" href="#" class="repeater-remove-item">x</a>
777:                     </span>
778:                 </td>
779:             </tr>
780: 
781:             <?php
782:         endforeach;
783:     }
784: 
785:     /**
786:      * Render repeater row template.
787:      */
788:     public function render_repeater_row_template() {
789:         ?>
790:         <script type="text/template" id="tmpl-papi-property-flexible-row">
791:             <tr>
792:                 <td class="handle">
793:                     <span class="toggle"></span>
794:                     <span class="count"><%= counter + 1 %></span>
795:                 </td>
796:                 <%= columns %>
797:                 <td class="last">
798:                     <span>
799:                         <a title="<?php _e( 'Remove', 'papi' ); ?>" href="#" class="repeater-remove-item">x</a>
800:                     </span>
801:                 </td>
802:             </tr>
803:         </script>
804:         <?php
805:     }
806: 
807:     /**
808:      * Setup actions.
809:      */
810:     protected function setup_actions() {
811:         add_action( 'admin_head', [$this, 'render_repeater_row_template'] );
812:     }
813: 
814:     /**
815:      * Check if the layout is valid or not.
816:      *
817:      * @param array $layout
818:      *
819:      * @return bool
820:      */
821:     private function valid_layout( $layout ) {
822:         return isset( $layout['title'] ) && isset( $layout['items'] );
823:     }
824: }
825: 
API documentation generated by ApiGen