SEO-friendly URLs for three taxonomies and a post type at the same time
Another note with code that is useless for 99% of WordPress users. But for 1% it will probably be a find. This is about SEO-friendly URLs, solving a difficult task. There are no explanations of what and how in this note, because it’s like explaining a joke — either you understand or you don’t...
The person who contacted me with this task tried several popular plugins first, which could not handle the task: Gd custom tools (paid), Custom Post Type Permalinks (free). Seeing what needed to be done, I wasn’t surprised — it’s difficult...
Task
Create a post type and three taxonomies for it:
realty— Post type: listings.country— Multilevel taxonomy: country > city (Spain > Valencia).type_deal— Single-level taxonomy. Contains only 2 terms:saleandrent(sale, rent).type_realty— Multilevel taxonomy: type of real estate (houses).
Create an arbitrary URL structure (permalinks) for the taxonomies and the post type.
For the taxonomies, links should be of the form:
-
dom/ estate / %country% / -
dom/ estate / %country% / %type_deal% / dom/ estate / %country% / %type_deal% / %type_realty% /
For the post type (listings):
dom/ estate / %country% / %type_deal% / %type_realty% / id-%post_id% /
The idea is to make the permalinks of the taxonomies and the post type look the same. So you could manually remove the last elements from the URL and land at a higher level.
Examples of what should appear in URLs
For all three taxonomies:
-
dom/estate/spain/- Real estate listings in Spain -
dom/estate/spain/sale/- Buy real estate in Spain -
dom/estate/spain/sale/houses/- Buy a house in Spain -
dom/estate/spain/valencia/sale/houses/- Buy a house in Spain > Valencia -
dom/estate/spain/valencia/sale-rent/- Sale and rental of all real estate in Valencia. It might look likedom/estate/spain/valencia/ -
dom/estate/spain/valencia/sale-rent/kommercheskaya/magazin/- Sale and rental of shops in Valencia
For each URL the taxonomy should have pagination .../page/2/, .../page/3/
For the post type:
All the same as for the term, but at the end also the post ID:
dom/estate/spain/valencia/sale/houses/id-123/. 123 - post ID
Solution
Create a file in the theme lib/register-ads-post-tax.php and include it in the theme’s functions.php:
require_once TEMPLATEPATH .'/lib/register-ads-post-tax.php';
Code of the file:
<?php
## registration of post type and taxonomies
add_action('init', 'estate_type_register');
function estate_type_register(){
// post_type realty
register_post_type('realty', array(
'description' => 'Listings of real estate abroad for sale.',
'labels' => array(
'name' => 'Listings',
'singular_name' => 'Listing',
'add_new' => 'Add new',
'add_new_item' => 'Add listing',
'edit_item' => 'Edit listing',
'new_item' => 'New listing',
'view_item' => 'View listings',
'search_items' => 'Search listings',
'not_found' => 'No listings found',
'not_found_in_trash' => 'No listings found in trash',
'parent_item_colon' => 'Parent Listing:',
'all_items' => 'All listings',
'menu_name' => 'Listings'
),
'taxonomies' => array( 'country', 'type_realty', 'type_deal' ),
'public' => true,
'query_var' => true,
'rewrite' => false,
//'rewrite' => true,
//'rewrite' => array( 'slug'=>'estate/%country%/%post_id%', 'with_front'=>false, 'feeds'=>false, 'pages'=>false, 'feed'=>false, 'paged'=>false ),
'show_in_menu' => true,
'has_archive' => false,
'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' ),
'show_ui' => true,
'show_in_nav_menus' => false,
));
## Adjusts the link to taxonomy terms - 'type_deal' and 'type_realty', which is obtained by get_term_link()
## The country is taken from the URL of the current page
add_filter('term_link', 'deal_realty_tax_link_fix', 10, 3);
function deal_realty_tax_link_fix( $link, $term, $taxonomy ){
if( ! in_array( $taxonomy, array('type_deal','type_realty') ) ) return $link;
$uri = $_SERVER['REQUEST_URI'];
if( $taxonomy == 'type_deal' ){
$link = preg_replace('~^(.*?/)(?:'. implode('|', __type_deal_elements()) .')/.*~', '\1', $uri ) . $term->slug;
}
if( $taxonomy == 'type_realty' ){
$link = preg_replace('~^(.*?/(?:'. implode('|', __type_deal_elements()) .')/).*~', '\1', $uri ) . __build_tax_uri( $term );
}
return esc_url( user_trailingslashit( home_url($link) ) );
}
// tricks with the link structure
if(1){
//add_action('wp', 'xxxxxxx'); // debug
function xxxxxxx($wp){
print_r( get_queried_object() );
print_r( $wp );
print_r( $GLOBALS['wp_rewrite'] );
exit;
}
// register the structure via add_permastruct, so the URL structure works for the post type
add_permastruct('realty', 'estate/%country%/%type_deal%/%type_realty%/id-%post_id%', array(
'with_front' => 0,
'paged' => 0,
'feed' => 0,
'walk_dirs' => 0,
));
/*
add_filter( 'query_vars', function( $vars ){
$vars[] = 'type_deal';
$vars[] = 'type_realty';
return $vars;
} );
*/
// fix request parameters - remove 'country' and add 'post_type=realty' because '?p=208' does not work, but '?p=208&post_type=realty' does
//$this->query_vars = apply_filters( 'request', $this->query_vars );
add_action('request', 'remove_unwanted_query_vars' );
function remove_unwanted_query_vars( $vars ){
if( !empty($vars['country']) ){
$_country = explode('/', $vars['country']);
$vars['country'] = end( $_country ); // last element
}
if( !empty($vars['type_realty']) ){
$_type_realty = explode('/', $vars['type_realty']);
$vars['type_realty'] = end( $_type_realty ); // last element
}
// query for post type 'realty' - parameters: country, type_realty, type_deal
if( isset($vars['p'], $vars['type_realty']) && $vars['post_type'] === 'realty' ){
$deal = __type_deal_elements();
if( $vars['type_deal'] == end($deal)/*'sale-rent'*/ )
unset( $vars['type_deal'] );
}
return $vars;
}
// add rewrite rules
//add_rewrite_rule('^estate/.+?/(?:sale|rent|sale-rent)/.+?/([0-9]+)/?', 'index.php?p=$matches[1]&post_type=realty', 'top' );
// remove all created rewrite rules and create new ones...
// $rules = apply_filters( $permastructname . '_rewrite_rules', $rules );
// do_action_ref_array( 'generate_rewrite_rules', array( &$this ) );
add_filter('realty'.'_rewrite_rules', 'delete_realty_rewrite_rules' );
function delete_realty_rewrite_rules( $rules ){
/* array(
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)/embed/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&embed=true
[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)/trackback/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&tb=1
[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)(?:/([0-9]+))?/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&page=$matches[5]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/?$] => index.php?attachment=$matches[1]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
) */
// must be before country rules
$rules = array(
'estate/(.+?)/('. implode('|', __type_deal_elements() ) /*sale|rent|sale-rent*/ .')/(.+?)/id-([0-9]+)/?$' => 'index.php?post_type=realty&p=$matches[4]&country=$matches[1]&type_deal=$matches[2]&type_realty=$matches[3]',
);
return $rules;
}
## add rewrite rules for 'country', 'type_deal', 'type_realty'
add_filter('country'.'_rewrite_rules', 'add_more_country_rewrite_rules' );
function add_more_country_rewrite_rules( $rules ){
/*Array(
[estate/(.+?)/page/?([0-9]{1,})/?$] => index.php?country=$matches[1]&paged=$matches[2]
[estate/(.+?)/?$] => index.php?country=$matches[1]
)*/
// should be after post type rules
$_first_part = 'estate/(.+?)/('. implode('|', __type_deal_elements() ) /*sale|rent|sale-rent*/ .')';
$_pade_part = 'page/?([0-9]{1,})';
$more_riles = array(
"$_first_part/(.+?)/$_pade_part/?$" => 'index.php?country=$matches[1]&type_deal=$matches[2]&type_realty=$matches[3]&paged=$matches[4]',
"$_first_part/$_pade_part/?$" => 'index.php?country=$matches[1]&type_deal=$matches[2]&paged=$matches[3]',
"$_first_part/(.+?)/?$" => 'index.php?country=$matches[1]&type_deal=$matches[2]&type_realty=$matches[3]',
"$_first_part/?$" => 'index.php?country=$matches[1]&type_deal=$matches[2]',
);
$rules = array_merge( $more_riles, $rules );
//die( print_r($rules) );
return $rules;
}
// array( sale, rent, sale-rent )
function __type_deal_elements(){
$deal_terms = get_terms(array( 'taxonomy'=>'type_deal','hide_empty'=>0, 'fields'=>'id=>slug' ));
$deal_terms[] = implode('-', $deal_terms); // sale-rent
return $deal_terms;
}
/*
add_action('generate_rewrite_rules', 'delete_ads_rewrite_rules' );
function delete_ads_rewrite_rules( $that ){
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)/embed/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&embed=true
[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)/trackback/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&tb=1
[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)(?:/([0-9]+))?/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&page=$matches[5]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/?$] => index.php?attachment=$matches[1]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
foreach( $that->extra_rules_top as $rule => $_ ) if( preg_match('~^estate/.+type_deal~', $rule) ) unset($that->extra_rules_top[$rule]);
foreach( $that->rules as $rule => $_ ) if( preg_match('~^estate/.+type_deal~', $rule) ) unset($that->rules[$rule]);
}
*/
// add post_type to request vars, because simply '?p=208' somehow does not work, but '?p=208&post_type=realty' does
//do_action_ref_array( 'parse_request', array( &$this ) );
/*
add_action('parse_request', function($wp){
if( strpos($wp->matched_query, 'foo_') ) $wp->query_vars['post_type'] = 'realty';
} );
// additional rewrite tag, to ignore
add_rewrite_tag('%fake_country%', '(.+?)', '&adsfoovar_='); // adsfoovar_ - non-existent query var, it will be removed... it's not needed...
// replace %realty% (%post_name%) with ID
//global $wp_rewrite;
//$wp_rewrite->extra_permastructs['realty'] = str_replace('%realty%', '%post_id%.html', $wp_rewrite->extra_permastructs['realty']);
// Remove trailing slash if needed. If there is a trailing slash in the URL structure, it will be added to permanent pages as well.
//add_filter('user_trailingslashit', 'no_ads_trailslash', 170, 2);
function no_ads_trailslash( $string, $type ){
global $wp_rewrite;
if( $type == 'realty' && $wp_rewrite->use_trailing_slashes && $wp_rewrite->using_permalinks() )
return untrailingslashit($string);
return $string;
}
*/
## Filter the URL of the arbitrary post type
add_filter('post_type_link', 'realty_permalink', 1, 2);
function realty_permalink( $permalink, $post ){
if( false === strpos($permalink, '%country%') ) return $permalink;
//global $wp_rewrite;
//$perm = $wp_rewrite->get_extra_permastruct('realty');
// Get the elements of taxonomy 'country'
$country_path = __build_tax_uri( get_the_terms( $post, 'country') ); // multiple levels - asd/asd
// type_realty_path
$type_realty_path = __build_tax_uri( get_the_terms( $post, 'type_realty') ); // multiple levels - asd/asd
$type_deal_path = __build_tax_uri( get_the_terms( $post, 'type_deal') ); // single level - asd
return strtr( $permalink, array(
'%type_deal%' => $type_deal_path,
'%type_realty%' => $type_realty_path,
'%country%' => $country_path,
'%post_id%' => $post->ID,
) );
}
## Gets the chain of labels for a given term - at the top level there are no parents - 'parent/child/child'
function __build_tax_uri( $terms ){
if( is_wp_error($terms) || empty($terms) || ! ( is_object(reset($terms)) || is_object($terms) ) )
return 'no_terms'; // no term in taxonomy, but it should...
$term = is_object(reset($terms)) ? reset($terms) : $terms;
$path = array( $term->slug );
while( $term->parent ){
$term = get_term( $term->parent );
$path[] = $term->slug;
}
return implode('/', array_reverse($path) );
}
## 404 page if wrong taxonomy names are in the URL.
## In fact, WP will then 301 redirect to the correct URL...
add_filter('pre_handle_404', 'realty_404_test' );
function realty_404_test( $false ){
if( ! get_queried_object() ) return $false; // nothing to do...
$_404 = false;
// post listing
if( is_singular('realty') ){
$post = get_queried_object();
// type_deal
if( ! $_404 && ( $term_name = get_query_var('type_deal') ) && ! has_term( explode('-', $term_name), 'type_deal', $post ) )
$_404 = 1;
// type_realty
if( ! $_404 && ( $term_name = get_query_var('type_realty') ) && ! has_term( $term_name, 'type_realty', $post ) )
$_404 = 1;
// country
if( ! $_404 && ( $term_name = get_query_var('country') ) && ! has_term( $term_name, 'country', $post ) )
$_404 = 1;
}
// for taxonomies
if( is_tax(['type_deal','type_realty','country']) ){
// type_deal
if( ! $_404 && ( $term_name = get_query_var('type_deal') ) && ! get_term_by('slug', $term_name, 'type_deal') )
$_404 = 1;
// type_realty
if( ! $_404 && ( $term_name = get_query_var('type_realty') ) && ! get_term_by('slug', $term_name, 'type_realty') )
$_404 = 1;
// country
if( ! $_404 && ( $term_name = get_query_var('country') ) && ! get_term_by('slug', $term_name, 'country') )
$_404 = 1;
}
if( $_404 ){
global $wp_query;
$wp_query->set_404();
status_header( 404 );
nocache_headers();
return 1; // stop further checks
}
return $false; // nothing to do...
}
}
## Taxonomy - country city region
register_taxonomy('country', array('realty'), array(
'labels' => array(
'name' => 'Countries',
'singular_name' => 'Country',
'search_items' => 'Search Countries',
'popular_items' => 'Popular Countries',
'all_items' => 'All Countries',
'parent_item' => 'Parent Country',
'parent_item_colon' => 'Parent Country:',
'edit_item' => 'Edit Country',
'view_item' => 'View Country',
'update_item' => 'Update Country',
'add_new_item' => 'Add New Country',
'new_item_name' => 'New Country Name',
'separate_items_with_commas' => 'Separate Countries with commas',
'add_or_remove_items' => 'Add or remove Countries',
'choose_from_most_used' => 'Choose from the most used Countries',
'not_found' => 'No Countries found.',
'menu_name' => 'Countries'
),
'hierarchical' => true,
'rewrite' => array('slug'=>'estate', 'hierarchical'=>true, 'with_front'=>0, 'feed'=>0 ),
'query_var' => true,
'public' => true,
'show_ui' => true,
'show_tagcloud' => true,
'show_admin_column' => false,
'show_in_nav_menus' => true,
));
## Taxonomy - type_realty
register_taxonomy( 'type_realty', array('realty'), array(
'labels' => array(
'name' => 'Property Type',
'singular_name' => 'Property Type',
'search_items' => 'Search Property Types',
'popular_items' => 'Popular Property Types',
'all_items' => 'All Property Types',
'parent_item' => 'Parent Property Type',
'parent_item_colon' => 'Parent Property Type:',
'edit_item' => 'Edit Property Type',
'view_item' => 'View Property Type',
'update_item' => 'Update Property Type',
'add_new_item' => 'Add New Property Type',
'new_item_name' => 'New Property Type Name',
'separate_items_with_commas' => 'Separate Property Types with commas',
'add_or_remove_items' => 'Add or remove Property Types',
'choose_from_most_used' => 'Choose from the most used Property Types',
'not_found' => 'No Property Types found.',
'menu_name' => 'Property Types'
),
'public' => false,
'rewrite' => false,
'hierarchical' => true,
'query_var' => true, // query_var, so term requests work...
'publicly_queryable' => true, // query_var, so term requests work...
'show_ui' => true,
'show_in_quick_edit' => true,
'show_tagcloud' => false,
'show_admin_column' => false,
'show_in_nav_menus' => false,
));
## Taxonomy - type_deal - two types - 'sale' and 'rent'
register_taxonomy('type_deal', array('realty'), array(
'labels' => array(
'name' => 'Deal Type',
'singular_name' => 'Deal Type',
'search_items' => 'Search Deal Type',
'popular_items' => 'Popular Deal Type',
'all_items' => 'All Deal Type',
'parent_item' => 'Parent Deal Type',
'parent_item_colon' => 'Parent Deal Type:',
'edit_item' => 'Edit Deal Type',
'view_item' => 'View Deal Type',
'update_item' => 'Update Deal Type',
'add_new_item' => 'Add New Deal Type',
'new_item_name' => 'New Deal Type Name',
'separate_items_with_commas' => 'Separate Deal Type with commas',
'add_or_remove_items' => 'Add or remove Deal Type',
'choose_from_most_used' => 'Choose from the most used Deal Type',
'not_found' => 'No Deal Type found.',
'menu_name' => 'Deal Type'
),
'public' => false,
'hierarchical' => true, // for easier selection
'query_var' => true, // query_var, so term requests work...
'publicly_queryable' => true, // query_var, so term requests work...
'rewrite' => false,
'show_ui' => true,
'show_in_quick_edit' => true,
'show_tagcloud' => false,
'show_in_nav_menus' => false,
'show_admin_column' => false,
));
}
## do not show the selected term at the top of the checkbox list of terms
add_filter( 'wp_terms_checklist_args', 'set_checked_ontop_default', 10 );
function set_checked_ontop_default( $args ){
// change default parameter to false
if( ! isset($args['checked_ontop']) ) $args['checked_ontop'] = false;
return $args;
}
## Change the page for a particular country
add_filter('template_include', 'top_country_tpl_file');
function top_country_tpl_file( $template ) {
// top-level term page
if( is_tax('country') ){
if( ! get_query_var('type_realty') && ! get_query_var('type_deal') && ($term = get_queried_object()) && ! $term->parent ){
$tpl_file = get_stylesheet_directory() . '/taxonomy-country-toplevel.php';
if( file_exists($tpl_file) ) $template = $tpl_file;
}
}
return $template;
}
// additional hooks for the ad creation page
// modify meta box block
if( is_admin() ){
// admin styles related to the quest post type
add_action('admin_print_footer_scripts', 'country_hook_admin_footer_scripts', 99);
function country_hook_admin_footer_scripts(){
$sc = get_current_screen();
if( $sc->base != 'post' || ! in_array($sc->post_type, ['realty']) )
return;
?>
<style>
.postbox div.tabs-panel{ max-height:700px; border:0; }
.category-tabs{ display:none; }
#countrydiv .inside{ margin:0; padding:0; }
#countrydiv #country-adder{ padding: 0 1em; }
#country-all ul.children{ display:none; }
</style>
<script>
jQuery(document).ready(function($){
var $main = $('#country-all'),
$checklist = $main.find('input[type="checkbox"]');
$checklist.prop('type','radio');
// arrows ▾ ▸
$main.find('label').each(function(){
var $li = $(this).closest('li')
if( $li.find('> .children').length )
$(this).append('<span class="has_ch" style="opacity:.5;">▸</span>');
});
// expand/collapse
$main.on('click', 'input', function(){
var $li = $(this).closest('li'),
$child = $li.find('> .children');
if( $child.length ){
if( $child.is(':visible') ){
$child.slideUp(200);
$child.prev().find('.has_ch').text('▸');
}
else {
$child.slideDown(200);
$child.prev().find('.has_ch').text('▾');
}
}
});
// show the selected internal
var $checked = $checklist.filter(':checked')
if( $checked.length )
$checked.parents('ul.children').show()
});
</script>
<?php
}
}
Explain the code I won’t, it’s long, complex, not much sense, and I’m lazy, sorry...