/* Copyright 2007-2024 John Havlik (email : john.havlik@mtekk.us) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ //Do a PHP version check, require 5.6 or newer if(version_compare(phpversion(), '5.6.0', '<')) { //Only purpose of this function is to echo out the PHP version error function bcn_phpold() { printf('

' . esc_html__('Your PHP version is too old, please upgrade to a newer version. Your version is %1$s, Breadcrumb NavXT requires %2$s', 'breadcrumb-navxt') . '

', phpversion(), '5.6.0'); } //If we are in the admin, let's print a warning then return if(is_admin()) { add_action('admin_notices', 'bcn_phpold'); } return; } require_once(dirname(__FILE__) . '/includes/multibyte_supplicant.php'); //Include admin base class if(!class_exists('\mtekk\adminKit\adminKit')) { require_once(dirname(__FILE__) . '/includes/adminKit/class-mtekk_adminkit.php'); } //Include the breadcrumb class require_once(dirname(__FILE__) . '/class.bcn_breadcrumb.php'); //Include the breadcrumb trail class require_once(dirname(__FILE__) . '/class.bcn_breadcrumb_trail.php'); if(class_exists('WP_Widget')) { //Include the WP 2.8+ widget class require_once(dirname(__FILE__) . '/class.bcn_widget.php'); } use mtekk\adminKit\adminKit as adminKit; use mtekk\adminKit\setting; $breadcrumb_navxt = null; //TODO change to extends \mtekk\plugKit class breadcrumb_navxt { const version = '7.3.0'; protected $name = 'Breadcrumb NavXT'; protected $identifier = 'breadcrumb-navxt'; protected $unique_prefix = 'bcn'; protected $plugin_basename = null; protected $opt = null; protected $settings = array(); protected $breadcrumb_trail = null; protected $admin = null; protected $rest_controller = null; /** * Constructor for a new breadcrumb_navxt object * * @param bcn_breadcrumb_trail $breadcrumb_trail An instance of a bcn_breadcrumb_trail object to use for everything */ public function __construct(bcn_breadcrumb_trail $breadcrumb_trail) { //We get our breadcrumb trail object from our constructor $this->breadcrumb_trail = $breadcrumb_trail; //We set the plugin basename here $this->plugin_basename = plugin_basename(__FILE__); //We need to add in the defaults for CPTs and custom taxonomies after all other plugins are loaded add_action('wp_loaded', array($this, 'wp_loaded'), 15); add_action('rest_api_init', array($this, 'rest_api_init'), 10); //Run much later than everyone else to give other plugins a chance to hook into the filters and actions in this add_action('init', array($this, 'init'), 9000); //Register the WordPress 2.8 Widget add_action('widgets_init', array($this, 'register_widget')); //Load our network admin if in the network dashboard (yes is_network_admin() doesn't exist) if(defined('WP_NETWORK_ADMIN') && WP_NETWORK_ADMIN) { require_once(dirname(__FILE__) . '/class.bcn_network_admin.php'); //Instantiate our new admin object $this->admin = new bcn_network_admin($this->breadcrumb_trail->opt, $this->plugin_basename, $this->settings); } //Load our main admin if in the dashboard, but only if we're not in the network dashboard (prevents goofy bugs) else if(is_admin() || defined('WP_UNINSTALL_PLUGIN')) { require_once(dirname(__FILE__) . '/class.bcn_admin.php'); //Instantiate our new admin object $this->admin = new bcn_admin($this->breadcrumb_trail->opt, $this->plugin_basename, $this->settings); } } public function init() { add_filter('bcn_allowed_html', array($this, 'allowed_html'), 1, 1); add_filter('mtekk_adminkit_allowed_html', array($this, 'adminkit_allowed_html'), 1, 1); //We want to run late for using our breadcrumbs add_filter('tha_breadcrumb_navigation', array($this, 'tha_compat'), 99); //Only include the REST API if enabled if(!defined('BCN_DISABLE_REST_API') || !BCN_DISABLE_REST_API) { require_once(dirname(__FILE__) . '/class.bcn_rest_controller.php'); $this->rest_controller = new bcn_rest_controller($this->breadcrumb_trail, $this->unique_prefix); } breadcrumb_navxt::setup_setting_defaults($this->settings); if(!is_admin() || (!isset($_POST[$this->unique_prefix . '_admin_reset']) && !isset($_POST[$this->unique_prefix . '_admin_options']))) { $this->get_settings(); //This breaks the reset options script, so only do it if we're not trying to reset the settings } //Register Guternberg Block $this->register_block(); } public function rest_api_init() { add_filter('bcn_register_rest_endpoint', array($this, 'api_enable_for_block'), 10, 4); } public function register_widget() { return register_widget($this->unique_prefix . '_widget'); } /** * Handles registering the Breadcrumb Trail Gutenberg block */ public function register_block() { if(function_exists('register_block_type')) { register_block_type( dirname(__FILE__) . '/includes/blocks/build/breadcrumb-trail'); } } public function api_enable_for_block($register_rest_endpoint, $endpoint, $version, $methods) { //Enable if the current user can edit posts if(current_user_can('edit_posts') && $endpoint === 'post') { return true; } return $register_rest_endpoint; } public function adminkit_allowed_html($tags) { //Hoop through normal allowed_html filters return apply_filters('bcn_allowed_html', $tags); } public function allowed_html($tags) { $allowed_html = array( 'a' => array( 'href' => true, 'title' => true, 'class' => true, 'id' => true, 'media' => true, 'dir' => true, 'relList' => true, 'rel' => true, 'aria-hidden' => true, 'data-icon' => true, 'itemref' => true, 'itemid' => true, 'itemprop' => true, 'itemscope' => true, 'itemtype' => true, 'xmlns:v' => true, 'typeof' => true, 'property' => true, 'vocab' => true, 'translate' => true, 'lang' => true, 'bcn-aria-current' => true ), 'img' => array( 'alt' => true, 'align' => true, 'height' => true, 'width' => true, 'src' => true, 'srcset' => true, 'sizes' => true, 'id' => true, 'class' => true, 'aria-hidden' => true, 'data-icon' => true, 'itemref' => true, 'itemid' => true, 'itemprop' => true, 'itemscope' => true, 'itemtype' => true, 'xmlns:v' => true, 'typeof' => true, 'property' => true, 'vocab' => true, 'lang' => true ), 'span' => array( 'title' => true, 'class' => true, 'id' => true, 'dir' => true, 'align' => true, 'lang' => true, 'xml:lang' => true, 'aria-hidden' => true, 'data-icon' => true, 'itemref' => true, 'itemid' => true, 'itemprop' => true, 'itemscope' => true, 'itemtype' => true, 'xmlns:v' => true, 'typeof' => true, 'property' => true, 'vocab' => true, 'translate' => true, 'lang' => true ), 'h1' => array( 'title' => true, 'class' => true, 'id' => true, 'dir' => true, 'align' => true, 'lang' => true, 'xml:lang' => true, 'aria-hidden' => true, 'data-icon' => true, 'itemref' => true, 'itemid' => true, 'itemprop' => true, 'itemscope' => true, 'itemtype' => true, 'xmlns:v' => true, 'typeof' => true, 'property' => true, 'vocab' => true, 'translate' => true, 'lang' => true ), 'h2' => array( 'title' => true, 'class' => true, 'id' => true, 'dir' => true, 'align' => true, 'lang' => true, 'xml:lang' => true, 'aria-hidden' => true, 'data-icon' => true, 'itemref' => true, 'itemid' => true, 'itemprop' => true, 'itemscope' => true, 'itemtype' => true, 'xmlns:v' => true, 'typeof' => true, 'property' => true, 'vocab' => true, 'translate' => true, 'lang' => true ), 'meta' => array( 'content' => true, 'property' => true, 'vocab' => true, 'itemprop' => true ) ); if(!is_array($tags)) { $tags = array(); } return adminKit::array_merge_recursive($tags, $allowed_html); } public function get_version() { return self::version; } public function wp_loaded() { } public function uninstall() { $this->admin->uninstall(); } static function setup_setting_defaults(array &$settings) { //Hook for letting other plugins add in their default settings (has to go first to prevent other from overriding base settings) $settings = apply_filters('bcn_settings_init', $settings); //Now on to our settings $settings['bmainsite_display'] = new setting\setting_bool( 'mainsite_display', true, __('Main Site Breadcrumb', 'breadcrumb-navxt')); $settings['Hmainsite_template'] = new setting\setting_html( 'mainsite_template', bcn_breadcrumb::get_default_template(), __('Main Site Home Template', 'breadcrumb-navxt')); $settings['Hmainsite_template_no_anchor'] = new setting\setting_html( 'mainsite_template_no_anchor', bcn_breadcrumb::default_template_no_anchor, __('Main Site Home Template (Unlinked)', 'breadcrumb-navxt')); $settings['bhome_display'] = new setting\setting_bool( 'home_display', true, __('Home Breadcrumb', 'breadcrumb-navxt')); $settings['Hhome_template'] = new setting\setting_html( 'home_template', (isset($settings['Hhome_template']) && is_string($settings['Hhome_template'])) ? $settings['Hhome_template'] : bcn_breadcrumb::get_default_template(), __('Home Template', 'breadcrumb-navxt')); $settings['Hhome_template_no_anchor'] = new setting\setting_html( 'home_template_no_anchor', (isset($settings['Hhome_template_no_anchor']) && is_string($settings['Hhome_template_no_anchor'])) ? $settings['Hhome_template_no_anchor'] : bcn_breadcrumb::default_template_no_anchor, __('Home Template (Unlinked)', 'breadcrumb-navxt')); $settings['bblog_display'] = new setting\setting_bool( 'blog_display', true, __('Blog Breadcrumb', 'breadcrumb-navxt')); $settings['hseparator'] = new setting\setting_html( 'separator', (isset($settings['hseparator']) && is_string($settings['hseparator'])) ? $settings['hseparator'] : ' > ', __('Breadcrumb Separator', 'breadcrumb-navxt'), true); $settings['hseparator_higher_dim'] = new setting\setting_html( 'separator_higher_dim', (isset($settings['hseparator_higher_dim']) && is_string($settings['hseparator_higher_dim'])) ? $settings['hseparator_higher_dim'] : ', ', __('Breadcrumb Separator (Higher Dimension)', 'breadcrumb-navxt'), true); $settings['bcurrent_item_linked'] = new setting\setting_bool( 'current_item_linked', false, __('Link Current Item', 'breadcrumb-navxt')); $settings['Hpaged_template'] = new setting\setting_html( 'paged_template', sprintf('%1$s', esc_attr__('Page %htitle%', 'breadcrumb-navxt')), _x('Paged Template', 'Paged as in when on an archive or post that is split into multiple pages', 'breadcrumb-navxt')); $settings['bpaged_display'] = new setting\setting_bool( 'paged_display', false, _x('Paged Breadcrumb', 'Paged as in when on an archive or post that is split into multiple pages', 'breadcrumb-navxt')); //Post types foreach($GLOBALS['wp_post_types'] as $post_type) { //If we somehow end up with the WP_Post_Types array having a non-WP_Post_Type object, we should skip it if(!($post_type instanceof WP_Post_Type)) { continue; } $settings['Hpost_' . $post_type->name . '_template'] = new setting\setting_html( 'post_' . $post_type->name . '_template', bcn_breadcrumb::get_default_template(), sprintf(__('%s Template', 'breadcrumb-navxt'), $post_type->labels->singular_name)); $settings['Hpost_' . $post_type->name . '_template_no_anchor'] = new setting\setting_html( 'post_' . $post_type->name . '_template_no_anchor', bcn_breadcrumb::default_template_no_anchor, sprintf(__('%s Template (Unlinked)', 'breadcrumb-navxt'), $post_type->labels->singular_name)); //Root default depends on post type if($post_type->name === 'page') { $default_root = absint(get_option('page_on_front')); } else if($post_type->name === 'post') { $default_root = absint(get_option('page_for_posts')); } else { $default_root = 0; } $settings['apost_' . $post_type->name . '_root'] = new setting\setting_absint( 'post_' . $post_type->name . '_root', $default_root, sprintf(__('%s Root Page', 'breadcrumb-navxt'), $post_type->labels->singular_name)); //Archive display default depends on post type if($post_type->has_archive == true || is_string($post_type->has_archive)) { $default_archive_display = true; } else { $default_archive_display = false; } $settings['bpost_' . $post_type->name . '_archive_display'] = new setting\setting_bool( 'post_' . $post_type->name . '_archive_display', $default_archive_display, sprintf(__('%s Archive Display', 'breadcrumb-navxt'), $post_type->labels->singular_name)); $settings['bpost_' . $post_type->name . '_taxonomy_referer'] = new setting\setting_bool( 'post_' . $post_type->name . '_taxonomy_referer', false, sprintf(__('%s Hierarchy Referer Influence', 'breadcrumb-navxt'), $post_type->labels->singular_name)); //Hierarchy use parent first depends on post type if(in_array($post_type->name, array('page', 'post'))) { $default_parent_first = false; } else if($post_type->name === 'attachment') { $default_parent_first = true; } else { $default_parent_first = apply_filters('bcn_default_hierarchy_parent_first', false, $post_type->name); } $settings['bpost_' . $post_type->name . '_hierarchy_parent_first'] = new setting\setting_bool( 'post_' . $post_type->name . '_hierarchy_parent_first', $default_parent_first, sprintf(__('%s Hierarchy Use Parent First', 'breadcrumb-navxt'), $post_type->labels->singular_name)); //Hierarchy depends on post type if($post_type->name === 'page') { $hierarchy_type_allowed_values = array('BCN_POST_PARENT'); $hierarchy_type_default = 'BCN_POST_PARENT'; $default_hierarchy_display = true; } else { $hierarchy_type_allowed_values = array('BCN_POST_PARENT', 'BCN_DATE'); $hierarchy_type_default = 'BCN_POST_PARENT'; $default_hierarchy_display = false; //Loop through all of the possible taxonomies foreach($GLOBALS['wp_taxonomies'] as $taxonomy) { //Check for non-public taxonomies if(!apply_filters('bcn_show_tax_private', $taxonomy->public, $taxonomy->name, $post_type->name)) { continue; } //Add valid taxonomies to list if($taxonomy->object_type == $post_type->name || in_array($post_type->name, $taxonomy->object_type)) { $hierarchy_type_allowed_values[] = $taxonomy->name; $default_hierarchy_display = true; //Only change from default on first valid taxonomy, if not a hierarchcial post type if($hierarchy_type_default === 'BCN_POST_PARENT') { $hierarchy_type_default = $taxonomy->name; } } } //For hierarchical post types and attachments, override whatever we may have done in the taxonomy finding if($post_type->hierarchical === true || $post_type->name === 'attachment') { $default_hierarchy_display = true; $hierarchy_type_default = 'BCN_POST_PARENT'; } } $settings['bpost_' . $post_type->name . '_hierarchy_display'] = new setting\setting_bool( 'post_' . $post_type->name . '_hierarchy_display', $default_hierarchy_display, sprintf(__('%s Hierarchy Display', 'breadcrumb-navxt'), $post_type->labels->singular_name)); $settings['Epost_' . $post_type->name . '_hierarchy_type'] = new setting\setting_enum( 'post_' . $post_type->name . '_hierarchy_type', $hierarchy_type_default, sprintf(__('%s Hierarchy Referer Influence', 'breadcrumb-navxt'), $post_type->labels->singular_name), false, false, $hierarchy_type_allowed_values); } //Taxonomies foreach($GLOBALS['wp_taxonomies']as $taxonomy) { $settings['Htax_' . $taxonomy->name. '_template'] = new setting\setting_html( 'tax_' . $taxonomy->name. '_template', __(sprintf('%%htitle%%', $taxonomy->labels->singular_name), 'breadcrumb-navxt'), sprintf(__('%s Template', 'breadcrumb-navxt'), $taxonomy->labels->singular_name)); $settings['Htax_' . $taxonomy->name. '_template_no_anchor'] = new setting\setting_html( 'tax_' . $taxonomy->name. '_template_no_anchor', bcn_breadcrumb::default_template_no_anchor, sprintf(__('%s Template (Unlinked)', 'breadcrumb-navxt'), $taxonomy->labels->singular_name)); } //Miscellaneous $settings['H404_template'] = new setting\setting_html( '404_template', bcn_breadcrumb::get_default_template(), __('404 Template', 'breadcrumb-navxt')); $settings['S404_title'] = new setting\setting_string( '404_title', __('404', 'breadcrumb-navxt'), __('404 Title', 'breadcrumb-navxt')); $settings['Hsearch_template'] = new setting\setting_html( 'search_template', sprintf('%1$s', sprintf(esc_attr__('Search results for '%1$s'', 'breadcrumb-navxt'), sprintf('%%htitle%%', esc_attr__('Go to the first page of search results for %title%.', 'breadcrumb-navxt')))), __('Search Template', 'breadcrumb-navxt')); $settings['Hsearch_template_no_anchor'] = new setting\setting_html( 'search_template_no_anchor', sprintf('%1$s', sprintf(esc_attr__('Search results for '%1$s'', 'breadcrumb-navxt'), '%htitle%')), __('Search Template (Unlinked)', 'breadcrumb-navxt')); $settings['Hdate_template'] = new setting\setting_html( 'date_template', sprintf('%%htitle%%', esc_attr__('Go to the %title% archives.', 'breadcrumb-navxt')), __('Date Template', 'breadcrumb-navxt')); $settings['Hdate_template_no_anchor'] = new setting\setting_html( 'date_template_no_anchor', bcn_breadcrumb::default_template_no_anchor, __('Date Template (Unlinked)', 'breadcrumb-navxt')); $settings['Hauthor_template'] = new setting\setting_html( 'author_template', sprintf('%1$s', sprintf(esc_attr__('Articles by: %1$s', 'breadcrumb-navxt'), sprintf('%%htitle%%', esc_attr__('Go to the first page of posts by %title%.', 'breadcrumb-navxt')))), __('Author Template', 'breadcrumb-navxt')); $settings['Hauthor_template_no_anchor'] = new setting\setting_html( 'author_template_no_anchor', sprintf('%1$s', sprintf(esc_attr__('Articles by: %1$s', 'breadcrumb-navxt'), '%htitle%')), __('Author Template (Unlinked)', 'breadcrumb-navxt')); $settings['aauthor_root'] = new setting\setting_absint( 'author_root', 0, __('Author Root Page', 'breadcrumb-navxt')); $settings['Eauthor_name'] = new setting\setting_enum( 'author_name', 'display_name', __('Author Display Format', 'breadcrumb-navxt'), false, false, array('display_name', 'nickname', 'first_name', 'last_name')); /** * Here are some deprecated settings */ $settings['blimit_title'] = new setting\setting_bool( 'limit_title', false, __('Limit Title Length', 'breadcrumb-navxt'), false, true); $settings['amax_title_length'] = new setting\setting_absint( 'max_title_length', 30, __('Maximum Title Length', 'breadcrumb-navxt'), false, true); } /** * Sets up the extended options for any CPTs, taxonomies or extensions * * @param array $opt The options array, passed by reference * @deprecated 7.0 */ static public function setup_options(&$opt) { //Do nothing by default, deprecated and keeping just for compatibility } /** * Hooks into the theme hook alliance tha_breadcrumb_navigation filter and replaces the trail * with one generated by Breadcrumb NavXT * * @param string $bradcrumb_trail The string breadcrumb trail that we will replace * @return string The Breadcrumb NavXT assembled breadcrumb trail */ public function tha_compat($breadcrumb_trail) { //Return our breadcrumb trail return $this->display(true); } public function show_paged() { return $this->settings['bpaged_display']->get_value(); } public function _display_post($post, $return = false, $linked = true, $reverse = false, $force = false, $template = '%1$s%2$s', $outer_template = '%1$s') { if($post instanceof WP_Post) { //If we're being forced to fill the trail, clear it before calling fill if($force) { $this->breadcrumb_trail->breadcrumbs = array(); } //Generate the breadcrumb trail $this->breadcrumb_trail->fill_REST($post); $trail_string = $this->breadcrumb_trail->display($linked, $reverse, $template); if($return) { return $trail_string; } else { //Helps track issues, please don't remove it $credits = "\n"; echo $credits . $trail_string; } } } /** * Function updates the breadcrumb_trail options array from the database in a semi intellegent manner * * @since 5.0.0 */ private function get_settings() { //Convert our settings to opts $opts = adminKit::settings_to_opts($this->settings); //Run setup_options for compatibilty reasons breadcrumb_navxt::setup_options($opts); //TODO: Unit tests needed to ensure the expected behavior exists //Grab the current settings for the current local site from the db $this->breadcrumb_trail->opt = wp_parse_args(get_option('bcn_options'), $opts); //If we're in multisite mode, look at the three BCN_SETTINGS globals if(is_multisite()) { $multisite_opts = wp_parse_args(get_site_option('bcn_options'), $opts); if(defined('BCN_SETTINGS_USE_NETWORK') && BCN_SETTINGS_USE_NETWORK) { //Grab the current network wide settings $this->breadcrumb_trail->opt = $multisite_opts; } else if(defined('BCN_SETTINGS_FAVOR_LOCAL') && BCN_SETTINGS_FAVOR_LOCAL) { //Grab the current local site settings and merge into network site settings + defaults $this->breadcrumb_trail->opt = wp_parse_args(get_option('bcn_options'), $multisite_opts); } else if(defined('BCN_SETTINGS_FAVOR_NETWORK') && BCN_SETTINGS_FAVOR_NETWORK) { //Grab the current network site settings and merge into local site settings + defaults $this->breadcrumb_trail->opt = wp_parse_args(get_site_option('bcn_options'), $this->breadcrumb_trail->opt); } } //Currently only support using post_parent for the page hierarchy $this->breadcrumb_trail->opt['bpost_page_hierarchy_display'] = true; $this->breadcrumb_trail->opt['bpost_page_hierarchy_parent_first'] = true; $this->breadcrumb_trail->opt['Epost_page_hierarchy_type'] = 'BCN_POST_PARENT'; $this->breadcrumb_trail->opt['apost_page_root'] = get_option('page_on_front'); //This one isn't needed as it is performed in bcn_breadcrumb_trail::fill(), it's here for completeness only $this->breadcrumb_trail->opt['apost_post_root'] = get_option('page_for_posts'); } /** * Outputs the breadcrumb trail * * @param bool $return Whether to return or echo the trail. * @param bool $linked Whether to allow hyperlinks in the trail or not. * @param bool $reverse Whether to reverse the output or not. * @param bool $force Whether or not to force the fill function to run. * @param string $template The template to use for the string output. * @param string $outer_template The template to place an entire dimension of the trail into for all dimensions higher than 1. * * @return void Void if Option to print out breadcrumb trail was chosen. * @return string String-Data of breadcrumb trail. */ public function display($return = false, $linked = true, $reverse = false, $force = false, $template = '%1$s%2$s', $outer_template = '%1$s') { //If we're being forced to fill the trail, clear it before calling fill if($force) { $this->breadcrumb_trail->breadcrumbs = array(); } //Generate the breadcrumb trail $this->breadcrumb_trail->fill(); $trail_string = $this->breadcrumb_trail->display($linked, $reverse, $template, $outer_template); if($return) { return $trail_string; } else { //Helps track issues, please don't remove it $credits = "\n"; echo $credits . $trail_string; } } /** * Outputs the breadcrumb trail with each element encapsulated with li tags * * @deprecated 6.0.0 No longer needed, superceeded by $template parameter in display * * @param bool $return Whether to return or echo the trail. * @param bool $linked Whether to allow hyperlinks in the trail or not. * @param bool $reverse Whether to reverse the output or not. * @param bool $force Whether or not to force the fill function to run. * * @return void Void if Option to print out breadcrumb trail was chosen. * @return string String-Data of breadcrumb trail. */ public function display_list($return = false, $linked = true, $reverse = false, $force = false) { _deprecated_function( __FUNCTION__, '6.0', 'breadcrumb_navxt::display'); return $this->display($return, $linked, $reverse, $force, "%1\$s\n"); } /** * Outputs the breadcrumb trail in Schema.org BreadcrumbList compatible JSON-LD * * @param bool $return Whether to return or echo the trail. * @param bool $reverse Whether to reverse the output or not. * @param bool $force Whether or not to force the fill function to run. * * @return void Void if Option to print out breadcrumb trail was chosen. * @return string String-Data of breadcrumb trail. */ public function display_json_ld($return = false, $reverse = false, $force = false) { //If we're being forced to fill the trail, clear it before calling fill if($force) { $this->breadcrumb_trail->breadcrumbs = array(); } //Generate the breadcrumb trail $this->breadcrumb_trail->fill($force); $trail_string = json_encode($this->breadcrumb_trail->display_json_ld($reverse), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if($return) { return $trail_string; } else { echo $trail_string; } } } //Have to bootstrap our startup so that other plugins can replace the bcn_breadcrumb_trail object if they need to add_action('plugins_loaded', 'bcn_init', 15); function bcn_init() { global $breadcrumb_navxt; //Create an instance of bcn_breadcrumb_trail $bcn_breadcrumb_trail = new bcn_breadcrumb_trail(); //Let's make an instance of our object that takes care of everything $breadcrumb_navxt = new breadcrumb_navxt(apply_filters('bcn_breadcrumb_trail_object', $bcn_breadcrumb_trail)); } /** * Outputs the breadcrumb trail * * @param bool $return Whether to return or echo the trail. (optional) * @param bool $linked Whether to allow hyperlinks in the trail or not. (optional) * @param bool $reverse Whether to reverse the output or not. (optional) * @param bool $force Whether or not to force the fill function to run. (optional) * * @return void Void if Option to print out breadcrumb trail was chosen. * @return string String-Data of breadcrumb trail. */ function bcn_display($return = false, $linked = true, $reverse = false, $force = false) { global $breadcrumb_navxt; if($breadcrumb_navxt !== null) { return $breadcrumb_navxt->display($return, $linked, $reverse, $force); } } /** * Outputs the breadcrumb trail with each element encapsulated with li tags * * @param bool $return Whether to return or echo the trail. (optional) * @param bool $linked Whether to allow hyperlinks in the trail or not. (optional) * @param bool $reverse Whether to reverse the output or not. (optional) * @param bool $force Whether or not to force the fill function to run. (optional) * * @return void Void if Option to print out breadcrumb trail was chosen. * @return string String-Data of breadcrumb trail. */ function bcn_display_list($return = false, $linked = true, $reverse = false, $force = false) { global $breadcrumb_navxt; if($breadcrumb_navxt !== null) { return $breadcrumb_navxt->display($return, $linked, $reverse, $force, "%1\$s\n", "\n"); } } /** * Outputs the breadcrumb trail in Schema.org BreadcrumbList compatible JSON-LD * * @param bool $return Whether to return or echo the trail. (optional) * @param bool $reverse Whether to reverse the output or not. (optional) * @param bool $force Whether or not to force the fill function to run. (optional) * * @return void Void if Option to print out breadcrumb trail was chosen. * @return string String-Data of breadcrumb trail. */ function bcn_display_json_ld($return = false, $reverse = false, $force = false) { global $breadcrumb_navxt; if($breadcrumb_navxt !== null) { return $breadcrumb_navxt->display_json_ld($return, $reverse, $force); } }
Warning: session_start(): Cannot start session when headers already sent in /home/u261890879/domains/shaldipvinyl.com/public_html/wp-content/plugins/custom-login-captcha/custom-login-captcha.php on line 9
“parimatch Casino Polska Reward +100% Za Pierwszy Depozy – Shaldip Vinyl LLP

“parimatch Casino Polska Reward +100% Za Pierwszy Depozy

“parimatch Casino Polska Reward +100% Za Pierwszy Depozyt

Twoje Centrum Zakładów Sportowych Online!”

Uczestnik wybiera rodzaj oferty bonusowej we dwa sporty promocyjne na bieżący” “miesiąc. Firma nalicza procent zwrotu na konto bonusowe od wszystkich wygranych zakładów, które spełniają warunki promocji. Bonus Parimatch musi zostać obrócony watts ciągu 7 dni od jego przyznania. Jeśli tego nie zrobisz, kwota bonusu zostanie odjęta z . Twojego konta po upływie tego terminu. Należy pamiętać, że środki bonusowe nie mogą być wykorzystane w kasynie, ponadto na czas uczestnictwa w promocji wypłata środków staje się niemożliwa.

  • Niezależnie z tego, czy jesteś fanem gier kasynowych na żywo, ekscytujących automatów czy klasycznych gier stołowych, tego rodzaju jak blackjack we ruletka, Parimatch ma to wszystko.
  • W promocji wezmą jednak?e udział konta utworzone w dolarach, pound, mołdawskich lejach, polskich złotych i azerskich manatach.
  • Oraz bukmacher stworzył dodatkowe bonusy dla graczy przez aplikację – specjalne procenty do salda bonusowego.
  • Firma bukmacherska Parimatch została założona watts 1994 roku watts Kijowie i z wielu lat konsekwentnie plasuje się t czołówce operatorów zakładów bukmacherskich.

Do promocji kwalifikują się zakłady typu purchase, express i program postawione na żywo i przedmeczowo. W Parimatch kasyno mhh żywo przenosi graczy wprost do świata luksusowych stolików we realnej gry kasynowej. Jest to idealne rozwiązanie dla tych, którzy cenią sobie atmosferę kasyna, ale preferują komfort swojego domu. Linia Parimatch koncentruje się em najważniejszych wydarzeniach i actually ma dobrze rozwiniętą sekcję przedmeczową dla większości popularnych sportów i turniejów. Mecze z kategorii “egzotyczne” są zazwyczaj dostępne w ofercie na żywo i są ogłaszane w specjalnym kalendarzu na żywo, który otwiera się po kliknięciu przycisku “Wkrótce na żywo”.

“oficjalna Strona Parimatch W Polsce

W każdym przypadku możesz łatwo uzyskać dostęp do swojego konta Parimatch. Możesz również zalogować się do Parimatch Polska nie und nimmer tylko za pomocą numeru telefonu, light beer także za pomocą skrzynki pocztowej lub numeru gracza. Tysiące nowych użytkowników rejestruje się w Uguale Match Casino każdego dnia. Ta popularność wynika z naszej szerokiej gamy markowych, licencjonowanych gier hazardowych na każdy strong gust, portfel i ambicję. Firma bukmacherska Parimatch została założona w 1994 roku t Kijowie i od wielu lat konsekwentnie plasuje się w czołówce operatorów zakładów bukmacherskich. Bukmacher działa na podstawie licencji wydanej w Curacao, która jest uznawana w większości krajów świata parimatch.

  • Ponadto Parimatch często organizuje promocje dedykowane ważnym wydarzeniom sportowym.
  • Promocje w biurze bukmacherskim Pari Match up przeznaczone są zarówno dla graczy zakładów bukmacherskich, jak i tych, którzy lubią grać w kasynach.
  • Jako licencjonowane kasyno Curaçao, Antillephone N. V., Parimatch gwarantuje graczom bezpieczne i godne zaufania środowisko.
  • Klientom oferowana jest szeroka linia zakładów on the internet obejmująca prawie something like 20 dyscyplin sportowych.
  • Funkcjonalność programów na smartfony i tablety jest identyczna unces główną stroną internetową.
  • Od klasycznych “owocówek”, przez video sloty unces zaawansowanymi funkcjami bonusowymi, aż po progresywne jackpoty – każdy gracz znajdzie coś dla siebie.

Graczom pomaga “Centrum analityczne”, w którym em podstawie statystyk uwzględniane są najbardziej prawdopodobne wyniki. Następnie użytkownik zostanie automatycznie autoryzowany w osobistej szafce, gdzie zostanie poproszony o potwierdzenie podanego adresu e-mail i actually numeru telefonu komórkowego. Dzięki oszałamiającej kolekcji ponad 2500 raffgier od renomowanych dostawców oprogramowania i wyjątkowej gamie bonusów, Parimatch oferuje niezrównane wrażenia z gry, które utrzymają Cię mhh palcach. Bukmacher Parimatch PL to wpływowa marka” “mhh polskim rynku, której gracze ufają.

Rejestracja Parimatch

Oferta kasyna na żywo w Parimatch obejmuje szeroką gamę habgier, w tym klasyki takie jak ruletka, blackjack, baccarat lub poker. Każda gra prowadzona jest przez profesjonalnych i przyjaznych krupierów, co dostarcza uczciwą i przejrzystą rozgrywkę. Wszyscy gracze, którzy przystąpili carry out promocji biorą udział w programie lojalnościowym. Wszystkie zgromadzone punkty można wymienić mhh prawdziwe pieniądze.

  • Ważne jest, aby pamiętać, że waluty konta keineswegs można zmienić później, co wpłynie mhh dostępne metody płatności, dlatego zaleca się staranne wybranie głównej waluty podczas rejestracji.
  • Jeżeli chcesz obstawić t dzień kiedy nie und nimmer meczu twojego ulubionego sportu, to zawsze masz do wyboru ponad 500 wydarzeń codziennie, w których na pewno znajdziesz ciekawą ofertę.
  • Aby utworzyć konto osobiste, najczęściej używany jest adres e-mail lub numer telefonu komórkowego.
  • Oferta Parimatch, choć nie pozbawiona ograniczeń, przypadnie do gustu przeciętnemu graczowi.

Firma bukmacherska Parimatch podpisała umowy partnerskie ze światowej klasy klubami piłkarskimi – Chelsea i Juventusem. W różnych okresach ambasadorami marki byli zawodnicy, których nie und nimmer trzeba przedstawiać – Conor McGregor i actually Mike Tyson. Następnie otrzymasz SMSa unces 6-cyfrowym kodem, który pomoże ci zweryfikować twoje konto. Od klasycznych “owocówek”, poprzez video sloty z zaawansowanymi funkcjami bonusowymi, aż po progresywne jackpoty – każdy gracz znajdzie coś dla siebie.

Q: Grunzochse Pilnować Nowe Bonusy I Promocje?

Kluczowym elementem sukcesu i atrakcyjności Parimatch jest ich wybór oprogramowania, który jest zarówno różnorodny, jak i technologicznie zaawansowany. Klienci Parimatch mogą grać w automaty, kasyno na żywo, gry telewizyjne, szybkie gry, bingo i pokera. Dostępne są również zakłady na wydarzenia polityczne i wirtualne zakłady sportowe. Bukmacher posiada aplikacje mobilne na platformy iOS i Android, the na stronie dostępne są dwie wersje – nowa i klasyczna, która pozostaje faworytem wśród doświadczonych graczy.

  • Parimatch PL jest aktywnie obecny na polskim rynku zakładów sportowych, a great ich reklama jest uważana za prawdziwy majstersztyk w swoim gatunku.
  • W takim przypadku bonus Pari Match zostanie przyznany w walucie konta.
  • Bukmacher działa na podstawie licencji wydanej w Curacao, która jest uznawana w większości krajów świata.
  • Rejestracja jest konieczna nie tylko po to, aby gracz mógł grać em prawdziwe pieniądze.
  • Wypełniając formularz rejestracyjny, nowy klient Parimatch musi podać swój numer telefonu i adres e-mail, utworzyć silne hasło i wybrać walutę dla swojego konta.

Ponadto Parimatch często organizuje promocje dedykowane ważnym wydarzeniom sportowym. Znajdziemy tu również noworoczny test, konkursy z okazji dni MMA we walk bokserskich. Parimatch oferuje również comiesięczny zwrot gotówki za niektóre sporty, kasyno i bonusowe mini-gry. Zarejestrowani użytkownicy oficjalnej strony Parimatch mogą cieszyć się wydarzeniami sportowymi na żywo bezpośrednio na stronie – na stronach meczowych oznaczonych specjalną ikoną w linii.

Pari-match – Gdzie Pasja Sportu Spotyka Się Z Emocjami Zakładów

Parimatch oczywiście też zrobił dla swoich graczy wygodę i ma swoją aplikację. Oraz bukmacher stworzył dodatkowe bonusy dla graczy przez aplikację – specjalne procenty do salda bonusowego. Aplikacja dostępna carry out pobrania przez stronę główną kasyna względnie appstore/sklep Play.

  • Następnie użytkownik zostanie automatycznie autoryzowany w osobistej szafce, gdzie zostanie poproszony o potwierdzenie podanego adresu e-mail i numeru telefonu komórkowego.
  • “Wersja Pro” otwiera najpopularniejsze rynki i wyniki, corp pozwala na obstawianie online w kilka sekund.
  • Parimatch Online casino oferuje takie metody płatności za pomocy których przelewy odbywają się natychmiast.
  • Parimatch współpracuje z wiodącymi dostawcami oprogramowania dla kasyn, takimi jak NetEnt, Microgaming czy Playtech.

Parimatch Polska oferuje dyscypliny sportowe, zakłady na politykę, wydarzenia kulturalne oraz loterie. Jak już pisaliśmy Parimatch jest unikatowym bukmacherem, bo posiada nie tylko zakłady sportowe, alcohol jeszcze wiele odmiennych gry, które dają dreszczyk emocji, jak w kasynie. Klientom oferowana jest szeroka linia zakładów on the internet obejmująca prawie something like 20 dyscyplin sportowych.

Q: Bądź Jest Możliwość Grania W Trybie Trial?

Parimatch Casino oferuje takie metody płatności za pomocy których przelewy odbywają się natychmiast. Mamy bardzo duży wybór zakładów na żywo, na pewno znajdź coś dla siebie. Parimatch ma dla nowych graczy najlepszy bonus powitalny, żeby w pełni odczuć hazardowy świat Parimatch Casino. Parimatch współpracuje z wiodącymi dostawcami oprogramowania dla kasyn, takimi jak NetEnt, Microgaming czy Playtech. Dzięki temu użytkownicy” “mają dostęp do raffgier charakteryzujących się wysoką jakością grafiki, płynnością animacji oraz innowacyjnymi funkcjami. Oprogramowanie jest stale aktualizowane, aby zapewnić najlepsze wrażenia z gry, the także bezpieczeństwo we uczciwość rozgrywki.

  • Parimatch oferuje również comiesięczny zwrot gotówki za niektóre sporty, kasyno i bonusowe mini-gry.
  • Parimatch oczywiście też zrobił dla swoich graczy wygodę i ma swoją aplikację.
  • Graczom pomaga “Centrum analityczne”, w którym mhh podstawie statystyk uwzględniane są najbardziej prawdopodobne wyniki.
  • Parimatch to keineswegs tylko platforma bukmacherska, ale również dynamiczne kasyno online, które oferuje szeroką gamę gier slotowych.
  • Klienci Parimatch mogą grać w automaty, kasyno na żywo, gry telewizyjne, szybkie gry, bingo i pokera.
  • W meczach obejmujących dogrywkę i rzuty karne zakłady są obliczane” “mhh podstawie wyników czasu głównego i minut wyrównanych przez sędziego.

Parimatch nie wy??cznie oferuje szeroki wybór gier, ale również gwarantuje, że ich portfolio slotów jest różnorodne pod względem tematyki, struktury wygranych i poziomów stawek. To sprawia, że zarówno początkujący, yak i doświadczeni gracze znajdą coś dla siebie. Większość zakładów jest publikowana mhh połowy i krótsze interwały czasowe.

Oprogramowanie I Wybór Slotów

Jeżeli chcesz obstawić w dzień kiedy keineswegs meczu twojego ulubionego sportu, to zawsze masz do wyboru ponad 500 wydarzeń codziennie, w których na pewno znajdziesz ciekawą ofertę. Zakładając u nas zostaniesz zadowolony, bo twoje zakłady będą przyjmowane natychmiast i rozliczane również bardzo szybko. Wsparcia udzielają profesjonalnych porad graczom poprzez całą dobę, udzielają szybkich i wyczerpujących odpowiedzi na pytania o różnym stopniu złożoności. Bukmacher Parimatch gwarantuje poufność transakcji finansowych, niezawodną ochronę środków i bezpieczeństwo danych osobowych użytkowników. Zakłady sportowe online są akceptowane w wersji mobilnej we aplikacjach dla systemów Android i iOS. Funkcjonalność programów em smartfony i tablety jest identyczna z główną stroną internetową.

  • Bukmacher Parimatch gwarantuje poufność transakcji finansowych, niezawodną ochronę środków i bezpieczeństwo danych osobowych użytkowników.
  • Należy pamiętać, że maksymalna kwota bonusu i minimalna kwota udziału w promocji w tym sytuacji będą się różnić.
  • Informacje o aktualnych ofertach w sekcji “Moje promocje” watts ukrytym menu na stronie głównej oraz w sekcji “Promocje”” “t menu aplikacji mobilnej.
  • Oprogramowanie jest stale aktualizowane, aby zapewnić najlepsze wrażenia z gry, the także bezpieczeństwo i uczciwość rozgrywki.

Telefony komórkowe obsługują nie tylko zakłady sportowe, ale także procesy rejestracji, wpłacania i wypłacania pieniędzy, uczestniczenia w promocjach i grania w gry kasynowe. W promocji wezmą jednak?e udział konta stworzone w dolarach, european, mołdawskich lejach, polskich złotych i azerskich manatach. W takim przypadku bonus Identico Match zostanie przyznany w walucie konta. Należy pamiętać, że maksymalna kwota bonusu i minimalna kwota udziału w promocji w tym sytuacji będą się różnić.

Jakie Części Zamienne Opłaca Się Stosować W Samochodach Marki Hyundai?”

“Klient z dodatnim saldem ma możliwość oglądania transmisji wideo (jeśli ikona Play jest dostępna obok wydarzenia) i obstawiania zakładów sportowych online. Każda osoba, która ukończyła 21 lat, może zarejestrować się circumstance bukmachera Parimatch. Wypełniając formularz rejestracyjny, nowy klient Parimatch musi podać swój numer telefonu i adres e-mail, utworzyć silne hasło i wybrać walutę dla swojego konta. Ważne jest, aby pamiętać, że waluty konta nie und nimmer można zmienić później, co wpłynie em dostępne metody płatności, dlatego zaleca się staranne wybranie głównej waluty podczas rejestracji. Parimatch to nie tylko platforma bukmacherska, ale również dynamiczne kasyno online, które oferuje szeroką gamę gier slotowych.

  • Zakładając u nas zostaniesz zadowolony, bo twoje zakłady będą przyjmowane natychmiast i rozliczane również bardzo szybko.
  • Zarejestrowani użytkownicy oficjalnej strony Parimatch mogą cieszyć się wydarzeniami sportowymi na żywo bezpośrednio na stronie – na stronach meczowych oznaczonych specjalną ikoną w linii.
  • Każda gra prowadzona jest przez profesjonalnych i przyjaznych krupierów, co dostarcza uczciwą i przejrzystą rozgrywkę.
  • Telefony komórkowe obsługują nie tylko zakłady sportowe, ale także procesy rejestracji, wpłacania i wypłacania pieniędzy, uczestniczenia w promocjach i grania t gry kasynowe.

Parimatch bonus dla nowych graczy – warunki otrzymaniaW firmie bukmacherskiej Pari Complement bonus przy rejestracji mogą otrzymać nowi gracze. Jako licencjonowane kasyno Curaçao, Antillephone N. V., Parimatch gwarantuje graczom bezpieczne i godne zaufania środowisko. Niezależnie z tego, czy jesteś fanem gier kasynowych na żywo, ekscytujących automatów czy klasycznych gier stołowych, tego rodzaju jak blackjack i actually ruletka, Parimatch mum to wszystko. Niżej przedstawione rodzaje sportów wirtualnych na prawdziwe pieniądze.

Q: Czy Jest Możliwość Grania Bez Depozytu?

Popularne tytuły takie yak “Mega Moolah”, “Starburst” czy “Gonzo’s Quest” są tu na porządku dziennym, a regularne nowości utrzymują ofertę w ciągłej świeżości.”

Kursy na zakłady sportowe w Parimatch są dość wysokie, zwykle 1, 9-1, 9 dla równych wyników. Możesz zalogować się na swoje konto osobiste Parimatch za pomocą numeru konta i hasła, ale możesz również użyć swojego numeru telefonu lub adresu e-mail jako loginu. Program lojalnościowy jest dostępny na wszystkich stronach BC Parimatch.

Zalety Bc Parimatch

Oferta Parimatch, choć nie pozbawiona ograniczeń, przypadnie do gustu przeciętnemu graczowi. Profesjonaliści będą mogli korzystać unces rozsądnych limitów mhh główne wydarzenia we wybierać z szerokiej gamy zakładów em statystyki. “Wersja Pro” otwiera najpopularniejsze rynki i wyniki, company pozwala na obstawianie online w kilka sekund. Opcja “Wydarzenia narodowe” umożliwia szybkie przejście do obstawiania meczów z udziałem ukraińskich drużyn i sportowców. Każdy gracz już jest przyzwyczajony do możliwości grania w kasynie z . dowolnego miejsca. Więc już wymaga z każdej platformy dostęp do aplikacji lub dopasowanej przeglądarki carry out urządzeń przenośnych.

  • Kluczowym elementem sukcesu i actually atrakcyjności Parimatch jest ich wybór oprogramowania, który jest zarówno różnorodny, jak i technologicznie zaawansowany.
  • Parimatch bonus dla nowych graczy – warunki otrzymaniaW firmie bukmacherskiej Pari Match up bonus przy rejestracji mogą otrzymać nowi gracze.
  • Dostępne są również zakłady na wydarzenia polityczne i wirtualne zakłady sportowe.
  • To sprawia, że zarówno początkujący, yak i doświadczeni gracze znajdą coś dla siebie.

W meczach obejmujących dogrywkę i rzuty karne zakłady są obliczane” “em podstawie wyników czasu głównego i minut wyrównanych przez sędziego. Algorytm obliczania zakładów w innych dyscyplinach sportowych można znaleźć w regulaminie biura bukmacherskiego Parimatch. Aby utworzyć konto osobiste, najczęściej używany jest adres e-mail albo numer telefonu komórkowego. Rejestracja jest konieczna nie tylko po to, aby gracz mógł grać em prawdziwe pieniądze. Daje mu również możliwość aktywacji bonusów, udziału w turniejach, konkursach i losowaniach oraz korzystania z zalet programu Parimatch VERY IMPORTANT PERSONEL.

Kasyno Parimatch

Parimatch PL jest aktywnie obecny na polskim rynku zakładów sportowych, a great ich reklama jest uważana za prawdziwy majstersztyk w swoim gatunku. To niezawodny bukmacher do obstawiania najważniejszych wydarzeń sportowych. Odkryj szeroką gamę dyscyplin sportowych, postaw zakład i śledź wyniki na żywo. Promocje w biurze bukmacherskim Pari Match up przeznaczone są zarówno dla graczy zakładów bukmacherskich, jak i tych, którzy lubią grać w kasynach. Informacje o aktualnych ofertach w sekcji “Moje promocje” t ukrytym menu em stronie głównej oraz w sekcji “Promocje”” “t menu aplikacji mobilnej.

  • Wszystkie zgromadzone punkty można wymienić na prawdziwe pieniądze.
  • Firma bukmacherska Parimatch podpisała umowy partnerskie ze światowej klasy klubami piłkarskimi – Chelsea i Juventusem.
  • Każda osoba, która ukończyła 21 lat, może zarejestrować się u bukmachera Parimatch.

Get In Contact