'show_macro', 'shortcode_name_alias' => 'mt_template', 'content_macro_post_type' => 'content_macro', 'filter' => '@', 'separator' => ', ', 'post_member' => '.', 'use_native_mode' => false ] ); } else { # if ( $TPCTI_MF2_ACTIVE ) { $options = get_option( 'tpcti_options', ( object ) [ 'shortcode_name' => 'mt_template', 'shortcode_name_alias' => '', 'content_macro_post_type' => 'mt_content_template', 'filter' => '@', 'separator' => ', ', 'post_member' => '.' ] ); } # if ( $TPCTI_MF2_ACTIVE ) { $error = NULL; add_action( 'init', function() use ( $options ) { global $wpdb; # the content macros are stored in its own post type register_post_type( $options->content_macro_post_type, [ 'label' => 'Post Content Templates', 'description' => 'defines a post content template for HTML and WordPress shortcodes', 'public' => TRUE, 'exclude_from_search' => TRUE, 'public_queryable' => FALSE, 'show_ui' => TRUE, 'show_in_nav_menus' => FALSE, 'show_in_menu' => TRUE, 'menu_position' => 50 ] ); # insert a sample content macro if ( !$wpdb->get_var( $wpdb->prepare( <<posts WHERE post_type = %s AND post_title = 'A Post Content Template Example' AND post_status = 'publish' EOD , $options->content_macro_post_type ) ) ) { wp_insert_post( [ 'post_type' => $options->content_macro_post_type, 'post_name' => 'a-post-content-template-example', 'post_title' => 'A Post Content Template Example', 'post_status' => 'publish', 'post_content' => <<<'EOD' [mt_template it="alpha:'1';'2'"] #if( $#alpha# = "1" )# Hello #else# #if( $#alpha# = $#beta# )# World #endif# #endif# [/mt_template] EOD ] ); } # if ( !$wpdb->get_var( <<get_col( $wpdb->prepare( <<posts WHERE post_type = %s AND post_title = %s AND post_status = 'publish' EOD , $options->content_macro_post_type, $_POST[ 'title' ] ) ); $post = [ 'post_type' => $options->content_macro_post_type, 'post_name' => $_POST['slug'], 'post_title' => $_POST['title'], 'post_status' => 'publish', 'post_content' => $_POST['text'] ]; if ( $ids ) { $post['ID'] = $ids[0]; $id0 = wp_update_post( $post ); } else { $id1 = wp_insert_post( $post ); } die( !empty ( $id0 ) ? "Content template $id0 updated." : ( !empty( $id1 ) ? "Content template $id1 created." : "Error: Content template not created/updated." ) ); } ); # add_action( 'wp_ajax_mf2tk_update_content_macro', function() { # AJAX action 'wp_ajax_tpcti_eval_post_content' # handles evaluate HTML fragments from post content editor shortcode tester add_action( 'wp_ajax_tpcti_eval_post_content', function( ) use ( &$do_macro, $TPCTI_MF2_ACTIVE ) { if ( $TPCTI_MF2_ACTIVE ) { require_once( MF_PATH . '/mf_front_end.php' ); # MF2 only } # if ( $TPCTI_MF2_ACTIVE ) { echo $do_macro( [ 'post' => $_POST[ 'post_id' ] ], stripslashes( $_POST[ 'post_content' ] ) ); exit; } ); # add_action( 'wp_ajax_tpcti_eval_post_content', function( ) use ( &$do_macro ) { # content macros should not be viewable add_filter( 'post_row_actions', function( $actions, $post ) use ( $options ) { if ( get_post_type( $post ) === $options->content_macro_post_type ) { unset( $actions['view'] ); } return $actions; }, 10, 2 ); # things to do only on post.php and post-new.php admin pages $post_editor_actions = function( ) use ( $options, $TPCTI_MF2_ACTIVE ) { # insert Content Template database into head of document # later JavaScript code will read this database and build the options for the select HTML element # of "Insert Template" popup add_action( 'admin_head', function( ) use ( $options, $TPCTI_MF2_ACTIVE ) { global $wpdb; $additional_select_clause_for_content_macros = " AND post_name NOT LIKE 'search-result-template-for-%%'"; # MF2 only $results = $wpdb->get_results( $wpdb->prepare( <<posts WHERE post_type = %s AND post_status = 'publish'$additional_select_clause_for_content_macros ORDER BY post_title EOD , $options->content_macro_post_type ), OBJECT ); if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { $name = 'macro'; } else { # if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { $name = 'name'; } # } else { # if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { ?> $_POST[ 'post_id' ] ], stripslashes( $_POST[ 'post_content' ] ) ); exit; } ); # add_action( 'wp_ajax_nopriv_tpcti_eval_post_content', function( ) use ( &$do_macro ) { } # $find_embedded_macros() finds the locations of directly (outer only) embedded macros # $ranges will be returned as an array of [ begin, end + 1 ] entries and should be passed by reference # the return value is the location of the end of the current macro + 1 or a string error message # the macro body should always have a "[/show_macro]" terminator $find_embedded_macros = NULL; $find_embedded_macros = function( $macro, &$ranges ) use ( &$find_embedded_macros, $options ) { # TODO: shortcode can have aliases $ranges = [ ]; $i = 0; while ( TRUE ) { $j = strpos( substr( $macro, $i ), "[{$options->shortcode_name}" ); $k = strpos( substr( $macro, $i ), "[/{$options->shortcode_name}" ); $current_shortcode = $options->shortcode_name; if ( $options->shortcode_name_alias ) { # the alias may also have been used so check for it and use it if it occurs earlier $j1 = strpos( substr( $macro, $i ), "[{$options->shortcode_name_alias}" ); if ( $j1 !== FALSE && ( $j === FALSE || $j1 < $j ) ) { $j = $j1; $current_shortcode = $options->shortcode_name_alias; } $k1 = strpos( substr( $macro, $i ), "[/{$options->shortcode_name_alias}" ); if ( $k1 !== FALSE && ( $k === FALSE || $k1 < $k ) ) { $k = $k1; } } $current_shortcode_len = strlen( $current_shortcode ); if ( $k !== FALSE && ( $j === FALSE || $k < $j ) ) { # this '[/show_macro]' terminates the current macro so # this should be the exit for all properly constructed macros, # i.e., done with a matching start '[show_macro]' and end '[/show_macro]' shortcodes return $i + $k; } if ( $j !== FALSE && ( $k !== FALSE && $j < $k ) ) { # find closing ']' - N.B. no valid macro parameter value should have a an embedded ']' $j = strpos( substr( $macro, $i + $j + $current_shortcode_len + 1 ), ']' ) + $i + $j + $current_shortcode_len + 2; # found start of inner [show_macro]; ranges of inner macros are not returned $inner_ranges; $m = $find_embedded_macros( substr( $macro, $j ), $inner_ranges ); if ( is_string( $m ) ) { # error detected so just return error message return $m; } $ranges[] = [ $j, $j + $m ]; $i = $j + $m + strlen( $current_shortcode ) + 3; # really 3 or 4 but it doesn't really matter continue; } # exit here if no matching '[/show_macro]' was found # this should not happen with matching start '[show_macro]' and end '[/show_macro]' shortcodes # nested shortcodes must be of the form "[show_macro]...[show_macro1]...[/show_macro1]...[/show_macro]" # since WordPress cannot correctly parse nested identical shortcodes return <<{$options->shortcode_name} error: missing "[/{$options->shortcode_name}]". N.B., nested templates must use the form " ... [{$options->shortcode_name}] ... [{$options->shortcode_name}1] ... [/{$options->shortcode_name}1] ... [/{$options->shortcode_name}] ... " since WordPress cannot correctly parse nested identical shortcodes. EOD; } }; # $get_custom_field() returns the value(s) of the custom field $field passed through any specified filters. # You can modify or replace $get_custom_field() to suit your particular custom fields. # $get_custom_field() should implement the psuedo filters: 'field_name', 'count', 'indexes' # and the array dereference filter '[n]' where n is a non-negative integer, e.g., [7] if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { $get_custom_field = function( $field, $as_array = FALSE ) use ( $options ) { # check if there is an @filter suffix $filter = ''; if ( $at = strpos( $field, $options->filter ) ) { $filter = substr( $field, $at + 1 ); $field = substr( $field, 0, $at ); } $show_custom_field_tag = mf2tk\get_tags( )[ 'show_custom_field' ]; if ( $as_array ) { # handle the value of multi-valued fields as separate distinct multiple values return explode( '!@#$%', do_shortcode( <<use_native_mode ) { $get_custom_field = function( $field_specifier, $as_array = FALSE ) use ( $options, &$error ) { global $post; $post_id = $post->ID; $fields = explode( $options->post_member, $field_specifier ); $last = count( $fields ) - 1; foreach ( $fields as $i => $field ) { # check if there is an @filter suffix $filter = ''; if ( $at = strpos( $field, $options->filter ) ) { $filter = substr( $field, $at + 1 ); $field = substr( $field, 0, $at ); } if ( $filter === 'field_name' ) { return $as_array ? [ $field ] : $field; } $value = []; $meta = get_post_meta( $post_id, $field ); if ( !$meta ) { $error = <<Error: custom field "$field" does not exists. EOD; return $as_array ? [ ] : ''; } foreach ( $meta as $v ) { # handle custom field values that are arrays. get_post_meta() can return # 1) an array of scalar values, # 2) a single element array whose only element is an array of scalars # 3) anything else # this really handles only 1) and 2) well. 3) returns a merged array with ambiguous indexing. $failed = FALSE; if ( is_scalar( $v ) ) { $value[ ] = $v; } else if ( is_array( $v ) ) { if ( array_reduce( $v, function( $c, $i ) { return $c && is_scalar( $i ); }, TRUE ) ) { $value = array_merge( $value, $v ); } else { $failed = TRUE; } } else { $failed = TRUE; } if ( $failed ) { $error = <<Error: The value of the custom field "$field" is not a scalar or an array of scalars. EOD; return $as_array ? [ ] : ''; } } # check if the filter is a request for count of values if ( $filter === 'count' ) { return $as_array ? [ count( $value ) ] : count( $value ); } # check if the filter is a request for indexes if ( $filter === 'indexes' || $filter === 'indices' ) { $count = count( $value ); $value = []; for ( $i = 0; $i < $count; $i++ ) { $value[] = (string) $i; } return $as_array ? $value : implode( ',', $value ); } if ( $filter ) { # the filter may be an '@' separated sequence of filter function names $filters = explode ( $options->filter, $filter ); $value = array_map( function( $v ) use ( $field, $filters, $options, &$error ) { foreach ( $filters as $f ) { if ( function_exists( $f ) ) { $v = call_user_func( $f, $field, $v ); } else if ( !preg_match( '/^<(\d+)>$/', $f ) ) { $error = <<Error: $f is an invalid filter for custom field "$field". EOD; return ''; } } return $v; }, $value ); if ( $error ) { return $as_array ? [ ] : ''; } # check if the last filter is an array dereference if ( preg_match( '/^<(\d+)>$/', $filters[ count( $filter ) - 1 ], $matches ) === 1 ) { if ( !array_key_exists( $matches[1], $value ) ) { $error = <<Error: $matches[1] is an invalid index for custom field "$field". EOD; return $as_array ? [ ] : ''; } $value = [ $value[ $matches[1] ] ]; } } # everything but the last field should be post id if ( $i !== $last ) { if ( count( $value ) !== 1 ) { $error = <<Error: The value of custom field "$field" is not a single value. EOD; return $as_array ? [ ] : ''; } $post_id = ( integer ) $value[0]; if ( !$post_id ) { $error = <<Error: The value of custom field "$field" is not a post id. EOD; return $as_array ? [ ] : ''; } continue; } if ( $as_array ) { return $value; } return implode( $options->separator, $value ); } }; } # } else { # if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { # $do_macro implements the [show_macro] shortcode $mf2tk_the_do_macro = $do_macro = function( $atts, $macro ) use ( &$do_macro, $find_embedded_macros, $get_custom_field, $options, &$error, $TPCTI_MF2_ACTIVE ) { global $post; global $wpdb; static $saved_inline_macros = [ ]; $error = NULL; if ( $TPCTI_MF2_ACTIVE ) { $mf_table_custom_groups = MF_TABLE_CUSTOM_GROUPS; # MF2 only $mf_table_custom_fields = MF_TABLE_CUSTOM_FIELDS; # MF2 only $mf_table_post_meta = MF_TABLE_POST_META; # MF2 only } # if ( $TPCTI_MF2_ACTIVE ) { if ( !$atts ) { $atts = [ ]; } if ( $macro ) { $macro = ltrim( $macro ); } # handle posts iterations #if ( $macro ) { $macro = htmlspecialchars_decode( $macro ); } # first check if the macro invocation has a post parameter of the form a comma separated list of post ids # or related_type fields or alt_related_type fields if ( is_array( $atts ) && array_key_exists( 'post', $atts ) ) { $att_post = $atts['post']; unset( $atts['post'] ); $result = ''; foreach ( explode( ';', $att_post ) as $post_id ) { if ( !is_numeric( $post_id ) ) { # post parameter is a related_type field or alt_related_type field # MF2 only - filter is if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { $post_ids = $get_custom_field( $post_id . "@tk_filter_by_type__related_type__alt_related_type", TRUE ); } else { # if ( $TPCTI_MF2_ACTIVE ) { $post_ids = $get_custom_field( $post_id, TRUE ); } # if ( $TPCTI_MF2_ACTIVE ) { if ( $error ) { return $error; } } else { $post_ids = [ $post_id ]; } $save_post = $post; foreach ( $post_ids as $post_id1 ) { if ( !$post_id1 ) { continue; } $post = get_post( $post_id1 ); $result .= $do_macro( $atts, $macro ); } $post = $save_post; } return $result; } if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { # do the Magic Fields 2 group or field iterators # next check if the macro invocation has an iterator parameter of the format # group_iterator="iterator_name:group_name" or field_iterator="iterator_name:field_name" # only one iterator parameter is allowed per macro invocation but macros can be nested to allow nested iterations if ( is_array( $atts ) && ( $group = array_key_exists( 'group_iterator', $atts ) or $field = array_key_exists( 'field_iterator', $atts ) ) ) { if ( $group ) { # find the group indexes list( $iterator_name, $group_name ) = explode( ':', $atts['group_iterator'] ); unset( $atts['group_iterator'] ); $indexes = $wpdb->get_col( $wpdb->prepare( <<ID GROUP BY m.group_count ORDER BY m.group_count EOD , $group_name ) ); } else if ( $field ) { # find the field indexes; * for group index means over all groups if ( preg_match( '/^(\w+):(\w+)(<(\*|\d+)>)?$/', $atts['field_iterator'], $matches ) ) { unset( $atts['field_iterator'] ); if ( array_key_exists( 4, $matches ) and $matches[4] !== '*' ) { $indexes = $wpdb->get_col( $wpdb->prepare( <<ID AND m.group_count = %d ORDER BY m.field_count EOD , $matches[2], $matches[4] ) ); } else { $indexes = $wpdb->get_col( $wpdb->prepare( <<ID GROUP BY m.field_count ORDER BY m.field_count EOD , $matches[2] ) ); } $iterator_name = $matches[1]; } else { } } /* if ( empty( $indexes ) ) { return <<Error: invalid group_iterator or field_iterator parameter EOD; } */ # do the iteration over either the group or field index values $result = ''; foreach ( $indexes as $index ) { $atts[$iterator_name] = $index; $result .= $do_macro( $atts, $macro ); } return $result; } } # if ( $TPCTI_MF2_ACTIVE ) { # do the generic iterators # finally check for generic iterator parameter - iterator="name:12345;"abcde";'abcde';alpha<1,1>" # "it" is accepted as an abbreviation for "iterator" if ( is_array( $atts ) && ( ( $iterator = array_key_exists( 'iterator', $atts ) ) || array_key_exists( 'it', $atts ) ) ) { if ( ( $ret = preg_match( '#^(\w+):((\s*(\d+|"[^"]*"|\'[^\']*\'|[^;]+)(;|$))+)#', $iterator ? $atts[ 'iterator' ] : $atts[ 'it' ], $matches ) ) === 1 ) { unset( $atts[ 'iterator' ], $atts[ 'it' ] ); $iterator_name = $matches[1]; if ( ( $ret = preg_match_all( '#(\s*(\d+)|\s*"([^"]*)"|\s*\'([^\']*)\'|\s*([^\s;]+))(;|$)#', $matches[2], $matches1, PREG_SET_ORDER ) ) !== false && $ret !== 0 ) { $iterator_values = []; foreach( $matches1 as $matches2 ) { if ( $matches2[2] ) { # number $iterator_values[] = $matches2[2]; } else if ( $matches2[3] ) { # double quoted string $iterator_values[] = $matches2[3]; } else if ( $matches2[4] ) { # single quoted string $iterator_values[] = $matches2[4]; } else if ( $matches2[5] ) { # custom field specifier $custom_field = $matches2[5]; $iterator_values = array_merge( $iterator_values, $get_custom_field( $custom_field, TRUE ) ); if ( $error ) { return $error; } } } } else { return <<{$options->shortcode_name} error: invalid iterator parameter. EOD; } } else { return <<{$options->shortcode_name} error: invalid iterator parameter. EOD; } # finally do the macro for each iteration value and concatenate the results $result = ''; foreach ( $iterator_values as $iterator_value ) { if ( !$iterator_value && $iterator_value !== "0" ) { continue; } $atts[$iterator_name] = $iterator_value; $result .= $do_macro( $atts, $macro ); } return $result; } # get the template definition if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { $name = 'macro'; } else { # if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { $name = 'name'; } # } else { # if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { if ( !$macro ) { if ( !empty( $atts[ $name ] ) ) { if ( !empty( $saved_inline_macros[ $atts[ $name ] ] ) ) { # saved inline macro definitions have priority over Content Macro definitions $macro = $saved_inline_macros[ $atts[ $name ] ]; } else { # get macro definition from MySQL table $macro = $wpdb->get_var( $wpdb->prepare( "SELECT post_content from $wpdb->posts WHERE post_type = %s AND post_name = %s", $options->content_macro_post_type, $atts[ $name ] ) ); if ( !$macro ) { return <<{$options->shortcode_name} error: {$atts[ $name ]} is not the slug of a Post Content Template. EOD; } } } else { return <<{$options->shortcode_name} error: no post content template specified. EOD; } } else { # There is an inline macro definition if ( !empty( $atts['save_inline_macro_as'] ) ) { # save inline macro definition for later use in the same session $saved_inline_macros[$atts['save_inline_macro_as']] = $macro; if ( array_key_exists( 'save_only_no_action', $atts ) ) { # this is a macro definition only - to be invoked in a later show_macro shortcode return; } } } unset( $atts[ $name ] ); # do the variable assignments # scan for defaults of the form or # or or (MF2) if ( preg_match_all( '/\r?\n?/', $macro, $assignments, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ) ) { # find locations of inner macros # add "[/show_macro]" terminator since $find_embedded_macros() requires that macro body be terminated $inner_macro_ranges = NULL; $status = $find_embedded_macros( $macro . "[/{$options->shortcode_name}]", $inner_macro_ranges ); if ( is_string( $status ) ) { # error detected so just return error message return $status; } foreach ( array_reverse( $assignments ) as $assignment ) { # skip assignments inside inner macros foreach ( $inner_macro_ranges as $range ) { if ( $assignment[0][1] >= $range[0] && $assignment[0][1] < $range[1] ) { continue 2; } } # do the assignment if ( !array_key_exists( $assignment[1][0], $atts ) ) { if ( array_key_exists( 7, $assignment ) ) { # assignment is to value of custom field $custom_field = $assignment[7][0]; $custom_field = preg_replace_callback( '/\$#(\w+)#/', function( $m ) use ( $atts, &$error ) { if ( array_key_exists( $m[1], $atts ) ) { return $atts[ $m[1] ]; } else { $error = '
' . "template variable {$m[1]} is not assigned a value.
"; } return $m[0]; }, $custom_field ); if ( $error ) { return $error; } $atts[ $assignment[1][0] ] = $get_custom_field( $custom_field ); if ( $error ) { return $error; } } else { # assignment is to a string constant $atts[ $assignment[1][0] ] = preg_replace_callback( '/\$#(\w+)#/', function( $m ) use ( $atts, &$error ) { if ( array_key_exists( $m[1], $atts ) ) { return $atts[ $m[1] ]; } else { $error = '
' . "template variable {$m[1]} is not assigned a value.
"; } return $m[0]; }, trim( $assignment[2][0], '"\'' ) ); if ( $error ) { return $error; } } } else { } # remove this variable assigment from source $macro = substr_replace( $macro, '', $assignment[0][1], strlen( $assignment[0][0] ) ); } # foreach ( array_reverse( $assignments ) as $assignment ) { } # handle conditional text inclusion # find locations of inner macros # add "[/show_macro]" terminator since $find_embedded_macros() requires that macro body be terminated $inner_macro_ranges = NULL; $status = $find_embedded_macros( $macro . "[/{$options->shortcode_name}]", $inner_macro_ranges ); if ( is_string( $status ) ) { # error detected so just return error message return $status; } # if statement is #if($#alpha#)# or #if($#alpha#=$#beta#)# or #if($#alpha#="gamma")# or #if($#alpha#='delta')# $if_count = preg_match_all( '/\r?\n?#if\(\s*\$#([\w-]+)#(\s*=\s*((\$#([\w-]+)#)|(("|\'|‘|’|“|”|′|″)(.*?)("|\'|‘|’|“|”|′|″))))?\s*\)#\r?\n?/', $macro, $if_matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ); $end_count = preg_match_all( '/\r?\n?#endif#\r?\n?/', $macro, $end_matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ); if ( $if_count !== $end_count ) { return <<{$options->shortcode_name} error: count of "#if(...)# not equal count of "#endif#" EOD; } if ( $inner_macro_ranges && $if_count ) { # first remove #if(...)# ... #endif# matches found in embedded inner macros $matches = [ $if_matches, $end_matches ]; foreach ( $matches as &$m ) { $m = array_merge( array_filter( array_map( function( $m ) use ( $inner_macro_ranges ) { foreach ( $inner_macro_ranges as $r ) { if ( $m[0][1] >= $r[0] && $m[0][1] < $r[1] ) { return FALSE; } } return $m; }, $m ) ) ); } unset( $m ); $if_matches = $matches[0]; $end_matches = $matches[1]; if ( ( $if_count = count( $if_matches ) ) !== ( $end_count = count( $end_matches ) ) ) { return <<{$options->shortcode_name} error: count of "#if(...)# not equal count of "#endif#" EOD; } } if ( $if_count ) { # do conditional text inclusion/exclusion $includes = array_map( function( $match ) use ( $atts ) { if ( !array_key_exists( $match[1][0], $atts ) ) { return false; } $value = $atts[$match[1][0]]; if ( array_key_exists( 6, $match ) ) { # #if($#alpha#='gamma')# # #if($#alpha#="gamma")# return $value === $match[8][0]; } else if ( array_key_exists( 4, $match ) ) { # #if($#alpha#=$#beta#)# if ( array_key_exists( $match[5][0], $atts ) ) { return $atts[$match[5][0]] === $value; } return $value === ''; } else if ( !array_key_exists( 2, $match ) ) { # #if($#alpha#)# return !is_null( $value ) && $value !== ''; } }, $if_matches ); $i = 0; while ( $if_matches && $end_matches ) { # find if that matches the first endif while ( $if_matches[$i][0][1] < $end_matches[0][0][1] ) { if ( ++$i == count( $if_matches ) ) { break; } } if ( --$i < 0 ) { # error return <<{$options->shortcode_name} error: unmatched "#endif" EOD; } $include = $includes[$i]; $start0 = $if_matches[$i][0][1]; $length0 = ( $end_matches[0][0][1] + strlen( $end_matches[0][0][0] ) ) - $start0; $start1 = $if_matches[$i][0][1] + strlen( $if_matches[$i][0][0] ); $length1 = $end_matches[0][0][1] - $start1; if ( preg_match_all( '/\r?\n?#else#\r?\n?/', substr( $macro, $start1 ), $else_matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ) ) { $the_else_match = NULL; foreach ( $else_matches as $else_match ) { $inner_macro_else = FALSE; if ( $inner_macro_ranges ) { foreach ( $inner_macro_ranges as $r ) { if ( $else_match[0][1] + $start1 >= $r[0] && $else_match[0][1] + $start1 < $r[1] ) { $inner_macro_else = TRUE; break; } } } if ( !$inner_macro_else ) { $the_else_match = $else_match; break; } } if ( $the_else_match ) { $start2 = $the_else_match[0][1] + $start1; $length1 = $start2 - $start1; $start2 += strlen( $the_else_match[0][0] ); $length2 = $end_matches[0][0][1] - $start2; } else { $start2 = $start1; # irrelevant since $length2 == 0 $length2 = 0; } } else { $start2 = $start1; # irrelevant since $length2 == 0 $length2 = 0; } if ( $include ) { # replace with #if($#...#)# clause $macro = substr_replace( $macro, substr( $macro, $start1, $length1 ), $start0, $length0 ); $offset = $length1 - $length0; } else { # replace with #else# clause $macro = substr_replace( $macro, substr( $macro, $start2, $length2 ), $start0, $length0 ); $offset = $length2 - $length0; } # remove the matched if array_splice( $if_matches, $i, 1 ); array_splice( $includes, $i, 1 ); # adjust offsets after text replacement for ( $j = $i; $j < count( $if_matches ); ++$j ) { $if_matches[$j][0][1] += $offset; $if_matches[$j][1][1] += $offset; } if ( $i ) { --$i; } #remove the matched endif array_shift( $end_matches ); # adjust offsets after text replacement for ( $j = 0; $j < count( $end_matches ); ++$j ) { $end_matches[$j][0][1] += $offset; } } # while ( $if_matches && $end_matches ) { if ( $if_matches || $end_matches ) { # error return 'show_macro:Error: unmatched "#if" or "#endif"'; } } # if ( $if_count ) { # do variable substitutions # find all variable locations if ( preg_match_all( '/\$#(\w+)#/', $macro, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ) ) { # find locations of inner macros # add "[/show_macro]" terminator since $find_embedded_macros() requires that macro body be terminated $inner_macro_ranges = NULL; $status = $find_embedded_macros( $macro . "[/{$options->shortcode_name}]", $inner_macro_ranges ); if ( is_string( $status ) ) { # error detected so just return error message return $status; } # remove variable matches that are inside inner macros $matches = array_filter( array_map( function( $match ) use ( $inner_macro_ranges ) { foreach ( $inner_macro_ranges as $range ) { if ( $match[0][1] >= $range[0] && $match[0][1] < $range[1] ) { return FALSE; } } return $match; }, $matches ) ); # do variable substitutions on remaining matches in source foreach ( array_reverse( $matches ) as $match ) { if ( array_key_exists( $match[1][0], $atts ) ) { $macro = substr_replace( $macro, $atts[$match[1][0]], $match[0][1], strlen( $match[0][0] ) ); } else { return '
' . "template variable {$match[1][0]} is not assigned a value.
"; } } } # if ( preg_match_all( '/\$#(\w+)#/', $macro, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ) ) { # finally do macro replacements $macro = do_shortcode( $macro ); return $macro; }; # $do_macro = function( $atts, $macro ) { # add the shortcodes - show_macro1, ... show_macro9 are needed to handle nested macros # as WordPress cannot correctly parse [show_macro][show_macro][/show_macro][/show_macro] # this needs to be written as [show_macro][show_macro1][/show_macro1][/show_macro] add_shortcode( $options->shortcode_name, $do_macro ); if ( !empty( $options->shortcode_name_alias ) ) { add_shortcode( $options->shortcode_name_alias, $do_macro ); } for ( $i = 1; $i < 9; $i++ ) { add_shortcode( $options->shortcode_name . $i, $do_macro ); if ( !empty( $options->shortcode_name_alias ) ) { add_shortcode( $options->shortcode_name_alias . $i, $do_macro ); } } # output settings page add_action( 'admin_init', function( ) use ( $TPCTI_MF2_ACTIVE, $options ) { add_settings_section( 'tpcti-options', 'A Tiny Post Content Template Interpreter', function( ) { ?> The defaults should work for almost all websites but you may want to change some of them in case of conflicts or for convenience, i.e., to reduce typing. The documentation for this plugin is here.
Shortcode Name: global so choose carefully
Shortcode Name Alias: global so choose carefully
Post Type: save templates as this post type - maximum 20 characters
Filter Prefix: precedes filter name in custom field specifier
Multi Valued Separator: use this to separate the values in a multi-valued field
Post Member Operator: post_member}\""; if ( $TPCTI_MF2_ACTIVE && !$options->use_native_mode ) { echo 'disabled'; } else { # if ( $TPCTI_MF2_ACTIVE ) { } # if ( $TPCTI_MF2_ACTIVE ) { ?>> left of operator is a post id and right of operator is a custom field in that post
Use TPCTI Native Mode: use_native_mode ) { echo 'checked'; } ?>> not compatible with version 0.5.8 and not recommended
'; settings_fields( 'tpcti_options' ); do_settings_sections( 'tpcti-settings-page' ); submit_button( ); echo ''; } ); } ); remove_filter( 'the_content', 'wpautop' ); }; # $construct = function( ) use ( &$mf2tk_the_do_macro ) { $construct( ); } #if ( $TPCTI_MF2_ACTIVE ) { namespace mf2tk { const TPCTI_VERSION = 1.0; if ( $TPCTI_MF2_ACTIVE ) { function do_macro( $atts, $macro ) { global $mf2tk_the_do_macro; require_once( MF_PATH . '/mf_front_end.php' ); $macro = stripslashes( $macro ); $result = $mf2tk_the_do_macro( $atts, $macro ); return $result; } } # if ( $TPCTI_MF2_ACTIVE ) { } #} # if ( $TPCTI_MF2_ACTIVE ) { ?>