Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2021-4410: class-qtranslate-slug.php in qtranslate-slug/trunk/includes – WordPress Plugin Repository

The Qtranslate Slug plugin for WordPress is vulnerable to Cross-Site Request Forgery in versions up to, and including, 1.1.18. This is due to missing or incorrect nonce validation on the save_postdata() function. This makes it possible for unauthenticated attackers to save post data via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.

CVE
#sql#js#java#wordpress#php#perl#auth#ssl

1<?php23/**4 * QtranslateSlug class5 *6 * @since 1.07 */8class QtranslateSlug {9 10 11 /**12 * array with old data system13 * @var bool14 * @since 1.015 */16 private $old_data = null;17 18 19 20 /**21 * stores permalink_structure option, for save queries to db22 * @var string23 * @since 1.024 */25 private $permalink_structure;26 27 28 29 /**30 * Stores options slugs from database31 * @var array32 * @since 1.033 */34 protected $options;35 36 37 38 /**39 * Variable used to override the language40 * @var string41 * @since 1.042 */43 private $lang = false;44 45 /**46 * variable for current language47 */48 private $current_lang = false;49 50 /**51 * variable for default language52 */53 private $default_language = false;54 55 /**56 * Array of enabled languages57 * @var array58 * @since 1.059 */60 private $enabled_languages = array();6162 /**63 * Array of enabled languages64 * @var array65 * @since 1.066 */67 private $url_path_mode = “";6869 /**70 * slug in meta_key name in meta tables71 * @var string72 * @since 1.073 */74 private $meta_key = “_qts_slug_%s";7576 /**77 * Array of translated versions of the current url78 * @var array79 * @since 1.080 */81 private $current_url = array();8283 /**84 * Variable that contains the prefix for base plugin function names (qtranslate/qtranslate-x)85 * @var string86 * @since ?87 */88 private $plugin_prefix = “";89 90 /**91 * return the current / temp language92 * @since 1.093 */94 private function get_lang() {95 return ($this->lang) ? $this->lang : $this->current_lang;96 }9798 /**99 * return the current / temp language100 * we store and use it all the way!101 * @since 1.1.9102 */103 private function get_currentlang() {104 return $this->current_lang;105 }106107 /**108 * return the enabled languages109 * we store and use it all the way!110 * @since 1.1.9111 */112 private function get_enabled_languages() {113 return $this->enabled_languages;114 }115116 /**117 * return the enabled languages118 * we store and use it all the way!119 * @since 1.1.9120 */121 private function get_url_path_mode() {122 return $this->url_path_mode;123 }124 125 /**126 * getter: options127 * @since 1.0128 */129 public function get_options() {130 $this->set_options();131 return $this->options;132 }133134 /**135 * Returns the correct prefix for the function names of the different supported translation plugins.136 * Will return ‘qtranxf_’ if qtranslate-x is used, ‘qtrans_’ otherwise.137 *138 * @return string the function name prefix for translation functions from other plugins139 * @since 1.1.9140 */141142 private function get_plugin_prefix(){143 144 return $this->plugin_prefix;145 }146 147 148 /**149 * setter: options | permalink_structure150 *151 * @since 1.0152 */153 public function set_options() {154155 if (empty($this->options)) {156 $this->options = get_option(QTS_OPTIONS_NAME);157 }158159 if (!$this->options) {160 add_option(QTS_OPTIONS_NAME, array());161 }162 163 if (is_null($this->permalink_structure)) {164 $this->permalink_structure = get_option(‘permalink_structure’);165 }166 }167 168 /**169 * Sets the prefix for the active fork. See get_plugin_prefix170 * @since 1.1.9171 *172 */173 private function set_plugin_prefix(){174 if (‘’ === $this->plugin_prefix){175 if (is_plugin_active(‘qtranslate-x/qtranslate.php’) || defined( ‘QTRANSLATE_FILE’ ) ){176 $this->plugin_prefix = 'qtranxf_’;177 } else {178 $this->plugin_prefix = 'qtrans_’;179 }180 }181 }182183 /**184 * Sets the url path mode based on the qtranslate or fork settings.185 *186 */187188 private function set_url_path_mode(){189 if (‘’ === $this->url_path_mode){190 if (is_plugin_active(‘qtranslate-x/qtranslate.php’) || defined( ‘QTRANSLATE_FILE’ ) ){191 $this->url_path_mode = QTX_URL_PATH;192 } else {193 $this->url_path_mode = QT_URL_PATH;194 }195 }196 }197 198 /**199 * setter: options | permalink_structure200 *201 * @since 1.0202 */203 public function save_options($new_options = false) {204 if (!$new_options || empty($new_options)) {205 return;206 }207 208 if (count($this->options) != count($new_options)) {209 return;210 }211 212 update_option(QTS_OPTIONS_NAME, $new_options);213 $this->options = $new_options;214 }215 216 217 218 /**219 * getter: meta key220 *221 * @since 1.0222 */223 public function get_meta_key( $force_lang = false ) {224 225 $lang = $this->get_lang();226 227 if ($force_lang) {228 $lang = $force_lang;229 }230 231 return sprintf($this->meta_key, $lang); // returns: _qts_slug_en232 }233 234 235 236 /**237 * check dependences for activation238 *239 * @since 1.0240 */241 static function block_activate() {242 global $wp_version;243 include_once( ABSPATH . ‘wp-admin/includes/plugin.php’ ); 244 return (245 version_compare($wp_version, “4.0", “<” ) ||246 !( defined( ‘QTRANSLATE_FILE’ ) ||247 ( is_plugin_active(‘qtranslate/qtranslate.php’) || 248 is_plugin_active(‘qtranslate/qtranslate-x.php’) ||249 is_plugin_active(‘mqtranslate/mqtranslate.php’) || 250 is_plugin_active(‘qtranslate-x/qtranslate.php’)) )251 );252 }253 254 255 256 257 /**258 * check if exists qtranslate and do the installation, support multisite259 *260 * @since 1.0261 */262 public function install() {263 global $wpdb;264 265 if ( self::block_activate() ) {266 if (is_admin()) {267 add_action('admin_notices’, array(&$this, ‘notice_dependences’));268 }269 }270 271 if ( function_exists(‘is_multisite’) && is_multisite() ) {272273 if (isset($_GET[‘networkwide’]) && ($_GET[‘networkwide’] == 1)) {274 275 $old_blog = $wpdb->blogid;276 $blogids = $wpdb->get_col($wpdb->prepare(“SELECT blog_id FROM $wpdb->blogs”));277 foreach ($blogids as $blog_id) {278 switch_to_blog($blog_id);279 $this->activate();280 }281 switch_to_blog($old_blog);282 return;283 }284 }285 286 $this->activate();287 }288 289 290 291 /**292 * activates and do the installation293 *294 * @since 1.0295 */296 private function activate() {297 global $wp_rewrite;298299 $this->set_options();300 301 $qts_version = get_option(‘qts_version’);302 303 // checks version and do the installation304 if ( !$qts_version || $qts_version != QTS_VERSION ) {305 306 // install termmeta table using functions from Simple-Term-Meta 307 // ( http://wordpress.org/extend/plugins/simple-term-meta/ )308 install_term_meta_table();309 310 // update installed option 311 update_option('qts_version’, QTS_VERSION);312 }313 314 // regenerate rewrite rules in db315 add_action( 'generate_rewrite_rules’, array(&$this, ‘modify_rewrite_rules’) );316 flush_rewrite_rules();317 }318 319 320321 /**322 * register front end styles and enqueue323 *324 * @since 1.1.7325 */326 public function register_plugin_styles() {327 wp_register_style( 'qts_front_styles’, plugins_url( '/assets/css/qts-default.css’, dirname(__FILE__ ) ) );328 wp_enqueue_style( ‘qts_front_styles’ );329 }330 /**331 * register minified front end styles and enqueue332 * 43LC: easier duplicating the function :|333 * @since 1.1.8334 */335 public function register_plugin_styles_min() {336 wp_register_style( 'qts_front_styles’, plugins_url( '/assets/css/qts-default.min.css’, dirname(__FILE__ ) ) );337 wp_enqueue_style( ‘qts_front_styles’ );338 }339340341342 /**343 * print front end styles344 *345 * @since 1.1.7346 */347 public function print_plugin_styles() {348349 $css_path = dirname(__FILE__).’/assets/css/qts-default.css’;350351 if (!file_exists($css_path) || !is_readable($css_path)) {352 return;353 }354355 $default_css_file = file_get_contents($css_path, FILE_USE_INCLUDE_PATH);356357 $css = “<style media=\"screen\">\n";358 $css .= “$default_css_file\n";359 $css .="</style>\n";360 echo $css;361 }362 363 364365 /**366 * actions when deactivating the plugin367 *368 * @since 1.0369 */370 public function deactivate() {371 global $wp_rewrite;372 373 // regenerate rewrite rules in db374 remove_action( 'generate_rewrite_rules’, array(&$this, ‘modify_rewrite_rules’) );375 $wp_rewrite->flush_rules();376 }377 378379380 /**381 * admin notice: update your old data 382 *383 * @since 1.0384 */385 function notice_update(){386 global $current_screen;387 388 if ($current_screen->id != ‘settings_page_qtranslate-slug-settings’) {389 390 echo “<div class=\"updated\">” . PHP_EOL;391 echo “<p><strong>Qtranslate Slug:</strong></p>” . PHP_EOL;392 printf(“<p>%s <a href=\"%s\” class=\"button\">%s</a></p>", __('Please update your old data to the new system.’, ‘qts’), add_query_arg(array(‘page’ => ‘qtranslate-slug-settings’), ‘options-general.php’), __('upgrade now’, ‘qts’)) . PHP_EOL;393 echo “</div>” . PHP_EOL;394 }395 }396 397 398 399 /**400 * admin notice: update your old data 401 *402 * @since 1.0403 */404 function notice_dependences(){405406 $ornewer=__(‘or newer’,’qts’);407 $info_url=admin_url(‘plugin-install.php?tab=plugin-information’);408 echo ‘<div class="error">’ . PHP_EOL;409 echo ‘<p><strong>Qtranslate Slug:</strong></p>’ . PHP_EOL;410 echo '<p>’;411 printf(__('This plugin requires at least %s and either %s, or %s, or %s’, ‘qts’),412 '<strong>WordPress 4.0</strong>’,413 '<a href="’.$info_url.’&plugin=qtranslate-x&TB_iframe=true” class="thickbox” aria-label="’.__(‘More information about’, ‘qts’).’ '.’qTranslate-X” data-title="qTranslate-X"><strong>qTranslate-X</strong></a> (2.9 ‘.$ornewer.’)',414 '<a href="’.$info_url.’&plugin=mqtranslate&TB_iframe=true” class="thickbox” aria-label="’.__(‘More information about’, ‘qts’).’ '.’mqTranslate” data-title="mqTranslate"><strong>mqTranslate</strong></a> (2.6.2.4 ‘.$ornewer.’)',415 '<a href="’.$info_url.’&plugin=qtranslate&TB_iframe=true" class="thickbox" aria-label="’.__(‘More information about’, ‘qts’).’ '.’qTranslate" data-title="qTranslate"><strong>qTranslate</strong></a> (2.5.8 ‘.$ornewer.’)');416 echo ‘</p>’ . PHP_EOL;417 echo ‘</div>’ . PHP_EOL; 418 }419 420 421 422 /**423 * checks if old table ‘qtranslate_slug’ exists and is not empty424 * 425 * @return object | false426 *427 * @since 1.0428 */429 public function check_old_data() {430 global $wpdb;431 432 if ($this->old_data === false) {433 return false;434 }435 436 $table_name = $wpdb->get_var(“SHOW TABLES LIKE '{$wpdb->prefix}qtranslate_slug’”);437 438 if (!empty($table_name)) {439 $this->old_data = $wpdb->get_results(“SELECT * FROM {$wpdb->prefix}qtranslate_slug”);440 }441 442 if ( empty($table_name) || empty($this->old_data) ) {443 $this->old_data = false;444 }445 446 return $this->old_data;447 }448 449 450 451 /**452 * actions when deactivating the plugin453 *454 * @since 1.0455 */456 private function check_old_versions() {457 458 if ( $this->check_old_data() ) {459 add_action('admin_notices’, array(&$this, ‘notice_update’));460 }461 }462 463 464 465 /**466 * Initialise the Class with all hooks467 *468 * @since 1.0469 */470 function init() {471 472 load_plugin_textdomain( 'qts’, false, basename( plugin_dir_path( dirname( __FILE__ ) ) ) . ‘/languages’ );473 474 // checking plugin activate475 if ( self::block_activate() ) {476 if (is_admin()) {477 add_action('admin_notices’, array(&$this, ‘notice_dependences’));478 }479 return;480 } else {481 remove_action(‘admin_notices’, array(&$this, ‘notice_dependences’));482 }483 if ( is_admin() ) {484 include_once(dirname(__FILE__).’/qtranslate-slug-settings.php’);485 }486 487 global $q_config;488 489 // until we get a proper function, this will make it for it.490 $this->current_lang = $q_config[‘language’];491 $this->enabled_languages = $q_config[‘enabled_languages’];492 $this->default_language = $q_config[‘default_language’];493 $this->set_plugin_prefix();494 $this->set_url_path_mode();495 496 if ( is_admin() ) {497 498 $this->check_old_versions();499 500 501 // add filters502 add_filter( 'qts_validate_post_slug’, array(&$this, ‘validate_post_slug’), 0, 3 );503 add_filter( 'qts_validate_post_slug’, array(&$this, ‘unique_post_slug’), 1, 3 );504 add_filter( 'qts_validate_term_slug’, array(&$this, ‘validate_term_slug’), 0, 3 );505 add_filter( 'qts_validate_term_slug’, array(&$this, ‘unique_term_slug’), 1, 3 );506 add_filter( 'wp_get_object_terms’, array(&$this, ‘get_object_terms’), 0, 4 );507 add_filter( 'get_terms’, array(&$this, ‘get_terms’), 0, 3 );508 509 // admin actions510 add_action( 'admin_menu’, array(&$this, ‘add_slug_meta_box’) );511 add_action( 'admin_menu’, array(&$this, ‘remove_defaultslug_meta_box’) );512 add_action( 'save_post’, array(&$this, ‘save_postdata’), 605, 2 );513 add_action( 'delete_term’, array(&$this, ‘delete_term’), 0, 3);514 add_action( 'created_term’, array(&$this, ‘save_term’), 605, 3);515 add_action( 'edited_term’, array(&$this, ‘save_term’), 605, 3 );516 add_action( 'admin_head’, array(&$this, ‘hide_slug_box’), 900 );517 518 add_action( 'init’, array(&$this, ‘taxonomies_hooks’), 805 );519 520 add_action( 'admin_head’, array(&$this, ‘hide_quick_edit’), 600 );521 if(!defined(‘QTRANSLATE_FILE’))522 add_action( ‘admin_init’, array(&$this, ‘fix_nav_menu’) );523 524 } else {525 526 add_filter( ‘request’, array(&$this, ‘filter_request’) );527528 // adds external style file529 $qts_options = $this->get_options();530 if ( !isset($qts_options[QTS_PREFIX.’styles’]) || $qts_options[QTS_PREFIX.’styles’] == “file” ) {531 add_action( ‘wp_enqueue_scripts’, array( &$this, ‘register_plugin_styles’ ) );532 } elseif ($qts_options[QTS_PREFIX.’styles’] == “minified” ) {533 add_action( ‘wp_enqueue_scripts’, array( &$this, ‘register_plugin_styles_min’ ) );534 } elseif ($qts_options[QTS_PREFIX.’styles’] == “inline” ) {535 add_action( 'wp_print_styles’, array( &$this, ‘print_plugin_styles’ ), 20 );536 }537 }538 //FIXME: query vars are broken539 add_filter( 'query_vars’, array(&$this, ‘query_vars’));540 add_action( 'generate_rewrite_rules’, array(&$this, ‘modify_rewrite_rules’) );541 542 // remove from qtranslate the discouraged meta http-equiv, inline styles543 // (including flag URLs) and wrong hreflang links 544 545 remove_action('wp_head’, $this->get_plugin_prefix() . ‘header’);546 if( “qtranxf_” === $this->get_plugin_prefix() ) {547 remove_action('wp_head’, $this->get_plugin_prefix() . ‘wp_head’);548 }549 550 // add proper hreflang links551 add_action('wp_head’,array(&$this, ‘qtranslate_slug_header_extended’));552 553 // remove some Qtranslate filters554 remove_filter( 'page_link’, $this->get_plugin_prefix() . ‘convertURL’ );555 remove_filter( 'post_link’, $this->get_plugin_prefix() . ‘convertURL’ );556 remove_filter( 'category_link’, $this->get_plugin_prefix() . ‘convertURL’ );557 remove_filter( 'tag_link’, $this->get_plugin_prefix() . ‘convertURL’ );558 559 //FIXME: query vars are broken560 add_filter( ‘qts_permastruct’ , array(&$this, ‘get_extra_permastruct’), 0, 2);561 add_filter( 'qts_url_args’, array(&$this, ‘parse_url_args’), 0, 1);562 add_filter( 'home_url’, array(&$this, ‘home_url’), 10, 4); 563 add_filter( 'post_type_link’, array(&$this, ‘post_type_link’), 600, 4 );564 add_filter( 'post_link’, array(&$this, ‘post_link’), 0, 3 );565 add_filter( '_get_page_link’, array(&$this, ‘_get_page_link’), 0, 2 );566 add_filter( 'term_link’, array(&$this, ‘term_link’), 600, 3 );567 568 add_filter( 'single_term_title’, $this->get_plugin_prefix() . 'useTermLib’, 805 );569 add_filter( 'get_blogs_of_user’, array(&$this, ‘blog_names’), 1 );570 add_action( 'widgets_init’, array(&$this, ‘widget_init’), 100 );571 // Add specific CSS class to body class based on current lang572 add_filter('body_class’, array($this, ‘qts_body_class’), 600, 1 );573 574 add_filter( 'nav_menu_attr_title’, array($this, ‘qts_filter_attr_title’), 0, 3 );575576577 }578 579 /**580 * Filters menu link attribute581 * 582 * @since 1.1.9583 */ 584 function qts_filter_attr_title( $attr_title ) {585 return $this->qts_quickuse($attr_title);586 }587588589 /**590 * Adds proper links to the content with available translations.591 * Fixes issue #25592 * 593 * @global $qtranslate_slug used to convert the url594 * @global $q_config available languages595 *596 * @since 1.1.8597 */ 598 public function qtranslate_slug_header_extended(){599 if(is_404()) { return; }600 601 //taken from qtx but see our #341 ticket for clarification 602 echo '<link hreflang="x-default" href="’.esc_url($this->get_current_url($this->default_language)) .’" rel="alternate" />’.PHP_EOL;603 foreach($this->get_enabled_languages() as $language) {604605 echo '<link hreflang="’.$language.’" href="’.esc_url($this->get_current_url($language)).’" rel="alternate" />’."\n";606 }607 }608 609610 /**611 * Add a class based on the current language612 * @param array $classes list of classes613 */614 public function qts_body_class( $classes ) {615 $classes[] = call_user_func($this->get_plugin_prefix() . ‘getLanguage’);616 return $classes;617 }618619 /**620 * Finds the translated slug of the given post 621 * based on: https://wordpress.org/support/topic/permalink-for-other-languages 622 * @param int $id the post id623 * @param string $lang which language to look for624 * @return string the slug or empty if not found625 * @author vbkun626 * @since 1.1.13627 */ 628 public function get_slug($id, $lang){629 $slugArray = get_post_meta( $id, ‘_qts_slug_’.$lang );630 return !empty($slugArray) ? $slugArray[0] : "";631 }632633 // TODO: properly test this634 /**635 * Get text in $lang, or in the current lang636 *637 * @package Qtranslate Slug638 * @since 1.1.9639 * @param string $text the whole text640 * @param string $lang (optional) get the text in this language, or if empty, the current641 * @return array the text in the required language642 */643 public function qts_quickuse( $text,$lang=’’ ){644 $lang = ‘’ == $lang ? $this->current_lang : $lang;645 $parsed_text = call_user_func($this->get_plugin_prefix() . 'getSortedLanguages’,$text);646 if( !empty($parsed_text[$lang])){647 return $parsed_text[$lang]; 648 } 649 }650 651652 /**653 * Adds news rules to translate the URL bases, 654 * this function must be called on flush_rewrite or ‘flush_rewrite_rules’ 655 * 656 * @param object $wp_rewrite657 *658 * @since 1.0659 */ 660 public function modify_rewrite_rules() {661 662 // post types rules663 $post_types = get_post_types( array(‘_builtin’ => false ), ‘objects’);664 foreach ( $post_types as $post_type ) {665 $this->generate_extra_rules( $post_type->name );666 }667 668 // taxonomies rules669 $taxonomies = $this->get_public_taxonomies();670 foreach ( $taxonomies as $taxonomy ) {671 $this->generate_extra_rules( $taxonomy->name );672 }673 }674 675 676 677 /**678 * Helper: news rules to translate the URL bases679 * 680 * @param string $name name of extra permastruct681 * @param string $type ‘post_type’ or 'taxonomy’682 *683 * @since 1.0684 */685 private function generate_extra_rules( $name = false ) {686 global $wp_rewrite;687 688 foreach ($this->get_enabled_languages() as $lang):689 690 if ( $base = $this->get_base_slug( $name, $lang) ):691 692 $struct = $wp_rewrite->extra_permastructs[$name];693 694 if ( is_array( $struct ) ) {695 if ( count( $struct ) == 2 )696 $rules = $wp_rewrite->generate_rewrite_rules( “/$base/%$name%", $struct[1] );697 else698 $rules = $wp_rewrite->generate_rewrite_rules( “/$base/%$name%", $struct[‘ep_mask’], $struct[‘paged’], $struct[‘feed’], $struct[‘forcomments’], $struct[‘walk_dirs’], $struct[‘endpoints’] );699 } else {700 $rules = $wp_rewrite->generate_rewrite_rules( “/$base/%$name%” );701 }702 703 $wp_rewrite->rules = array_merge($rules, $wp_rewrite->rules);704 705 endif;706 707 endforeach;708 }709 710 711 712 /**713 * Helper that gets a base slug stored in options714 * 715 * @param string $name of extra permastruct716 * @return string base slug for ‘post_type’ and ‘language’ or false717 *718 * @since 1.0719 */720 public function get_base_slug($name = false, $lang = false) {721 722 if ( !$name || !$lang ) {723 return false;724 }725 726 if ( taxonomy_exists($name) ) {727 $type = 'taxonomy’;728 } else if ( post_type_exists($name) ) {729 $type = 'post_type’;730 } else {731 return false;732 }733 734 $qts_options = $this->get_options();735 736 $option_name = QTS_PREFIX . $type . ‘_’ . $name;737 738 if ( !isset($qts_options[$option_name]) || empty($qts_options[$option_name]) ) {739 return false;740 }741 742 if (isset($qts_options[$option_name][$lang])) {743 return $qts_options[$option_name][$lang];744 }745 746 return false;747 }748 749 750 751 /**752 * Helper: returns public built-in and not built-in taxonomies753 * 754 * @return array of public taxonomies objects755 *756 * @since 1.0757 */758 private function get_public_taxonomies() {759 760 $builtin = get_taxonomies( array( ‘public’ => true, ‘show_ui’ => true, ‘_builtin’ => true ), ‘object’); 761 $taxonomies = get_taxonomies( array( ‘public’ => true, ‘show_ui’ => true, ‘_builtin’ => false ), ‘object’ ); 762 763 return array_merge( $builtin, $taxonomies );764 }765 766 767 768 /**769 * parse and adds $_GET args passed to an url770 * 771 * @param string $url parameters772 * @param string $lang processed773 * @return string converted url774 *775 * @since 1.0776 */777 public function parse_url_args( $url ) {778 global $q_config; //TODO: q_config : url_info, url_mode779780 if (is_admin()) {781 return $url;782 }783 784 $url = preg_replace('/&/’, '&’, $url);785 786 // if no permalink structure ads ?lang=en787 $base_query = parse_url($q_config[‘url_info’][‘original_url’]);788 // FIXME: why we do this :789 $base_args = isset($base_query[‘query’]) ? wp_parse_args($base_query[‘query’]) : array();790 791 if ( empty($this->permalink_structure) || $q_config[‘url_mode’] == 1 ) {792 $base_args[‘lang’] = $this->get_lang();793794 }795796 // rebuild query with all args797 $url = add_query_arg($base_args, $url);798799 $url = str_replace('/?’, '?’, $url); // TODO: hack: improve this code800 $url = str_replace('?’, '/?’, $url); // TODO: hack: improve this code801 802 return $url;803 }804 805 806 807 /**808 * Fix get_page_by_path when querying vars809 * 810 * @param $query_vars objec query vars founded811 * @return object $query_vars processed812 *813 * @since 1.0814 */815 public function query_vars( $query_vars ) {816 global $wp, $wp_rewrite;817818 $wp->query_vars = array();819 $post_type_query_vars = array();820821 // Fetch the rewrite rules.822 $rewrite = $wp_rewrite->wp_rewrite_rules();823824 if ( ! empty($rewrite) ) {825 // If we match a rewrite rule, this will be cleared.826 $error = '404’;827 $wp->did_permalink = true;828829 if ( isset($_SERVER[‘PATH_INFO’]) ) {830 $pathinfo = $_SERVER[‘PATH_INFO’];831 } else {832 $pathinfo = '’;833 }834 $pathinfo_array = explode('?’, $pathinfo);835 $pathinfo = str_replace(“%", “%25", $pathinfo_array[0]);836 $req_uri = $_SERVER[‘REQUEST_URI’];837 $req_uri_array = explode('?’, $req_uri);838 $req_uri = $req_uri_array[0];839 $self = $_SERVER[‘PHP_SELF’];840 $home_path = parse_url(home_url());841 842 if ( isset($home_path[‘path’]) ) {843 $home_path = $home_path[‘path’];844 } else {845 $home_path = '’;846 }847 $home_path = trim($home_path, ‘/’);848849 // Trim path info from the end and the leading home path from the850 // front. For path info requests, this leaves us with the requesting851 // filename, if any. For 404 requests, this leaves us with the852 // requested permalink.853 $req_uri = str_replace($pathinfo, '’, $req_uri);854 $req_uri = trim($req_uri, ‘/’);855 $req_uri = preg_replace(“|^$home_path|", '’, $req_uri);856 $req_uri = trim($req_uri, ‘/’);857 if ($GLOBALS[‘q_config’][‘url_mode’] == $this->get_url_path_mode()) {858 $req_uri = preg_replace(“/^{$GLOBALS[‘q_config’][‘language’]}(\/|$)/", '’, $req_uri);859 }860 $pathinfo = trim($pathinfo, ‘/’);861 $pathinfo = preg_replace(“|^$home_path|", '’, $pathinfo);862 $pathinfo = trim($pathinfo, ‘/’);863 $self = trim($self, ‘/’);864 $self = preg_replace(“|^$home_path|", '’, $self);865 $self = trim($self, ‘/’);866867 // The requested permalink is in $pathinfo for path info requests and868 // $req_uri for other requests.869 if ( ! empty($pathinfo) && !preg_match(‘|^.*’ . $wp_rewrite->index . '$|’, $pathinfo) ) {870 $request = $pathinfo;871 } else {872 // If the request uri is the index, blank it out so that 873 // we don’t try to match it against a rule.874 if ( $req_uri == $wp_rewrite->index )875 $req_uri = '’;876 $request = $req_uri;877 }878 879 $wp->request = $request;880881 // Look for matches.882 $request_match = $request;883 if ( empty( $request_match ) ) {884 // An empty request could only match against ^$ regex885 if ( isset( $rewrite[‘$’] ) ) {886 $wp->matched_rule = '$’;887 $query = $rewrite[‘$’];888 $matches = array(‘’);889 }890 } else if ( $req_uri != ‘wp-app.php’ ) {891 foreach ( (array) $rewrite as $match => $query ) {892 // If the requesting file is the anchor of the match, prepend it to the path info.893 if ( ! empty($req_uri) && strpos($match, $req_uri) === 0 && $req_uri != $request ) {894 $request_match = $req_uri . ‘/’ . $request;895 }896897 if ( preg_match(“#^$match#", $request_match, $matches) || preg_match(“#^$match#", urldecode($request_match), $matches) ) {898899 if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/’, $query, $varmatch ) ) {900 // this is a verbose page match, lets check to be sure about it901 if ( ! $page_foundid = $this->get_page_by_path( $matches[ $varmatch[1] ] ) ) {902 continue;903 } else {904 wp_cache_set('qts_page_request’, $page_foundid); // caching query :)905 } 906 }907908 // Got a match.909 $wp->matched_rule = $match;910 break;911 }912 }913 }914915 if ( isset( $wp->matched_rule ) ) {916 // Trim the query of everything up to the '?’.917 $query = preg_replace(“!^.+\?!", '’, $query);918919 // Substitute the substring matches into the query.920 $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));921922 $wp->matched_query = $query;923924 // Parse the query.925 parse_str($query, $perma_query_vars);926927 // If we’re processing a 404 request, clear the error var928 // since we found something.929 unset( $_GET[‘error’] );930 unset( $error );931 }932933 // If req_uri is empty or if it is a request for ourself, unset error.934 if ( empty($request) || $req_uri == $self || strpos($_SERVER[‘PHP_SELF’], ‘wp-admin/’) !== false ) {935 unset( $_GET[‘error’] );936 unset( $error );937938 if ( isset($perma_query_vars) && strpos($_SERVER[‘PHP_SELF’], ‘wp-admin/’) !== false ) {939 unset( $perma_query_vars );940 }941942 $wp->did_permalink = false;943 }944 }945946 return count(array_diff($query_vars, $wp->public_query_vars)) > 0 ? $query_vars : $wp->public_query_vars;947 }948 949 950 951 /**952 * Function called when query parameters are processed by Wordpress.953 * 954 * @param $query query parameters955 * @return array() $query processed956 *957 * @since 1.0958 */959 function filter_request( $query ) {960 global $wp_query, $wp;961 // FIXME: why is this here? it breaks custom variables getter962 // https://wordpress.org/support/topic/cant-retrieve-public-query-variables963 if ((isset($wp->matched_query) || empty($query)) && ! isset($query[‘s’]) ) {964 $query = wp_parse_args($wp->matched_query);965 }966 967 foreach (get_post_types() as $post_type) {968 if ( array_key_exists($post_type, $query) && !in_array($post_type, array('post’, ‘page’)) ) {969 $query[‘post_type’] = $post_type;970 }971 }972 973 $page_foundit = false;974 975 // -> page976 if ( isset($query[‘pagename’]) || isset($query[‘page_id’]) ):977 978 $page = wp_cache_get(‘qts_page_request’);979 if (!$page) {980 $page = isset($query[‘page_id’]) ? get_post($query[‘page_id’]) : $this->get_page_by_path($query[‘pagename’]);981 }982 983 if (!$page) {984 return $query;985 }986 987 $id = $page->ID;988 $cache_array = array($page);989 update_post_caches($cache_array, ‘page’); // caching query :)990 wp_cache_delete(‘qts_page_request’);991 $query[‘pagename’] = get_page_uri($page);992 $function = 'get_page_link’;993 994 // -> custom post type995 elseif ( isset($query[‘post_type’]) ):996 if (count($query) == 1) {997 $function = 'get_post_type_archive_link’;998 $id = $query[‘post_type’];999 } else {1000 $page_slug = ( isset($query[‘name’]) && !empty($query[‘name’]) ) ? $query[‘name’] : $query[$query[‘post_type’]];1001 $page = $this->get_page_by_path($page_slug, OBJECT, $query[‘post_type’]);1002 if (!$page) return $query;1003 $id = $page->ID;1004 $cache_array = array($page);1005 update_post_caches($cache_array, $query[‘post_type’]); // caching query :)1006 $query[‘name’] = $query[$query[‘post_type’]] = get_page_uri($page); 1007 $function = 'get_post_permalink’;1008 }1009 // -> post1010 elseif ( isset($query[‘name’]) || isset($query[‘p’]) ):1011 1012 $post = isset($query[‘p’]) ? get_post($query[‘p’]) : $this->get_page_by_path($query[‘name’], OBJECT, ‘post’);1013 if (!$post) {1014 return $query;1015 }1016 $query[‘name’] = $post->post_name;1017 $id = $post->ID;1018 $cache_array = array($post);1019 update_post_caches($cache_array);1020 $function = 'get_permalink’;1021 1022 // -> category1023 elseif ( ( isset($query[‘category_name’]) || isset($query[‘cat’])) ):1024 if ( isset($query[‘category_name’]) ) {1025 $term_slug = $this->get_last_slash( $query[‘category_name’] );1026 }1027 $term = isset($query[‘cat’]) ? get_term($query[‘cat’], ‘category’) : $this->get_term_by('slug’, $term_slug, ‘category’);1028 if (!$term) {1029 return $query;1030 }1031 $cache_array = array($term);1032 update_term_cache($cache_array, ‘category’); // caching query :)1033 $id = $term->term_id;1034 $query[‘category_name’] = $term->slug; // uri1035 $function = 'get_category_link’;1036 1037 // -> tag1038 elseif ( isset($query[‘tag’]) ):1039 1040 $term = $this->get_term_by('slug’, $query[‘tag’], ‘post_tag’);1041 if (!$term) {1042 return $query;1043 }1044 $cache_array = array($term);1045 update_term_cache($cache_array, ‘post_tag’); // caching query :)1046 $id = $term->term_id;1047 $query[‘tag’] = $term->slug;1048 $function = 'get_tag_link’;1049 1050 endif;10511052 1053 // -> taxonomy1054 $taxonomies = get_taxonomies( array( ‘public’ => true, ‘_builtin’ => false ) );1055 foreach ($taxonomies as $term_name):1056 if ( isset($query[$term_name]) ) {1057 1058 $term_slug = $this->get_last_slash( $query[$term_name] );1059 $term = $this->get_term_by('slug’, $term_slug, $term_name);1060 if (!$term) {1061 return $query;1062 }1063 $cache_array = array($term);1064 update_term_cache($cache_array, $term_name); // caching query :)1065 $id = $term;1066 $query[$term_name] = $term->slug;1067 $function = 'get_term_link’;1068 1069 }1070 endforeach;10711072 // -> home url1073 if ( empty($query) ) {1074 1075 $function = 'home_url’;1076 $id = '’;1077 1078 }1079 1080 // -> search1081 if (isset($query[‘s’])) {10821083 $id = $query[‘s’];1084 $function="get_search_link";1085 1086 }1087 1088 if ( isset($function) ) {1089 1090 // parse all languages links1091 foreach( $this->get_enabled_languages() as $lang ) {1092 1093 $this->lang = $lang;1094 $this->current_url[$lang] = esc_url(apply_filters('qts_url_args’, call_user_func($function, $id)));1095 }10961097 $this->lang = false;1098 }1099 1100 return $query;1101 }1102 1103 1104 1105 /**1106 * Parse a hierarquical name and extract the last one1107 *1108 * @param string $lang Page path1109 * @return string1110 *1111 * @since 1.01112 */1113 public function get_current_url( $lang = false ) {11141115 if (!$lang) {1116 $lang = $this->get_lang();1117 }11181119 if (isset($this->current_url[$lang]) && !empty($this->current_url[$lang])) {1120 return $this->current_url[$lang];1121 }1122 return '’;1123 }1124 1125 1126 1127 /**1128 * Parse a hierarquical name and extract the last one 1129 *1130 * @param string $slug Page path1131 * @return string1132 *1133 * @since 1.01134 */1135 private function get_last_slash($slug) {1136 1137 $slug = rawurlencode( urldecode( $slug ) );1138 $slug = str_replace('%2F’, '/’, $slug);1139 $slug = str_replace('%20’, ' ', $slug);1140 $exploded_slug = explode('/’, $slug);11411142 return array_pop( $exploded_slug );1143 }1144114511461147 /**1148 * Retrieves a page id given its path.1149 *1150 * @param string $page_path Page path1151 * @param string $output Optional. Output type. OBJECT, ARRAY_N, or ARRAY_A. Default OBJECT.1152 * @param string $post_type Optional. Post type. Default page.1153 * @return mixed Null when complete.1154 *1155 * @since 1.01156 */1157 private function get_page_id_by_path($page_path, $output = OBJECT, $post_type = ‘page’) {1158 global $wpdb;11591160 $page_path = rawurlencode(urldecode($page_path));1161 $page_path = str_replace('%2F’, '/’, $page_path);1162 $page_path = str_replace('%20’, ' ', $page_path);1163 $parts = explode( '/’, trim( $page_path, ‘/’ ) );1164 $parts = array_map( 'esc_sql’, $parts );1165 $parts = array_map( 'sanitize_title_for_query’, $parts );1166 $in_string = “’". implode( “’,’", $parts ) . “’";1167 $meta_key = $this->get_meta_key();1168 $post_type_sql = $post_type;1169 $wpdb->escape_by_ref( $post_type_sql );1170 1171 $pages = $wpdb->get_results( “SELECT $wpdb->posts.ID, $wpdb->posts.post_parent, $wpdb->postmeta.meta_value FROM $wpdb->posts,$wpdb->postmeta WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id AND $wpdb->postmeta.meta_key = ‘$meta_key’ AND $wpdb->postmeta.meta_value IN ($in_string) AND ($wpdb->posts.post_type = ‘$post_type_sql’ OR $wpdb->posts.post_type = ‘attachment’)“, OBJECT_K );11721173 $revparts = array_reverse( $parts );11741175 $foundid = 0;1176 foreach ( (array) $pages as $page ) {1177 if ( $page->meta_value == $revparts[0] ) {1178 $count = 0;1179 $p = $page;1180 while ( $p->post_parent != 0 && isset( $pages[ $p->post_parent ] ) ) {1181 $count++;1182 $parent = $pages[ $p->post_parent ];1183 if ( ! isset( $revparts[ $count ] ) || $parent->meta_value != $revparts[ $count ] ) {1184 break;1185 }1186 $p = $parent;1187 }11881189 if ( $p->post_parent == 0 && $count+1 == count( $revparts ) && $p->meta_value == $revparts[ $count ] ) {1190 $foundid = $page->ID;1191 break;1192 }1193 }1194 }1195 1196 if ( $foundid ) {1197 return $foundid;1198 1199 } else {1200 1201 $last_part = array_pop($parts);1202 $page_id = $wpdb->get_var( “SELECT ID FROM $wpdb->posts WHERE post_name = ‘$last_part’ AND (post_type = ‘$post_type_sql’ OR post_type = ‘attachment’)" );1203 1204 if ( $page_id ) {1205 return $page_id;1206 }1207 }12081209 return null;1210 }1211 1212 1213 1214 1215 /**1216 * Retrieves a page given its path.1217 *1218 * @param string $page_path Page path1219 * @param string $output Optional. Output type. OBJECT, ARRAY_N, or ARRAY_A. Default OBJECT.1220 * @param string $post_type Optional. Post type. Default page.1221 * @return mixed Null when complete.1222 *1223 * @since 1.01224 */1225 private function get_page_by_path($page_path, $output = OBJECT, $post_type = ‘page’) {1226 1227 $foundid = $this->get_page_id_by_path($page_path, $output, $post_type);1228 if ( $foundid ) {1229 return get_post( $foundid, $output );1230 }1231 1232 return null;1233 }1234 1235 1236 1237 /**1238 * Ignores if the mod_rewrite func is the caller1239 *1240 * @return boolean1241 *1242 * @since 1.01243 */1244 private function ignore_rewrite_caller() {1245 1246 $backtrace = debug_backtrace();1247 1248 $ignore_functions = array('mod_rewrite_rules’, 'save_mod_rewrite_rules’, 'flush_rules’, 'rewrite_rules’, 'wp_rewrite_rules’, ‘query_vars’);1249 1250 if ( isset($backtrace[‘function’]) ) {1251 if (in_array($backtrace[‘function’], $ignore_functions)) {1252 return true;1253 }1254 } else {1255 foreach ($backtrace as $trace) {1256 if ( isset($trace[‘function’]) && in_array($trace[‘function’], $ignore_functions) ) {1257 return true;1258 }1259 }1260 }1261 1262 return false;1263 }1264 1265 1266 /**1267 * Retrieve the home url for a given site.1268 *1269 * @param int $blog_id (optional) Blog ID. Defaults to current blog.1270 * @param string $path (optional) Path relative to the home url.1271 * @param string $scheme (optional) Scheme to give the home url context. Currently 'http’, 'https’.1272 * @return string Home url link with optional path appended.1273 *1274 * @since 1.01275 */1276 public function home_url($url, $path, $scheme, $blog_id) {1277 if ( !in_array( $scheme, array( 'http’, ‘https’ ) ) ) {1278 $scheme = is_ssl() && !is_admin() ? ‘https’ : 'http’;1279 }12801281 if ( empty( $blog_id ) || !is_multisite() ) {1282 $url = get_option( ‘home’ );1283 } else {1284 $url = get_blog_option( $blog_id, ‘home’ );1285 }12861287 if ( ‘http’ != $scheme ) {1288 $url = str_replace( 'http://’, “$scheme://", $url );1289 }12901291 $ignore_caller = $this->ignore_rewrite_caller();12921293 if ( !empty( $path ) && is_string( $path ) && strpos( $path, ‘…’ ) === false ) {1294 $url .= ‘/’ . ltrim( $path, ‘/’ );1295 }12961297 if ( !$ignore_caller ) {1298 $url = call_user_func($this->get_plugin_prefix() . 'convertURL’, $url, $this->get_lang(), true);1299 }13001301 return $url;1302 }1303 1304 1305 1306 /**1307 * Filter that changes the permastruct depending … on what?1308 * 1309 * @param string $permastruct default permastruct given b wp_rewrite1310 * @param string $name the name of the extra permastruct1311 * @return string processed permastruct1312 *1313 * @since 1.01314 */1315 public function get_extra_permastruct( $permastruct = false, $name = false ) {1316 1317 if ( !$name || !$permastruct ) {1318 return '’;1319 }1320 1321 if ( $base = $this->get_base_slug($name, $this->get_lang()) ) {1322 return “/$base/%$name%";1323 }1324 1325 return $permastruct;1326 }1327 1328 1329 1330 /**1331 * Filter that translates the slug parts in a page link1332 * 1333 * @param $link the link for the page generated by Wordpress1334 * @param $id the id of the page1335 * @return the link translated1336 *1337 * @since 1.01338 */1339 public function post_type_link( $link, $post, $leavename, $sample ) {1340 global $wp_rewrite;13411342 if ( is_wp_error( $post ) ) {1343 return $post;1344 }13451346 $post_link = apply_filters( 'qts_permastruct’, $wp_rewrite->get_extra_permastruct($post->post_type), $post->post_type);1347 1348 $slug = get_post_meta( $post->ID, $this->get_meta_key(), true );1349 if (!$slug) {1350 $slug = $post->post_name;1351 }13521353 $draft_or_pending = isset($post->post_status) && in_array( $post->post_status, array( 'draft’, 'pending’, ‘auto-draft’ ) );13541355 $post_type = get_post_type_object($post->post_type);13561357 if ( !empty($post_link) && ( !$draft_or_pending || $sample ) ) {1358 if ( ! $leavename ) {1359 if ( $post_type->hierarchical )1360 $slug = $this->get_page_uri($post->ID);1361 $post_link = str_replace(“%$post->post_type%", $slug, $post_link);1362 }1363 1364 $post_link = home_url( user_trailingslashit($post_link) );1365 1366 } else {1367 1368 if ( $post_type->query_var && ( isset($post->post_status) && !$draft_or_pending ) ) {1369 $post_link = add_query_arg($post_type->query_var, $slug, ‘’);1370 } else {1371 $post_link = add_query_arg(array(‘post_type’ => $post->post_type, ‘p’ => $post->ID), ‘’);1372 }1373 1374 $post_link = home_url($post_link);1375 }13761377 return $post_link;1378 }1379 1380 1381 1382 /**1383 * Filter that translates the slug in a post link1384 * 1385 * @param $link the link generated by wordpress1386 * @param $post the post data1387 * @param $leavename parameter used by get_permalink. Whether to keep post name or page name. 1388 * @return the link translated1389 *1390 * @since 1.01391 */1392 public function post_link( $link, $post, $leavename ) {1393 global $q_config; //TODO: q_config : url_mode13941395 $rewritecode = array(1396 '%year%’,1397 '%monthnum%’,1398 '%day%’,1399 '%hour%’,1400 '%minute%’,1401 '%second%’,1402 $leavename? ‘’ : '%postname%’,1403 '%post_id%’,1404 '%category%’,1405 '%author%’,1406 $leavename? ‘’ : '%pagename%’,1407 );14081409 if ( empty($post->ID) ) {1410 return false;1411 }14121413 $permalink = $this->permalink_structure;14141415 if ( ‘’ != $permalink && !in_array($post->post_status, array('draft’, 'pending’, ‘auto-draft’)) ) {1416 $unixtime = strtotime($post->post_date);1417 1418 $category = '’;1419 if ( strpos($permalink, ‘%category%’) !== false ) {1420 $cats = get_the_category($post->ID);1421 if ( $cats ) {1422 usort($cats, ‘_usort_terms_by_ID’); // order by ID1423 1424 $category = get_term_meta($cats[0]->term_id, $this->get_meta_key(), true );1425 if (!$category) $category = $cats[0]->slug;1426 1427 if ( $parent = $cats[0]->parent )1428 $category = $this->get_category_parents($parent, false, '/’, true) . $category;1429 }1430 // show default category in permalinks, without1431 // having to assign it explicitly1432 if ( empty($category) ) {1433 $default_category = get_category( get_option( ‘default_category’ ) );1434 1435 $default_category_slug = get_term_meta($default_category->term_id, $this->get_meta_key(), true );1436 if (!$default_category_slug) $default_category_slug = $default_category->slug;1437 1438 $category = is_wp_error( $default_category ) ? ‘’ : $default_category_slug;1439 }1440 }14411442 $author = ‘’;1443 if ( strpos($permalink, ‘%author%’) !== false ) {1444 $authordata = get_userdata($post->post_author);1445 $author = $authordata->user_nicename;1446 }14471448 $date = explode(" “,date('Y m d H i s’, $unixtime));1449 1450 $post_slug = get_post_meta($post->ID, $this->get_meta_key(), true );1451 if (!$post_slug) {1452 $post_slug = $post->post_name;1453 }1454 1455 $rewritereplace =1456 array(1457 $date[0],1458 $date[1],1459 $date[2],1460 $date[3],1461 $date[4],1462 $date[5],1463 $post_slug,1464 $post->ID,1465 $category,1466 $author,1467 $post_slug,1468 );1469 $permalink = home_url( str_replace($rewritecode, $rewritereplace, $permalink));1470 if ($q_config[‘url_mode’] != 1) {1471 $permalink = user_trailingslashit($permalink, ‘single’);1472 }1473 } else { // if they’re not using the fancy permalink option1474 $permalink = home_url(‘?p=’ . $post->ID);1475 }1476 1477 return $permalink;1478 }1479 1480 1481 1482 /**1483 * Retrieve category parents with separator.1484 *1485 * @param int $id Category ID.1486 * @param bool $link Optional, default is false. Whether to format with link.1487 * @param string $separator Optional, default is '/’. How to separate categories.1488 * @param bool $nicename Optional, default is false. Whether to use nice name for display.1489 * @param array $visited Optional. Already linked to categories to prevent duplicates.1490 * @return string1491 *1492 * @since 1.01493 */1494 private function get_category_parents( $id, $link = false, $separator = '/’, $nicename = false, $visited = array() ) {1495 1496 $chain = '’;1497 if(phpversion() >= 5.4) {1498 $parent = get_category( $id );1499 } else {1500 $parent = &get_category( $id );1501 }15021503 if ( is_wp_error( $parent ) ) {1504 return $parent;1505 }15061507 if ( $nicename ) {15081509 $name = get_term_meta($parent->term_id, $this->get_meta_key(), true );1510 if (!$name) {1511 $name = $parent->slug;1512 }1513 } else {1514 $name = $parent->name;1515 }15161517 if ( $parent->parent && ( $parent->parent != $parent->term_id ) && !in_array( $parent->parent, $visited ) ) {1518 $visited[] = $parent->parent;1519 $chain .= $this->get_category_parents( $parent->parent, $link, $separator, $nicename, $visited );1520 }1521 1522 if ( $link ) {1523 $chain .= ‘<a href="’ . get_category_link( $parent->term_id ) . '” title="’ . esc_attr( sprintf( __( “View all posts in %s", “qts” ), $parent->name ) ) . ‘">’.$name.’</a>’ . $separator;1524 } else {1525 $chain .= $name.$separator;1526 }1527 1528 return $chain;1529 }1530 15311532 1533 1534 1535 /**1536 * Filter that translates the slug parts in a page link1537 * 1538 * @param $link the link for the page generated by Wordpress1539 * @param $id the id of the page1540 * @return the link translated1541 *1542 * @since 1.01543 */1544 public function _get_page_link( $link, $id ) {1545 global $post, $wp_rewrite, $q_config; //TODO: q_config : url_mode15461547 $current_post = $post;15481549 if ( !$id ) {1550 $id = (int) $post->ID;1551 } else {1552 if(phpversion() >= 5.4) {1553 $current_post = get_post($id);1554 } else {1555 $current_post = &get_post($id);1556 }1557 }15581559 $draft_or_pending = in_array( $current_post->post_status, array( 'draft’, 'pending’, ‘auto-draft’ ) );15601561 $link = $wp_rewrite->get_page_permastruct();15621563 if ( !empty($link) && ( isset($current_post->post_status) && !$draft_or_pending ) ) {1564 1565 $link = str_replace('%pagename%’, $this->get_page_uri($id), $link);1566 1567 $link = trim($link, ‘/’); // hack1568 $link = home_url(“/$link/”); // hack1569 1570 if ($q_config[‘url_mode’] != 1)1571 $link = user_trailingslashit($link, ‘page’);1572 1573 } else {1574 1575 $link = home_url(“?page_id=$id”);1576 }15771578 return $link;1579 }1580 1581 1582 1583 /**1584 * Builds URI for a page.1585 *1586 * Sub pages will be in the “directory” under the parent page post name.1587 *1588 * @param mixed $page Page object or page ID.1589 * @return string Page URI.1590 *1591 * @since 1.01592 */1593 private function get_page_uri($page) {1594 1595 if ( ! is_object($page) ) {1596 $page = get_post($page);1597 }1598 1599 $uri = get_post_meta( $page->ID, $this->get_meta_key(), true );1600 if (!$uri) {1601 $uri = $page->post_name;1602 }16031604 // A page cannot be it’s own parent.1605 if ( $page->post_parent == $page->ID ) {1606 return $uri;1607 }16081609 while ($page->post_parent != 0) {1610 $page = get_post($page->post_parent);1611 1612 $page_name = get_post_meta( $page->ID, $this->get_meta_key(), true );1613 if (!$page_name) {1614 $page_name = $page->post_name;1615 }1616 1617 $uri = $page_name . “/” . $uri;1618 }16191620 return $uri;1621 }1622 1623 1624 1625 /**1626 * Filter that translates the slug parts in a term link1627 * 1628 * @param $link the link for the page generated by Wordpress1629 * @param $term object1630 * @param $taxonomy object1631 * @return the link translated1632 *1633 * @since 1.01634 */1635 public function term_link( $link, $term, $taxonomy ) {1636 global $wp_rewrite;1637 1638 // parse normal term names for ?tag=tagname1639 if (empty($this->permalink_structure)) { return $link; }16401641 if ( !is_object($term) ) {1642 if ( is_int($term) ) {1643 $term = &get_term($term, $taxonomy);1644 } else {1645 $term = $this->get_term_by('slug’, $term, $taxonomy);1646 }1647 }16481649 if ( !is_object($term) ) {1650 $term = new WP_Error('invalid_term’, __('Empty Term’, ‘qts’));1651 }16521653 if ( is_wp_error( $term ) ) {1654 return $term;1655 }16561657 $taxonomy = $term->taxonomy;16581659 $termlink = apply_filters( 'qts_permastruct’, $wp_rewrite->get_extra_permastruct($taxonomy), $taxonomy);1660 1661 $slug = get_term_meta( $term->term_id, $this->get_meta_key(), true );1662 if (!$slug) {1663 $slug = $term->slug;1664 }1665 1666 $t = get_taxonomy($taxonomy);16671668 if ( empty($termlink) ) {1669 if ( ‘category’ == $taxonomy ) {1670 $termlink = ‘?cat=’ . $term->term_id;1671 } elseif ( $t->query_var ) {1672 $termlink = “?$t->query_var=$slug";1673 } else {1674 $termlink = “?taxonomy=$taxonomy&term=$slug";1675 }1676 $termlink = home_url($termlink);1677 } else {1678 if ( $t->rewrite[‘hierarchical’] ) {1679 $hierarchical_slugs = array();1680 $ancestors = get_ancestors($term->term_id, $taxonomy);1681 foreach ( (array)$ancestors as $ancestor ) {1682 $ancestor_term = get_term($ancestor, $taxonomy);1683 1684 $ancestor_slug = get_term_meta( $ancestor_term->term_id, $this->get_meta_key(), true );1685 if (!$ancestor_slug) {1686 $ancestor_slug = $ancestor_term->slug;1687 }1688 1689 $hierarchical_slugs[] = $ancestor_slug;1690 }1691 $hierarchical_slugs = array_reverse($hierarchical_slugs);1692 $hierarchical_slugs[] = $slug;1693 $termlink = str_replace(“%$taxonomy%", implode('/’, $hierarchical_slugs), $termlink);1694 } else {1695 $termlink = str_replace(“%$taxonomy%", $slug, $termlink);1696 }1697 $termlink = home_url( user_trailingslashit($termlink, ‘category’) );1698 }1699 return $termlink;1700 }1701 1702 1703 1704 /**1705 * Get all Term data from database by Term field and data.1706 *1707 * @param (string) $field Either 'slug’, 'name’, or 'id’1708 * @param (string|int) $value Search for this term value1709 * @param (string) $taxonomy Taxonomy Name1710 * @param (string) $output Constant OBJECT, ARRAY_A, or ARRAY_N1711 * @param (string) $filter Optional, default is raw or no WordPress defined filter will applied.1712 * @return (mixed) Term Row from database. Will return false if $taxonomy does not exist or $term was not found.1713 *1714 * @since 1.01715 */1716 private function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = ‘raw’) {1717 global $wpdb;1718 1719 $original_field = $field;17201721 if ( ! taxonomy_exists($taxonomy) ) {1722 return false;1723 }17241725 if ( ‘slug’ == $field ) {1726 $field = ‘m.meta_key = \’’.$this->get_meta_key().’\’ AND m.meta_value’;1727 $value = sanitize_title($value);1728 if ( empty($value) )1729 return false;1730 } else if ( ‘name’ == $field ) {1731 // Assume already escaped1732 $value = stripslashes($value);1733 $field = 't.name’;1734 } else {1735 $term = get_term( (int) $value, $taxonomy, $output, $filter);1736 if ( is_wp_error( $term ) )1737 $term = false;1738 return $term;1739 }1740 1741 $term = $wpdb->get_row( $wpdb->prepare( “SELECT t.*, tt.* FROM $wpdb->terms AS t, $wpdb->term_taxonomy AS tt, $wpdb->termmeta AS m WHERE t.term_id = tt.term_id AND tt.term_id = m.term_id AND tt.taxonomy = %s AND $field = %s LIMIT 1", $taxonomy, $value) );17421743 if ( !$term && ‘slug’ == $original_field ) {1744 $field = 't.slug’;1745 $term = $wpdb->get_row( $wpdb->prepare( “SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND $field = %s LIMIT 1", $taxonomy, $value) );1746 }1747 1748 if ( !$term ) {1749 return false;1750 }17511752 wp_cache_add($term->term_id, $term, $taxonomy);17531754 $term = apply_filters('get_term’, $term, $taxonomy);1755 $term = apply_filters(“get_$taxonomy", $term, $taxonomy);1756 $term = sanitize_term($term, $taxonomy, $filter);17571758 if ( $output == OBJECT ) {1759 return $term;1760 } elseif ( $output == ARRAY_A ) {1761 return get_object_vars($term);1762 } elseif ( $output == ARRAY_N ) {1763 return array_values(get_object_vars($term));1764 } else {1765 return $term;1766 }1767 }1768176917701771 /**1772 * Fix for:1773 * - Taxonomy names in Taxonomy Manage page1774 * - ‘Popular Tags’ in Taxonomy (Tags) Manage page1775 * - Category filter dropdown menu in Post Manage page1776 * - Category list in Post Edit page1777 * - ‘Most Used’ tags list in Post Edit page (but have issues when saving)1778 * 1779 * @param (array) $terms1780 * @param (string|array) $taxonomy1781 * @since 1.21782 */1783 function get_terms($terms, $taxonomy) {1784 1785 global $pagenow;17861787 if ( $pagenow != ‘admin-ajax.php’ ) {1788 1789 $meta = get_option(‘qtranslate_term_name’);1790 $lang = call_user_func($this->get_plugin_prefix() . ‘getLanguage’);1791 1792 1793 if ( !empty( $terms ) ) {1794 foreach ($terms as $term_index => $term) {1795 // after saving, dont do anything1796 if( ( isset($_POST[‘action’] ) && $_POST[‘action’] == “editedtag”) ||1797 !is_object( $term ) ) {1798 return $terms;1799 }1800 if( isset( $meta[$term->name][$lang] ) ) {1801 $term->name = $meta[$term->name][$lang];1802 }1803 }1804 }1805 }1806 return $terms;1807 }1808180918101811 /**1812 * Fix for:1813 * - Taxonomy & custom taxonomy names in Post Manage page1814 * - List of tags already added to the post in Post 1815 * - Edit page (but have issues when saving)1816 *1817 * @param (array) $terms1818 * @param (int|array) $obj_id1819 * @param (string|array) $taxonomy1820 * @param (array) $taxonomy1821 * @since 1.21822 */ 1823 function get_object_terms($terms, $obj_id, $taxonomy, $args) {1824 1825 global $pagenow;1826 1827 // Although in post edit page the tags are translated,1828 // but when saving/updating the post Wordpress considers1829 // the translated tags as new tags. Due to this1830 // issue I limit this ‘hack’ to the post manage1831 // page only.1832 if ( $pagenow == ‘edit.php’ ) {1833 1834 // $taxonomy output seems to be wrapped1835 // in single quotes, thus remove them to1836 // make the output valid1837 $tax = str_replace(“’", “", $taxonomy);1838 1839 // get the name from qtx1840 $meta = get_option(‘qtranslate_term_name’);1841 $lang = call_user_func($this->get_plugin_prefix() . ‘getLanguage’);1842 1843 if ( !empty( $terms ) ) {1844 foreach ($terms as $term) {1845 if( isset( $meta[$term->name][$lang] ) ) {1846 $term->name = $meta[$term->name][$lang];1847 }1848 }1849 }1850 1851 }1852 return $terms;1853 }1854185518561857 /**1858 * hide quickedit slug1859 * 1860 * @since 1.01861 */1862 public function hide_quick_edit() {1863 echo “<!-- QTS remove quick edit box -->” . PHP_EOL;1864 echo “<style type=\"text/css\” media=\"screen\">” . PHP_EOL;1865 echo " .inline-edit-row fieldset.inline-edit-col-left .inline-edit-col *:first-child + label + label { display: none !important }” . PHP_EOL;1866 echo “</style>” . PHP_EOL;1867 }1868 1869 1870 1871 /**1872 * Hide auttomatically the wordpress slug box in edit terms page1873 *1874 * @since 1.01875 */1876 public function hide_slug_box() {1877 global $pagenow;1878 switch ( $pagenow ):1879 case 'edit-tags.php’:1880 1881 echo “<!-- QTS remove slug box -->” . PHP_EOL;1882 echo “<script type=\"text/javascript\” charset=\"utf-8\">” . PHP_EOL;1883 echo " jQuery(document).ready(function($){” . PHP_EOL;1884 echo " $(\"#tag-slug\”).parent().hide();” . PHP_EOL;1885 echo " $(\".form-field td #slug\”).parent().parent().hide();” . PHP_EOL;1886 echo " });” . PHP_EOL;1887 echo “</script>” . PHP_EOL;1888 break;1889 /* 1890 case 'post.php’:1891 1892 echo “<!-- QTS remove slug box -->” . PHP_EOL;1893 echo “<style type=\"text/css\” media=\"screen\">” . PHP_EOL;1894 echo " #slugdiv2 { display: none !important}” . PHP_EOL;1895 echo “</style>” . PHP_EOL;1896 break;*/1897 endswitch;1898 }1899 /**1900 * Hide auttomatically the wordpress slug box in edit posts page1901 * User should still be able to use it if needed1902 * @since 1.01903 */1904 public function remove_defaultslug_meta_box() {1905 1906 if (is_admin()) {1907 if( !current_user_can(‘manage_options’) ) {1908 remove_meta_box('slugdiv’, 'post’, ‘normal’);1909 }1910 }1911 }19121913 /**1914 * Creates a metabox for every post, page and post type avaiable1915 *1916 * @since 1.01917 */1918 public function add_slug_meta_box() {1919 1920 if ( function_exists( ‘add_meta_box’ ) ) {1921 1922 $context = apply_filters(“qts_admin_meta_box_context","side”);1923 $priority = apply_filters(“qts_admin_meta_box_priority","high”);19241925 add_meta_box( 'qts_sectionid’, __('Slug QTS’, ‘qts’), array(&$this, ‘draw_meta_box’), 'post’, $context, $priority);1926 add_meta_box( 'qts_sectionid’, __('Slug QTS’, ‘qts’), array(&$this, ‘draw_meta_box’), 'page’, $context, $priority);1927 1928 foreach ( get_post_types( array(‘_builtin’ => false ) ) as $ptype ) {1929 add_meta_box( 'qts_sectionid’, __('Slug QTS’, ‘qts’), array(&$this, ‘draw_meta_box’), $ptype, $context, $priority );1930 }1931 }1932 }1933 1934 1935 1936 /**1937 * Shows the fields where insert the translated slugs in the post and page edit form.1938 *1939 * @param $post (object) current post object1940 *1941 * @since 1.01942 */1943 public function draw_meta_box( $post ) {1944 global $q_config; // //TODO: q_config : language_name19451946 // Use nonce for verification1947 echo “<table style=\"width:100%\">” . PHP_EOL;1948 echo “<input type=\"hidden\” name=\"qts_nonce\” id=\"qts_nonce\” value=\"” . wp_create_nonce( ‘qts_nonce’ ) . “\” />” . PHP_EOL;1949 1950 foreach ($this->enabled_languages as $lang):1951 1952 $slug = get_post_meta( $post->ID, $this->get_meta_key($lang), true);1953 1954 $value = ( $slug ) ? htmlspecialchars( $slug , ENT_QUOTES ) : '’;1955 1956 echo “<tr>” . PHP_EOL;1957 echo “<th style=\"text-align:left; width:10%; color:#555 \"><label for=\"qts_{$lang}_slug\">".__($q_config[‘language_name’][$lang], ‘qtranslate’)."</label></th>” . PHP_EOL;1958 echo “<td><input type=\"text\” id=\"qts_{$lang}_slug\” name=\"qts_{$lang}_slug\” value=\"” . urldecode($value) . “\” style=\"width:90%; margin-left:10%; color:#777\” /></td>” . PHP_EOL;1959 echo “</tr>” . PHP_EOL;1960 1961 endforeach;1962 1963 echo ‘</table>’ . PHP_EOL;1964 }1965 1966 1967 1968 /**1969 * Sanitize title as slug, if empty slug1970 * 1971 * @param $post (object) the post object1972 * @param $slug (string) the slug name1973 * @param $lang (string) the language1974 * @return the slug validated1975 *1976 * @since 1.01977 */1978 public function validate_post_slug( $slug, $post, $lang ) { 1979 1980 $post_title = trim(call_user_func($this->get_plugin_prefix() . 'use’,$lang, $_POST[‘post_title’]));1981 $post_name = get_post_meta($post->ID, $this->get_meta_key($lang), true);1982 if (!$post_name) {1983 $post_name = $post->post_name;1984 }1985 1986 //TODO: if has a slug, test and use it 1987 //TODO: and then replace the default slug with the dafault language slug1988 $name = ( $post_title == ‘’ || strlen($post_title) == 0 ) ? $post_name : $post_title;1989 1990 $slug = trim($slug);1991 1992 $slug = (empty($slug)) ? sanitize_title($name) : sanitize_title($slug);19931994 return htmlspecialchars( $slug , ENT_QUOTES );1995 }1996 1997 1998 1999 /**2000 * Validates post slug against repetitions per language2001 * 2002 * @param $post (object) the post object2003 * @param $slug (string) the slug name2004 * @param $lang (string) the language2005 * @return the slug validated2006 *2007 * @since 1.02008 */2009 public function unique_post_slug( $slug, $post, $lang ) {2010 2011 $original_status = $post->post_status;2012 2013 if ( in_array($post->post_status, array('draft’, ‘pending’)) ) {2014 $post->post_status = 'publish’;2015 }2016 2017 $slug = $this->wp_unique_post_slug( $slug, $post->ID, $post->post_status, $post->post_type, $post->post_parent, $lang );2018 2019 $post->post_status = $original_status;2020 2021 return $slug;2022 }2023 2024 2025 2026 /**2027 * Computes a unique slug for the post and language, when given the desired slug and some post details.2028 *2029 * @param string $slug the desired slug (post_name)2030 * @param integer $post_ID2031 * @param string $post_status no uniqueness checks are made if the post is still draft or pending2032 * @param string $post_type2033 * @param integer $post_parent2034 * @return string unique slug for the post, based on language meta_value (with a -1, -2, etc. suffix)2035 *2036 * @since 1.02037 */2038 public function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent, $lang ) {2039 if ( in_array( $post_status, array( 'draft’, 'pending’, ‘auto-draft’ ) ) ) {2040 return $slug;2041 }20422043 global $wpdb, $wp_rewrite;20442045 $feeds = $wp_rewrite->feeds;2046 if ( ! is_array( $feeds ) ) {2047 $feeds = array();2048 }2049 2050 $meta_key = $this->get_meta_key($lang);2051 if ( ‘attachment’ == $post_type ) {2052 // Attachment slugs must be unique across all types.2053 $check_sql = “SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1";2054 $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) );20552056 if ( $post_name_check || in_array( $slug, $feeds ) || apply_filters( 'wp_unique_post_slug_is_bad_attachment_slug’, false, $slug ) ) {2057 $suffix = 2;2058 do {2059 // TODO: update unique_slug :: differs from current wp func ( 4.3.1 )2060 $alt_post_name = substr ($slug, 0, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";2061 $post_name_check = $wpdb->get_var( $wpdb->prepare($check_sql, $alt_post_name, $post_ID ) );2062 $suffix++;2063 } while ( $post_name_check );2064 $slug = $alt_post_name;2065 }2066 } else {2067 // TODO: update unique_slug :: missing hieararchical from current wp func ( 4.3.1 )2068 // Post slugs must be unique across all posts.2069 $check_sql = “SELECT $wpdb->postmeta.meta_value FROM $wpdb->posts,$wpdb->postmeta WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id AND $wpdb->postmeta.meta_key = ‘%s’ AND $wpdb->postmeta.meta_value = ‘%s’ AND $wpdb->posts.post_type = %s AND ID != %d LIMIT 1";2070 $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $meta_key, $slug, $post_type, $post_ID ) );20712072 // TODO: update unique_slug :: missing check for conflict with dates archive from current wp func ( 4.3.1 )2073 if ( $post_name_check || in_array( $slug, $feeds ) || apply_filters( 'wp_unique_post_slug_is_bad_flat_slug’, false, $slug, $post_type ) ) {2074 $suffix = 2;2075 do {2076 // TODO: update unique_slug :: same as above: differs from current wp func ( 4.3.1 )2077 $alt_post_name = substr( $slug, 0, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";2078 $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $meta_key, $alt_post_name, $post_type, $post_ID ) );2079 $suffix++;2080 } while ( $post_name_check );2081 $slug = $alt_post_name;2082 }2083 }20842085 return $slug;2086 }2087 2088 2089 2090 2091 /**2092 * Saves the translated slug when the page is saved2093 * 2094 * @param $post_id the post id2095 * @param $post the post object2096 *2097 * @since 1.02098 */2099 public function save_postdata( $post_id, $post ) {2100 2101 $post_type_object = get_post_type_object( $post->post_type);2102 2103 2104 if ((defined(‘DOING_AUTOSAVE’) && DOING_AUTOSAVE) // check autosave2105 || (!isset($_POST[‘post_ID’]) || $post_id != $_POST[‘post_ID’]) // check revision2106 || (isset($_POST[‘qts_nonce’]) && !wp_verify_nonce( $_POST[‘qts_nonce’], ‘qts_nonce’)) // verify nonce2107 || (!current_user_can($post_type_object->cap->edit_post, $post_id))) { // check permission2108 return $post_id;2109 }2110 foreach ($this->get_enabled_languages() as $lang) {2111 2112 // check required because it is not available inside quick edit2113 if (isset($_POST[“qts_{$lang}_slug”])) {2114 $meta_name = $this->get_meta_key($lang);2115 $meta_value = apply_filters( 'qts_validate_post_slug’, $_POST[“qts_{$lang}_slug”], $post, $lang);2116 delete_post_meta($post_id, $meta_name);2117 update_post_meta($post_id, $meta_name, $meta_value);2118 } 2119 }2120 }2121 2122 2123 2124 /**2125 * Display multiple input fields, one per language2126 * 2127 * @param $term the term object2128 *2129 * @since 1.02130 */2131 public function show_term_fields( $term ) {2132 global $q_config; //TODO: q_config : language_name2133 2134 // prints the fields in edit page2135 if (isset($_GET[‘action’]) && $_GET[‘action’] == ‘edit’ ):2136 2137 echo “<table class=\"form-table\">” . PHP_EOL;2138 echo “<input type=\"hidden\” name=\"qts_nonce\” id=\"qts_nonce\” value=\"” . wp_create_nonce( ‘qts_nonce’ ) . “\” />” . PHP_EOL;2139 2140 foreach( $this->enabled_languages as $lang ) {2141 2142 $slug = (is_object($term)) ? get_term_meta( $term->term_id, $this->get_meta_key($lang), true ) : '’;2143 2144 $value = ( $slug ) ? htmlspecialchars( $slug , ENT_QUOTES ) : '’;2145 2146 echo “<tr class=\"form-field form-required\">” . PHP_EOL;2147 echo “<th scope=\"row\” valig=\"top\"><label for=\"qts_{$lang}_slug\">".sprintf( __('Slug (%s)', ‘qts’), $q_config[‘language_name’][$lang] )."</label></th>” . PHP_EOL;2148 echo “<td><input type=\"text\” name=\"qts_{$lang}_slug\” value=\"". urldecode($value) . “\” /></td></tr>” . PHP_EOL;2149 2150 }2151 2152 echo '</table>’;2153 2154 // prints the fields in new page2155 else:2156 echo “<input type=\"hidden\” name=\"qts_nonce\” id=\"qts_nonce\” value=\"” . wp_create_nonce( ‘qts_nonce’ ) . “\” />” . PHP_EOL;2157 echo “<div id=\"qts_term_slugs\"><div class=\"qts_term_block\">” . PHP_EOL;2158 foreach( $this->enabled_languages as $lang ) {2159 2160 echo “<div class=\"form-field\">” . PHP_EOL;2161 2162 $slug = (is_object($term)) ? get_term_meta( $term->term_id, $this->get_meta_key($lang), true ) : '’;2163 2164 $value = ( $slug ) ? htmlspecialchars( $slug , ENT_QUOTES ) : '’;2165 2166 echo “<label for=\"qts_{$lang}_slug\">".sprintf( __('Slug (%s)', ‘qts’), $q_config[‘language_name’][$lang] )."</label>” . PHP_EOL;2167 echo “<input type=\"text\” name=\"qts_{$lang}_slug\” value=\"". urldecode($value) . “\” aria-required=\"true\">" . PHP_EOL;2168 echo '</div>’;2169 2170 2171 }2172 echo '</div></div>’;2173 endif;2174 }2175 2176 2177 2178 /**2179 * Sanitize title as slug, if empty slug2180 * 2181 * @param $term (object) the term object2182 * @param $slug (string) the slug name2183 * @param $lang (string) the language2184 * @return the slug validated2185 *2186 * @since 1.02187 */2188 public function validate_term_slug( $slug, $term, $lang ) {21892190 global $q_config; //TODO: q_config : term_name21912192 $term_key = call_user_func($this->get_plugin_prefix() . 'split’,$term->name);2193 // after split we will get array (with language code as a key )2194 2195 $term_key = $term_key[$this->default_language];2196 2197 $name_in_lang = $q_config[‘term_name’][$term_key][$lang];2198 2199 $ajax_name = ‘new’ . $term->taxonomy;2200 $post_name = isset($_POST[‘name’]) ? $_POST[‘name’] : ‘’;2201 $term_name = isset($_POST[$ajax_name]) ? trim($_POST[$ajax_name]) : $post_name;2202 2203 if (empty($term_name)) {2204 return $slug;2205 }2206 2207 $name = ( $name_in_lang == ‘’ || strlen($name_in_lang) == 0 ) ? $term_name : $name_in_lang;2208 $slug = trim($slug);2209 $slug = (empty($slug)) ? sanitize_title($name) : sanitize_title($slug);2210 return htmlspecialchars( $slug , ENT_QUOTES );2211 }2212 2213 2214 2215 /**2216 * Will make slug unique per language, if it isn’t already.2217 *2218 * @param string $slug The string that will be tried for a unique slug2219 * @param object $term The term object that the $slug will belong too2220 * @param object $lang The language reference 2221 * @return string Will return a true unique slug.2222 *2223 * @since 1.02224 */2225 public function unique_term_slug($slug, $term, $lang) {2226 2227 global $wpdb;2228 2229 $meta_key_name = $this->get_meta_key($lang);2230 $query = $wpdb->prepare("SELECT term_id FROM $wpdb->termmeta WHERE meta_key = ‘%s’ AND meta_value = ‘%s’ AND term_id != %d ", $meta_key_name, $slug, $term->term_id);2231 $exists_slug = $wpdb->get_results($query);22322233 if ( empty($exists_slug) ) {2234 return $slug;2235 }2236 2237 // If we didn’t get a unique slug, try appending a number to make it unique.2238 $query = $wpdb->prepare(“SELECT meta_value FROM $wpdb->termmeta WHERE meta_key = ‘%s’ AND meta_value = ‘%s’ AND term_id != %d", $meta_key_name, $slug, $term->term_id);22392240 if ( $wpdb->get_var( $query ) ) {2241 $num = 2;2242 do {2243 $alt_slug = $slug . "-$num";2244 $num++;2245 $slug_check = $wpdb->get_var(2246 $wpdb->prepare(2247 “SELECT meta_value FROM $wpdb->termmeta WHERE meta_key = ‘%s’ AND meta_value = '%s’",2248 $meta_key_name,2249 $alt_slug) );2250 } while ( $slug_check );2251 $slug = $alt_slug;2252 }22532254 return $slug;2255 }2256 2257 2258 2259 /**2260 * Display multiple input fields, one per language2261 * 2262 * @param $term_id the term id2263 * @param $tt_id the term taxonomy id2264 * @param $taxonomy the term object2265 *2266 * @since 1.02267 */2268 public function save_term( $term_id, $tt_id, $taxonomy ) {2269 2270 $cur_screen = get_current_screen();2271 if ( (defined(‘DOING_AUTOSAVE’) && DOING_AUTOSAVE ) // check autosave2272 || ( !current_user_can(‘edit_posts’) ) // check permission2273 || ( isset($cur_screen) && $cur_screen->id === “nav-menus”) 2274 ) { 2275 return $term_id;2276 }2277 2278 $term = get_term( $term_id, $taxonomy);2279 foreach( $this->get_enabled_languages() as $lang ) {2280 $meta_name = $this->get_meta_key($lang);2281 2282 //43LC: when at the post edit screen and creating a new tag2283 // the $slug comes from $_POST with the value of the post slug,2284 // not with the term slug. 2285 if ($_POST[‘action’] == “editpost”) {2286 // so we use the slug wp gave it2287 $term_slug = $term->slug;2288 } else {2289 // otherwise, its the edit term screen2290 $term_slug = $_POST[“qts_{$lang}_slug”];2291 }2292 2293 $meta_value = apply_filters( ‘qts_validate_term_slug’, $term_slug, $term, $lang);2294 2295 delete_term_meta($term_id, $meta_name);2296 update_term_meta($term_id, $meta_name, $meta_value);2297 }2298 }2299 2300 2301 2302 /**2303 * deletes termmeta rows associated with the term2304 * 2305 * @since 1.02306 */ 2307 public function delete_term($term_id, $tt_id, $taxonomy) {2308 global $wpdb;2309 2310 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->termmeta WHERE term_id = %d", $term_id ) );2311 }2312 2313 2314 2315 /**2316 * creates and prints the forms and hides the default fields 2317 * @param object $term the term object 2318 * @since 1.1.122319 * 2320 * TODO: change Slug column and View link2321 * TODO: move code into js file2322 *2323 */2324 public function qts_modify_term_form($term) {2325 echo "<script type=\"text/javascript\">\n// <![CDATA[\r\n";2326 /*2327 if( is_object($term) && isset($term->name) ) {2328 $termname = $term->name;2329 } else {2330 $termname = "";2331 }2332 if(isset($_GET[‘action’]) && $_GET[‘action’]==’edit’) {2333 foreach($this->get_enabled_languages() as $language) {2334 2335 //echo $this->qts_insert_term_input('name’, __(‘Name’,’qts’), $termname, $language,"edit”);2336 }2337 } else {2338 foreach($this->get_enabled_languages() as $language) {2339 //echo $this->qts_insert_term_input('tag-name’, __(‘Name’,’qts’), $termname, $language,"edit”); 2340 }2341 }2342 */2343 // hide real category text2344 //echo "if (ins != null) ins.style.display=’none’;\n";2345 echo "2346 var slugforms = jQuery(‘#qts_term_slugs’).html();2347 jQuery(‘#slug’).parent().html(slugforms)\n;2348 console.log(slugforms);2349 2350 ";2351 echo "// ]]>\n</script>\n";2352 }23532354 /**2355 * Helper for qts_modify_term_form_for2356 * @param string $id the term id2357 * @param object #term the term2358 * @param string $language the term name2359 * @param string $action the term name2360 * @return string $html the new input fields2361 * @since 1.1.122362 * TODO: use DocumentFragment2363 */2364 private function qts_insert_term_input($id,$name, $termname, $language,$action){2365 global $q_config; //TODO: q_config : language_name, term_name2366 $html = “";2367 if( $action === “new”) {2368 $html ="2369 var il = document.getElementsByTagName(‘input’),2370 d = document.createElement(‘div’),2371 l = document.createTextNode('".$name.” (“.$q_config[‘language_name’][$language].”)'),2372 ll = document.createElement(‘label’),2373 i = document.createElement(‘input’),2374 ins = null;2375 for(var j = 0; j < il.length; j++) {2376 if(il[j].id==’".$id."’) {2377 ins = il[j];2378 break;2379 }2380 }2381 i.type = 'text’;2382 i.id = i.name = ll.htmlFor =’qtrans_term_".$language."’;2383 “;2384 } elseif ( $action === “edit”) {2385 $html ="2386 var tr = document.createElement(‘tr’),2387 th = document.createElement(‘th’),2388 ll = document.createElement(‘label’),2389 l = document.createTextNode('".$name.” (“.$q_config[‘language_name’][$language].”)'),2390 td = document.createElement(‘td’),2391 i = document.createElement(‘input’),2392 ins = document.getElementById(‘".$id."’);2393 i.type = ‘text’;2394 i.id = i.name = ll.htmlFor =’qtrans_term_".$language."’;2395 ";2396 }2397 if(isset($q_config[‘term_name’][$termname][$language])) {2398 $html .="2399 i.value = ‘".addslashes(htmlspecialchars_decode($q_config[‘term_name’][$termname][$language], ENT_QUOTES))."’;";2400 //43LC: applied ENT_QUOTES to both edit and new forms. 2401 } else {2402 $html .="2403 if (ins != null)2404 i.value = ins.value;2405 ";2406 }2407 2408 if($language == $this->default_language) {2409 $html .="2410 i.onchange = function() { 2411 var il = document.getElementsByTagName(‘input’),2412 ins = null;2413 for(var j = 0; j < il.length; j++) {2414 if(il[j].id==’".$id."’) {2415 ins = il[j];2416 break;2417 }2418 }2419 if (ins != null)2420 ins.value = document.getElementById(‘qtrans_term_".$language."’).value;2421 };2422 “;2423 }2424 if( $action === “new”) {2425 $html .="2426 if (ins != null)2427 ins = ins.parentNode;2428 d.className = 'form-field form-required’;2429 ll.appendChild(l);2430 d.appendChild(ll);2431 d.appendChild(i);2432 if (ins != null)2433 ins.parentNode.insertBefore(d,ins);2434 “;2435 } elseif ( $action === “edit”) {2436 $html .="2437 ins = ins.parentNode.parentNode;2438 tr.className = 'form-field form-required’;2439 th.scope = 'row’;2440 th.vAlign = 'top’;2441 ll.appendChild(l);2442 th.appendChild(ll);2443 tr.appendChild(th);2444 td.appendChild(i);2445 tr.appendChild(td);2446 ins.parentNode.insertBefore(tr,ins);2447 “;2448 }2449 return $html; 2450 }2451 /**2452 * adds support for qtranslate in taxonomies2453 *2454 * @since 1.02455 */2456 public function taxonomies_hooks() {2457 2458 $taxonomies = $this->get_public_taxonomies();24592460 if ($taxonomies) {2461 foreach ($taxonomies as $taxonomy ) {2462 add_action( $taxonomy->name.’_add_form’, array(&$this, ‘qts_modify_term_form’));2463 add_action( $taxonomy->name.’_edit_form’, array(&$this, ‘qts_modify_term_form’));2464 add_action( $taxonomy->name.’_add_form_fields’, array(&$this, ‘show_term_fields’));2465 add_action( $taxonomy->name.’_edit_form_fields’, array(&$this, ‘show_term_fields’) );2466 add_filter('manage_edit-'.$taxonomy->name.’_columns’, array(&$this, ‘taxonomy_columns’));2467 add_filter('manage_’.$taxonomy->name.’_custom_column’, array(&$this, ‘taxonomy_custom_column’), 0, 3);2468 }2469 }2470 }2471 2472 2473 2474 /*2475 * Bug fix for slug column in taxonomies2476 * 2477 * @since 1.02478 */2479 public function taxonomy_columns($columns) {2480 unset($columns[‘slug’]);2481 unset($columns[‘posts’]);2482 2483 $columns[‘qts-slug’] = __('Slug’, ‘qts’);2484 $columns[‘posts’] = __('Posts’, ‘qts’);2485 2486 return $columns;2487 }2488 2489 2490 2491 /*2492 * Bug fix for slug column in taxonomies2493 * 2494 * @since 1.02495 */2496 public function taxonomy_custom_column($str, $column_name, $term_id) {2497 2498 switch ($column_name) {2499 case 'qts-slug’:2500 echo get_term_meta($term_id, $this->get_meta_key(), true);2501 break;2502 }2503 return false;2504 }2505 2506 2507 2508 2509 /**2510 * Bug fix for multisite blog names2511 * 2512 * @since 1.02513 */2514 public function blog_names($blogs) {2515 2516 foreach ($blogs as $blog) {2517 $blog->blogname = __($blog->blogname);2518 }2519 2520 return $blogs;2521 }2522 2523 2524 2525 /**2526 * Initialise the Language Widget selector2527 * 2528 * @since 1.02529 */2530 public function widget_init() {2531 2532 if (class_exists(‘qTranslateWidget’)) {2533 unregister_widget(‘qTranslateWidget’);2534 }2535 if (class_exists(‘mqTranslateWidget’)) {2536 unregister_widget(‘mqTranslateWidget’);2537 }2538 if (class_exists(‘ppqTranslateWidget’)) {2539 unregister_widget(‘ppqTranslateWidget’);2540 }2541 2542 //if (class_exists(‘qTranslateXWidget’)) {//it has additional features some people use.2543 // unregister_widget(‘qTranslateXWidget’);2544 //}2545 2546 register_widget(‘QtranslateSlugWidget’);2547 }2548 2549 2550 2551 /**2552 * remove some default dashboard Widgets on Desktop2553 *2554 * @since 1.02555 * @deprecated 2556 */2557 function remove_dashboard_widgets() {2558 global $wp_meta_boxes;2559 unset($wp_meta_boxes[‘dashboard’][‘side’][‘core’][‘dashboard_quick_press’]);2560 } 2561 2562 2563 2564 /**2565 * adds support for qtranslate nav menus2566 * 2567 * @since 1.02568 */2569 public function fix_nav_menu() {2570 global $pagenow;2571 2572 if( $pagenow != ‘nav-menus.php’ ) {2573 return;2574 }2575 //FIXME: fix the nav menu box2576 // wp_enqueue_script( 'nav-menu-query’, plugins_url( ‘assets/js/qts-nav-menu-min.js’ , dirname(__FILE__ ) ), 'nav-menu’, ‘1.0’ );2577 // add_meta_box( 'qt-languages’, __('Languages’, ‘qts’), array(&$this, ‘nav_menu_meta_box’), 'nav-menus’, 'side’, ‘default’ );2578 }2579 2580 2581 2582 /**2583 * draws meta box for select language2584 * 2585 * @since 1.02586 */2587 public function nav_menu_meta_box() {2588 global $q_config;2589 echo ‘<p>’;2590 foreach($q_config[‘enabled_languages’] as $id => $language) {2591 $checked = ($language == $q_config[‘language’]) ? ' checked="checked"’ : '’;2592 echo '<p style="margin:0 0 5px 0"><input type="radio” style="margin-right:5px” name="wa_qt_lang” value="’ . $language . ‘" id="wa_gt_lang_’ . $id . '" ' . $checked . '/>’;2593 echo ‘<label for="wa_gt_lang_’ . $id . '">’;2594 echo ‘<img src="’ . trailingslashit(WP_CONTENT_URL).$q_config[‘flag_location’].$q_config[‘flag’][$language] . '"/> ';2595 echo __($q_config[‘language_name’][$language], ‘qtranslate’);2596 echo '</label></p>’;2597 }2598 echo '<p style="margin:0 0 5px 0"><input type="radio" style="margin-right:5px" name="wa_qt_lang" value="all" id="wa_gt_lang_999" />’;2599 echo '<label for="wa_gt_lang_999">’;2600 echo __('All languages’, ‘qts’);2601 echo '</label></p>’;2602 echo '</p>’;2603 }2604 2605 2606 2607 /**2608 * Language select function for templating2609 *2610 * @param $type (string) choose the type of menu: 'text’, 'image’, 'both’, ‘dropdown’ 2611 * @param $args (array) some args for draw the menu: array( 'id’, 'class’, ‘short’ );2612 * 2613 * @since 1.02614 */2615 public function language_menu( $type = “text", $args = array() ) {2616 global $q_config;2617 26182619 // default arguments2620 $defaults = array(2621 ‘id’ => “qts-lang-menu",2622 ‘class’ => “qts-lang-menu",2623 ‘short’ => false2624 );2625 $args = wp_parse_args( $args, $defaults );2626 2627 $languages = call_user_func($this->get_plugin_prefix() . ‘getSortedLanguages’);2628 2629 // every type2630 switch ( $type ) {2631 2632 case 'image’:2633 case 'text’:2634 case 'both’:2635 2636 $baseurl = dirname(plugins_url());2637 $num_languages = count($languages);2638 echo “<ul id=\"{$args[‘id’]}\” class=\"qts_type_{$type} {$args[‘class’]}\">". PHP_EOL;2639 2640 foreach( $languages as $index => $lang ):2641 2642 $url = $this->get_current_url($lang);2643 // 43LC: hack to play nice with qtranslate-x2644 if( “qtranxf_” === $this->plugin_prefix && $this->default_language === $lang ) {26452646 $url = qtranxf_convertURL('’,$lang,false,true);2647 }2648 $item_class = array();2649 if ( (string)$q_config[‘language’] == (string)$lang ) $item_class[] = 'current-menu-item’;2650 if ( $index == ( $num_languages - 1) ) $item_class[] = 'last-child’;2651 2652 2653 $item_class = ' class="qts_lang_item ' . implode(' ', $item_class) . '"’;2654 2655 $language_name = ($args[‘short’]) ? $lang : $q_config[‘language_name’][$lang];2656 2657 if ( $type == ‘image’ ) {2658 $link_class = " class=\"qtrans_flag qtrans_flag_$lang\"";2659 $link_content = “<span style=\"display:none\">$language_name</span>";2660 } else if ( $type == ‘both’ ) {2661 $link_class = " class=\"qts_both qtrans_flag qtrans_flag_$lang\"";2662 $link_content = “$language_name"; 2663 } else {2664 $link_class = '’;2665 $link_content = $language_name;2666 $link_flag =’’;2667 }2668 if( $type == ‘image’ || $type == ‘both’ ) {2669 $link_flag_url = $baseurl . '/’.$q_config[‘flag_location’].$q_config[‘flag’][$lang];2670 2671 //TODO: add i18n for alt attribute2672 //43LC: hardcoding height and width2673 $link_flag = “<img widht=\"18\” height=\"12\” src=\"$link_flag_url\” alt=\"$language_name\” />";2674 }26752676 echo “<li $item_class><a href=\"$url\” lang=\"$lang\” hreflang=\"$lang\"$link_class>$link_flag$link_content</a></li>” . PHP_EOL;2677 2678 endforeach;2679 2680 echo “</ul>". PHP_EOL;2681 2682 break;2683 2684 case 'dropdown’:2685 2686 echo “<select id=\"{$args[‘id’]}\” class=\"{$args[‘class’]}\” onchange=\"window.location.href=this.options[this.selectedIndex].value\">". PHP_EOL;2687 2688 foreach( $languages as $index => $lang ):2689 2690 $url = $this->get_current_url($lang);2691 2692 $item_class = '’;2693 if ( (string)$q_config[‘language’] == (string)$lang ) $item_class = 'selected="selected"’;2694 2695 $language_name = ($args[‘short’]) ? $lang : $q_config[‘language_name’][$lang];2696 2697 echo “<option value=\"$url\” $item_class>$language_name</option>" . PHP_EOL;2698 2699 endforeach;2700 2701 echo "</select>". PHP_EOL;27022703 break;2704 }2705 2706 }2707}

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907