PluginProbe
Knowledge Base documentation & wiki plugin – BasePress Docs / trunk
Knowledge Base documentation & wiki plugin – BasePress Docs vtrunk
basepress / includes / class-basepress-cpt.php

class-basepress-cpt.php in Knowledge Base documentation & wiki plugin – BasePress Docs trunk, at includes/class-basepress-cpt.php

1,006 lines 46.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This is the class that adds and handles BasePress custom post type and taxonomies
5 */
6 // Exit if called directly
7 if ( !defined( 'ABSPATH' ) ) {
8 exit;
9 }
10 if ( !class_exists( 'Basepress_CPT' ) ) {
11 class Basepress_CPT {
12 private $options;
13
14 private $kb_slug;
15
16 private $bulk_edit_id_counter = 1;
17
18 /**
19 * basepress_cpt constructor.
20 *
21 * @since 1.0.0
22 */
23 public function __construct() {
24 global $basepress_utils;
25 add_filter( 'request', array($this, 'set_correct_request_for_item'), 5 );
26 //Add rewrite rules to handle links properly
27 add_filter( 'rewrite_rules_array', array($this, 'rewrite_rules') );
28 $this->add_rewrite_tags();
29 add_filter( 'knowledgebase_cat_rewrite_rules', array($this, 'clean_rewrite_rules') );
30 add_filter( 'knowledgebase_rewrite_rules', array($this, 'clean_rewrite_rules') );
31 //Add the product and section name on the post permalink
32 add_filter(
33 'post_type_link',
34 array($this, 'post_permalinks'),
35 1,
36 2
37 );
38 //Canonical redirections
39 add_filter( 'request', array($this, 'canonical_redirect'), 10 );
40 //Add the product name on the archive permalink
41 add_filter(
42 'term_link',
43 array($this, 'sections_permalink'),
44 99,
45 3
46 );
47 add_filter(
48 'term_link',
49 array($this, 'tag_permalink'),
50 99,
51 3
52 );
53 //Add Product filter dropdown on post list table
54 add_action( 'restrict_manage_posts', array($this, 'filter_list_dropdowns') );
55 //Filter Articles by products and sections
56 add_filter( 'parse_query', array($this, 'filter_list_query') );
57 //Add and manage Product and Section columns
58 add_filter( 'manage_knowledgebase_posts_columns', array($this, 'add_custom_columns') );
59 add_action(
60 'manage_knowledgebase_posts_custom_column',
61 array($this, 'manage_custom_columns'),
62 10,
63 2
64 );
65 //Add views, votes and score metafields default values on post save
66 add_action(
67 'save_post_knowledgebase',
68 array($this, 'save_articles_data'),
69 10,
70 2
71 );
72 // Add Bulk actions for Quick Edit
73 add_action(
74 'bulk_edit_custom_box',
75 array($this, 'quick_edit_custom_box_kb'),
76 10,
77 2
78 );
79 add_action(
80 'quick_edit_custom_box',
81 array($this, 'quick_edit_custom_box_kb'),
82 10,
83 2
84 );
85 //Add admin notice for articles with missing data
86 add_action( 'admin_notices', array($this, 'missing_data_notice') );
87 //Remove Feeds and oEmbed links in site header
88 add_action( 'template_redirect', array($this, 'remove_cpt_feeds') );
89 //Add Knowledge Base state to entry page in page list screen
90 add_filter(
91 'display_post_states',
92 array($this, 'set_display_post_states'),
93 10,
94 2
95 );
96 add_action( 'wp_ajax_basepress_get_sections_filter', array($this, 'basepress_get_sections_filter') );
97 $this->options = get_option( 'basepress_settings' );
98 $this->kb_slug = $basepress_utils->get_kb_slug();
99 $this->register_taxonomies();
100 $this->register_post_type();
101 }
102
103 /**
104 * Add rewrite tag for is_knowledgebase_product
105 *
106 * @since 2.2.0
107 */
108 public function add_rewrite_tags() {
109 add_rewrite_tag( '%is_knowledgebase_product%', '([^/]+)' );
110 add_rewrite_tag( '%knowledgebase_tax_term%', '([^/]+)' );
111 add_rewrite_tag( '%knowledgebase_items%', '([^/]+)' );
112 }
113
114 /**
115 * Removes default rewrite rules for our CPT and Taxonomy
116 *
117 * @since 1.9.0
118 *
119 * @param $rules
120 * @return array
121 */
122 public function clean_rewrite_rules() {
123 return array();
124 }
125
126 /**
127 * Adds rewrite rules for Basepress post type
128 * Called by flush_rewrite rules
129 *
130 * @since 1.0.0
131 * @updated 1.4.0
132 *
133 * @param $rules
134 * @return array
135 */
136 public function rewrite_rules( $rules ) {
137 global $wp_rewrite, $basepress_utils;
138 $options = get_option( 'basepress_settings' );
139 //If the entry page has not been set skip the rewrite rules
140 if ( !isset( $options['entry_page'] ) ) {
141 return $rules;
142 }
143 //Force Utils to refresh the options to make sure we have updated ones when creating our rewrite rules
144 //Essential for the Wizard
145 $basepress_utils->load_options();
146 $kb_slug = $basepress_utils->get_kb_slug( true );
147 $entry_page = ( isset( $this->options['entry_page'] ) ? $this->options['entry_page'] : '' );
148 /**
149 * Filter the kb_slug before generating the rewrite rules
150 * @since 1.5.0
151 */
152 $kb_slug = apply_filters( 'basepress_rewrite_rules_kb_slug', $kb_slug, $entry_page );
153 $search_base = $wp_rewrite->search_base;
154 $page_base = $wp_rewrite->pagination_base;
155 $comments_pagination_base = $wp_rewrite->comments_pagination_base;
156 $tag_base = ( isset( $options['tags_slug'] ) ? sanitize_key( $options['tags_slug'] ) : 'tag' );
157 $new_rules = array();
158 $product = get_terms( array(
159 'taxonomy' => 'knowledgebase_cat',
160 'parent' => 0,
161 'hide_empty' => false,
162 'meta_key' => 'basepress_position',
163 'orderby' => 'meta_value_num',
164 'order' => 'ASC',
165 'number' => 1,
166 ) );
167 $product_slug = '';
168 if ( !empty( $product ) && !is_a( $product, 'WP_Error' ) ) {
169 $product_slug = $product[0]->slug;
170 }
171 $uncategorized_slug = apply_filters( 'basepress_uncategorized_section', 'uncategorized' );
172 //General rules for uncategorized articles
173 $new_rules[$kb_slug . '/' . $uncategorized_slug . '/?$'] = 'index.php?is_knowledgebase_product=true&knowledgebase_cat=' . $uncategorized_slug;
174 $new_rules[$kb_slug . '/' . $uncategorized_slug . '/(.+)/([0-9]+)?/?$'] = 'index.php?post_type=knowledgebase&knowledgebase=$matches[1]&page=$matches[2]&knowledgebase_tax_term=' . $uncategorized_slug;
175 $new_rules[$kb_slug . '/' . $uncategorized_slug . '/(.+)/?$'] = 'index.php?post_type=knowledgebase&knowledgebase=$matches[1]&knowledgebase_tax_term=' . $uncategorized_slug;
176 if ( isset( $options['single_product_mode'] ) ) {
177 //entry page
178 $new_rules[$kb_slug . '/?$'] = 'index.php?is_knowledgebase_product=true&knowledgebase_cat=' . $product_slug;
179 //Search
180 $new_rules[$kb_slug . '/' . $search_base . '/(.+)/page/?([0-9]{1,})/?$'] = 'index.php?s=$matches[1]&post_type=knowledgebase&knowledgebase_cat=' . $product_slug . '&paged=$matches[2]';
181 $new_rules[$kb_slug . '/' . $search_base . '/(.+)/?$'] = 'index.php?s=$matches[1]&post_type=knowledgebase&knowledgebase_cat=' . $product_slug;
182 $new_rules[$kb_slug . '/' . $search_base . '/?$'] = 'index.php?s=&post_type=knowledgebase&knowledgebase_cat=' . $product_slug;
183 //Paged sections
184 $new_rules[$kb_slug . '/(.+?)/' . $page_base . '/([0-9]+)/?$'] = 'index.php?post_type=knowledgebase&knowledgebase_items=$matches[1]&paged=$matches[2]';
185 //Paged Comments
186 $new_rules[$kb_slug . '/(.+?)/' . $comments_pagination_base . '-([0-9]{1,})/?$'] = 'index.php?post_type=knowledgebase&knowledgebase_items=$matches[1]&cpage=$matches[2]';
187 //Paged articles
188 $new_rules[$kb_slug . "/(.+?)(?:/([0-9]+))/?\$"] = 'index.php?post_type=knowledgebase&knowledgebase_items=$matches[1]&page=$matches[2]';
189 //Catch all rule for sections and articles
190 $new_rules[$kb_slug . "/(.+?)/?\$"] = 'index.php?post_type=knowledgebase&knowledgebase_items=$matches[1]';
191 } else {
192 //entry page
193 $new_rules[$kb_slug . '/?$'] = 'index.php?page_id=' . $options['entry_page'];
194 //Search
195 //Empty searches
196 $new_rules[$kb_slug . '/(.+)/' . $search_base . '/?$'] = 'index.php?s=&post_type=knowledgebase&knowledgebase_cat=$matches[1]';
197 $new_rules[$kb_slug . '/' . $search_base . '/?$'] = 'index.php?s=&post_type=knowledgebase&knowledgebase_cat=$matches[1]';
198 //Global searches
199 $new_rules[$kb_slug . '/' . $search_base . '/(.+)/page/?([0-9]{1,})/?$'] = 'index.php?s=$matches[1]&post_type=knowledgebase&paged=$matches[2]';
200 $new_rules[$kb_slug . '/' . $search_base . '/(.+)/?$'] = 'index.php?s=$matches[1]&post_type=knowledgebase';
201 //All other cases
202 $new_rules[$kb_slug . '/(.+)/' . $search_base . '/(.+)/page/?([0-9]{1,})/?$'] = 'index.php?s=$matches[2]&post_type=knowledgebase&knowledgebase_cat=$matches[1]&paged=$matches[3]';
203 $new_rules[$kb_slug . '/(.+)/' . $search_base . '/(.+)/?$'] = 'index.php?s=$matches[2]&post_type=knowledgebase&knowledgebase_cat=$matches[1]';
204 //Paged sections
205 $new_rules[$kb_slug . '/(.+?)/' . $page_base . '/([0-9]+)/?$'] = 'index.php?post_type=knowledgebase&knowledgebase_items=$matches[1]&paged=$matches[2]';
206 //Paged Comments
207 $new_rules[$kb_slug . '/(.+?)/' . $comments_pagination_base . '-([0-9]{1,})/?$'] = 'index.php?post_type=knowledgebase&knowledgebase_items=$matches[1]&cpage=$matches[2]';
208 //Paged articles
209 $new_rules[$kb_slug . "/(.+?)(?:/([0-9]+))/?\$"] = 'index.php?post_type=knowledgebase&knowledgebase_items=$matches[1]&page=$matches[2]';
210 //Catch all rule for products, sections and articles
211 $new_rules[$kb_slug . "/(.+?)/?\$"] = 'index.php?post_type=knowledgebase&knowledgebase_items=$matches[1]';
212 }
213 $new_rules = apply_filters( 'basepress_rewrite_rules', $new_rules, array(
214 'entry_page' => $options['entry_page'],
215 'kb_slug' => $kb_slug,
216 'uncategorized_slug' => $uncategorized_slug,
217 'product_slug' => $product_slug,
218 'search_base' => $search_base,
219 'page_base' => $page_base,
220 'comments_pagination_base' => $comments_pagination_base,
221 ) );
222 //IMPORTANT: the new rules must be added at the top of the array to have higher priority
223 return array_merge( $new_rules, $rules );
224 }
225
226 /**
227 * Registers the taxonomy 'knowledgebase_cat'
228 *
229 * @since version 1.0.0
230 */
231 public function register_taxonomies() {
232 //We need to disable the REST API for the articles edit screen otherwise the category metabox would display
233 //Setting the 'meta_box_cb' to false is not enough for Gutenberg
234 $not_rest_api_pages = array('post-new.php', 'post.php');
235 $show_in_rest = ( isset( $GLOBALS['pagenow'] ) && in_array( $GLOBALS['pagenow'], $not_rest_api_pages ) ? false : true );
236 register_taxonomy( 'knowledgebase_cat', 'knowledgebase', array(
237 'labels' => array(
238 'name' => esc_html__( 'Knowledge Base categories', 'basepress' ),
239 'singular_name' => esc_html__( 'Knowledge Base category', 'basepress' ),
240 'menu_name' => esc_html__( 'Knowledge Base categories', 'basepress' ),
241 ),
242 'hierarchical' => true,
243 'query_var' => true,
244 'public' => true,
245 'show_ui' => false,
246 'show_admin_column' => true,
247 'show_in_nav_menus' => true,
248 'show_tagcloud' => false,
249 'show_in_rest' => $show_in_rest,
250 'meta_box_cb' => false,
251 'rewrite' => array(
252 'slug' => $this->kb_slug . '/%terms%',
253 'with_front' => false,
254 'feeds' => false,
255 ),
256 ) );
257 }
258
259 /**
260 * Registers Basepress post type
261 *
262 * @since version 1.0.0
263 */
264 public function register_post_type() {
265 global $basepress_utils;
266 $options = $basepress_utils->get_options();
267 $exclude_from_search = ( isset( $options['exclude_from_wp_search'] ) ? true : false );
268 //TODO: this has been disabled for testing as it may create confusion
269 $show_ui = true;
270 //get_option( 'basepress_run_wizard' ) ? false : true;
271 register_post_type( 'knowledgebase', array(
272 'label' => esc_html__( 'Knowledge Base', 'basepress' ),
273 'labels' => array(
274 'name' => esc_html__( 'Knowledge Base', 'basepress' ),
275 'singular_name' => esc_html__( 'Knowledge Base Article', 'basepress' ),
276 'all_items' => esc_html__( 'All Articles', 'basepress' ),
277 'edit_item' => esc_html__( 'Edit Article', 'basepress' ),
278 'view_item' => esc_html__( 'View Article', 'basepress' ),
279 'search_items' => esc_html__( 'Search Articles', 'basepress' ),
280 'add_new' => esc_html__( 'Add New Article', 'basepress' ),
281 ),
282 'description' => esc_html__( 'These are the Knowledge base articles from BasePress.', 'basepress' ),
283 'supports' => array(
284 'title',
285 'editor',
286 'author',
287 'thumbnail',
288 'excerpt',
289 'trackbacks',
290 'revisions',
291 'comments'
292 ),
293 'taxonomies' => array('knowledgebase_cat'),
294 'hierarchical' => false,
295 'query_var' => true,
296 'public' => true,
297 'show_ui' => $show_ui,
298 'show_in_menu' => true,
299 'show_in_nav_menus' => true,
300 'show_in_admin_bar' => true,
301 'menu_position' => 25,
302 'menu_icon' => 'data:image/svg+xml;base64,' . base64_encode( '<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="rgb(135,135,135)" d="M 13.65 1 L 17.756 1 L 17.756 1 L 17.756 6.008 L 15.784 3.089 L 13.65 6.008 L 13.65 1 L 13.65 1 L 13.65 1 L 13.65 1 L 13.65 1 L 13.65 1 L 13.65 1 L 13.65 1 L 13.65 1 Z M 2.768 22.951 C 1.463 22.951 1.05 22.221 1.05 20.911 L 1.05 3.089 C 1.05 1.578 1.428 1.049 2.768 1.049 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 L 2.768 22.951 Z M 5 9 L 17.756 9 L 17.756 11 L 5 11 L 5 9 L 5 9 L 5 9 L 5 9 L 5 9 L 5 9 L 5 9 L 5 9 Z M 5 12 L 12 12 L 12 14 L 5 14 L 5 12 L 5 12 L 5 12 L 5 12 L 5 12 L 5 12 L 5 12 Z M 13.65 0 L 2.415 0 L 2.415 0 L 2.415 0 L 2.415 0 L 2.415 0 C 1.082 0 0 1.031 0 2.3 L 0 21.7 C 0 22.969 1.082 24 2.415 24 L 18.585 24 C 19.918 24 21 22.969 21 21.7 L 21 2.3 C 21 1.031 19.918 0 18.585 0 L 17.756 0 L 13.65 0 L 13.65 0 L 13.65 0 L 13.65 0 L 13.65 0 L 13.65 0 L 13.65 0 L 13.65 0 L 13.65 0 L 13.65 0 Z"/></svg>' ),
303 'can_export' => true,
304 'has_archive' => false,
305 'exclude_from_search' => $exclude_from_search,
306 'publicly_queryable' => true,
307 'capability' => 'edit_post',
308 'show_in_rest' => true,
309 'rewrite' => array(
310 'slug' => $this->kb_slug . '/%taxonomies%',
311 'with_front' => false,
312 'feeds' => false,
313 ),
314 ) );
315 }
316
317 /**
318 * Takes the request generated from our rewrite rules and determines if the request is for an article or section
319 * The reuqest is then updated accordingly
320 *
321 * @since 2.14.0
322 *
323 * @param $request
324 * @return mixed
325 */
326 public function set_correct_request_for_item( $request ) {
327 global $wp, $wpdb, $basepress_utils;
328 $request_items = ( isset( $request['knowledgebase_items'] ) && !empty( $request['knowledgebase_items'] ) ? explode( '/', wp_unslash( $request['knowledgebase_items'] ) ) : false );
329 if ( isset( $request['post_type'] ) && 'knowledgebase' == $request['post_type'] && $request_items ) {
330 $request_item = array_pop( $request_items );
331 $parent_item = array_pop( $request_items );
332 //Check if a post exists with the passed slug
333 $post_exists = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_name = %s AND post_type = 'knowledgebase' LIMIT 1", $request_item ) );
334 //Check if a section exists with the passed slug
335 $maybe_section = get_term_by( 'slug', $request_item, 'knowledgebase_cat' );
336 $section_exists = !empty( $maybe_section ) && is_a( $maybe_section, 'WP_Term' );
337 if ( $section_exists ) {
338 if ( 0 == $maybe_section->parent ) {
339 $request['is_knowledgebase_product'] = true;
340 $request['knowledgebase_cat'] = $request_item;
341 return $request;
342 }
343 }
344 if ( $post_exists && !$section_exists || !$post_exists && !$section_exists ) {
345 $request['knowledgebase'] = $request_item;
346 $request['name'] = $request_item;
347 $request['knowledgebase_tax_term'] = $parent_item;
348 } elseif ( $post_exists && $section_exists ) {
349 $kb_slug = $basepress_utils->get_kb_slug( true );
350 $kb_slug = apply_filters( 'basepress_rewrite_rules_kb_slug', $kb_slug, $this->options['entry_page'] );
351 $post_permalink = get_the_permalink( $post_exists );
352 $section_permalink = get_term_link( $maybe_section, 'knowledgebase_cat' );
353 $_request_items = str_replace( $kb_slug . '/', '', $wp->request );
354 //First check if the section slug matches
355 if ( false !== strpos( $section_permalink, $_request_items ) ) {
356 $request['knowledgebase_cat'] = $request_item;
357 } elseif ( false !== strpos( $post_permalink, $_request_items ) ) {
358 $request['knowledgebase'] = $request_item;
359 $request['name'] = $request_item;
360 $request['knowledgebase_tax_term'] = $parent_item;
361 } else {
362 $request['knowledgebase_cat'] = $request_item;
363 }
364 } elseif ( !$post_exists && $section_exists ) {
365 $request['knowledgebase_cat'] = $request_item;
366 }
367 }
368 return $request;
369 }
370
371 /**
372 * Canonical redirection for articles and uncategorized section
373 *
374 * @since 2.7.0
375 *
376 * @param $request
377 * @return mixed
378 */
379 public function canonical_redirect( $request ) {
380 global $basepress_utils;
381 if ( is_admin() || isset( $request['rest_route'] ) ) {
382 return $request;
383 }
384 if ( !apply_filters( 'basepress_canonical_redirect', true, $request ) ) {
385 return $request;
386 }
387 //Entry page
388 if ( isset( $request['is_knowledgebase_product'] ) ) {
389 return $request;
390 }
391 //Tags
392 if ( isset( $request['knowledgebase_tag'] ) ) {
393 return $request;
394 }
395 if ( !isset( $request['knowledgebase_items'] ) || empty( $request['knowledgebase_items'] ) ) {
396 return $request;
397 }
398 $uncategorized_slug = apply_filters( 'basepress_uncategorized_section', 'uncategorized' );
399 $wrong_product = false;
400 $wrong_section = false;
401 //Prevent access to the uncategorized section
402 if ( isset( $request['is_knowledgebase_product'] ) && isset( $request['knowledgebase_tax_term'] ) && $uncategorized_slug == $request['knowledgebase_tax_term'] ) {
403 $options = $basepress_utils->get_options();
404 $entry_page = ( isset( $options['entry_page'] ) ? $options['entry_page'] : '' );
405 /**
406 * This filter allows to modify the entry page ID
407 */
408 $entry_page = apply_filters( 'basepress_entry_page', $entry_page );
409 $entry_page_url = get_permalink( $entry_page );
410 wp_redirect( $entry_page_url, 301, 'BasePress [1]' );
411 die;
412 }
413 //Redirect for wrong section URL
414 if ( isset( $request['knowledgebase_cat'] ) && !empty( $request['knowledgebase_cat'] ) ) {
415 $section = get_term_by( 'slug', $request['knowledgebase_cat'], 'knowledgebase_cat' );
416 $taxonomy_tree = $this->get_taxonomies( $section, 'section' );
417 $taxonomy_tree = ( !empty( $taxonomy_tree ) ? explode( '/', $taxonomy_tree ) : array() );
418 $request_tree = explode( '/', $request['knowledgebase_items'] );
419 //Remove last item which is the requested section slug
420 array_pop( $request_tree );
421 $wrong_section = $taxonomy_tree !== $request_tree;
422 if ( $wrong_section ) {
423 add_action( 'template_redirect', function () use($request, $section) {
424 global $wp_rewrite;
425 $term_link = get_term_link( $section, 'knowledgebase_cat' );
426 if ( isset( $request['paged'] ) && !empty( $request['paged'] ) ) {
427 $page_base = $wp_rewrite->pagination_base;
428 $term_link = untrailingslashit( $term_link ) . '/' . $page_base . '/' . $request['paged'] . '/';
429 }
430 wp_redirect( $term_link, 301, 'BasePress [2]' );
431 die;
432 }, 999 );
433 }
434 }
435 //Redirection for wrong article URL
436 if ( isset( $request['knowledgebase'] ) && isset( $request['post_type'] ) && 'knowledgebase' == $request['post_type'] && isset( $request['knowledgebase_tax_term'] ) ) {
437 $_post = get_posts( array(
438 'post_type' => 'knowledgebase',
439 'name' => $request['knowledgebase'],
440 'nopaging' => true,
441 'fields' => 'ids',
442 ) );
443 if ( empty( $_post ) ) {
444 return $request;
445 }
446 $post_section = get_the_terms( $_post[0], 'knowledgebase_cat' );
447 //Redirects articles if the URL contains the uncategorized section but the article has a section
448 if ( $uncategorized_slug == $request['knowledgebase_tax_term'] ) {
449 $wrong_section = !empty( $post_section );
450 } else {
451 //Redirect all other cases where the section or the product are incorrect
452 $term = get_term_by( 'slug', $request['knowledgebase_tax_term'], 'knowledgebase_cat' );
453 if ( is_a( $term, 'WP_Term' ) ) {
454 $taxonomy_tree = $this->get_taxonomies( $post_section[0], 'article' );
455 $taxonomy_tree = explode( '/', $taxonomy_tree );
456 $request_tree = explode( '/', $request['knowledgebase_items'] );
457 //Remove last item which is the post slug
458 array_pop( $request_tree );
459 //Wrong Section?
460 $wrong_section = $taxonomy_tree !== $request_tree;
461 //Wrong Product?
462 $terms = $this->get_taxonomies( $post_section[0], 'article' );
463 $terms = ( !empty( $terms ) ? explode( '/', $terms ) : array() );
464 $wrong_product = false == stripos( $_SERVER['REQUEST_URI'], $terms[0] );
465 // phpcs:ignore
466 } else {
467 $wrong_section = true;
468 }
469 }
470 if ( $wrong_section || $wrong_product ) {
471 $_post = $_post[0];
472 add_action( 'template_redirect', function () use($_post, $request) {
473 $post_permalink = get_permalink( $_post );
474 if ( isset( $request['page'] ) && !empty( $request['page'] ) ) {
475 $post_permalink = untrailingslashit( $post_permalink ) . '/' . $request['page'] . '/';
476 }
477 wp_redirect( $post_permalink, 301, 'BasePress [3]' );
478 die;
479 }, 9 );
480 }
481 }
482 return $request;
483 }
484
485 /**
486 * Gets the permalink structure from the settings and sets the default when necessary
487 *
488 * @since 2.14.0
489 *
490 * @param $source
491 * @return string|string[]
492 */
493 public function get_permalink_structure( $source ) {
494 global $basepress_utils;
495 $permalink_structure = $basepress_utils->get_option( "{$source}_permalink_structure" );
496 switch ( $source ) {
497 case 'article':
498 $default_permalink_structure = '%knowledge_base%/%article_section%';
499 break;
500 case 'section':
501 $default_permalink_structure = '%knowledge_base%';
502 break;
503 default:
504 $default_permalink_structure = '%knowledge_base%';
505 }
506 if ( '%none%' == $permalink_structure ) {
507 $permalink_structure = '';
508 } else {
509 $permalink_structure = ( !empty( $permalink_structure ) ? $permalink_structure : $default_permalink_structure );
510 }
511 if ( (bool) $basepress_utils->get_option( 'single_product_mode' ) ) {
512 // The knowledgebase tag might be followed by a slash or not so we need to consider both options
513 $permalink_structure = str_replace( array('%knowledge_base%/', '%knowledge_base%'), '', $permalink_structure );
514 }
515 return $permalink_structure;
516 }
517
518 /**
519 * Adds the product and section name on the post permalink
520 *
521 * @since version 1.0.0
522 *
523 * @param $link
524 * @param $post
525 * @return mixed
526 */
527 public function post_permalinks( $link, $post ) {
528 //Return if this is not a basepress post
529 if ( 'knowledgebase' != $post->post_type ) {
530 return $link;
531 }
532 $terms = get_the_terms( $post->ID, 'knowledgebase_cat' );
533 if ( !empty( $terms ) and !is_wp_error( $terms ) ) {
534 //replace '%taxonomies%' with the appropriate terms hierarchy
535 $taxonomies = $this->get_taxonomies( $terms[0], 'article' );
536 $taxonomies = ( !empty( $taxonomies ) ? '/' . $taxonomies : '' );
537 $link = str_replace( '/%taxonomies%', $taxonomies, $link );
538 } else {
539 $uncategorized_section = apply_filters( 'basepress_uncategorized_section', 'uncategorized' );
540 $link = str_replace( '%taxonomies%', $uncategorized_section, $link );
541 }
542 /**
543 * Filters the section permalink before returning it
544 */
545 $link = apply_filters( 'basepress_post_permalink', $link, $post );
546 return $link;
547 }
548
549 /**
550 * Adds the product name on the archive permalink
551 *
552 * @since version 1.0.0
553 *
554 * @param $termlink
555 * @param $term
556 * @param $taxonomy
557 * @return mixed
558 */
559 public function sections_permalink( $termlink, $term, $taxonomy ) {
560 //If this term is not a basepress product return the $termlink unchanged
561 if ( 'knowledgebase_cat' != $taxonomy ) {
562 return $termlink;
563 }
564 if ( 0 == $term->parent ) {
565 $termlink = str_replace( '/%terms%', '', $termlink );
566 return $termlink;
567 }
568 $permastructure = $this->get_permalink_structure( 'section' );
569 if ( empty( $permastructure ) ) {
570 $termlink = str_replace( '/%terms%', '', $termlink );
571 } else {
572 $taxonomies = $this->get_taxonomies( $term, 'section' );
573 $taxonomies = ( !empty( $taxonomies ) ? '/' . $taxonomies : '' );
574 $termlink = str_replace( '/%terms%', $taxonomies, $termlink );
575 }
576 /**
577 * Filters the section permalink before returning it
578 */
579 $termlink = apply_filters( 'basepress_sections_permalink', $termlink, $term );
580 return $termlink;
581 }
582
583 /**
584 *
585 * @since version 1.0.0
586 *
587 * @param $terms
588 * @return string
589 */
590 public function get_taxonomies( $term, $source ) {
591 global $basepress_utils;
592 $hierarchy = $basepress_utils->get_sections_tree( $term );
593 $terms = wp_list_pluck( $hierarchy, 'slug' );
594 $kb = array_shift( $terms );
595 $section = array_pop( $terms );
596 $parent_sections = implode( '/', $terms );
597 $taxonomies = str_replace( array('%knowledge_base%', '%parent_sections%', '%article_section%'), array($kb, $parent_sections, $section), $this->get_permalink_structure( $source ) );
598 $taxonomies = ltrim( $taxonomies, '/' );
599 $taxonomies = rtrim( $taxonomies, '/' );
600 $taxonomies = preg_replace( '/\\/+/', '/', $taxonomies );
601 return $taxonomies;
602 }
603
604 public function tag_permalink( $termlink, $term, $taxonomy ) {
605 global $basepress_utils;
606 if ( 'knowledgebase_tag' != $taxonomy ) {
607 return $termlink;
608 }
609 if ( !get_option( 'permalink_structure' ) ) {
610 $product = $basepress_utils->get_product();
611 $termlink = add_query_arg( 'knowledgebase_cat', $product->slug, $termlink );
612 } else {
613 if ( $basepress_utils->is_single_product_mode ) {
614 $termlink = str_replace( '%kb_product%/', '', $termlink );
615 } else {
616 $product = $basepress_utils->get_product();
617 if ( !empty( $product->slug ) ) {
618 $termlink = str_replace( '%kb_product%', $product->slug, $termlink );
619 } else {
620 $termlink = str_replace( '%kb_product%/', '', $termlink );
621 }
622 }
623 }
624 return $termlink;
625 }
626
627 /**
628 * Adds articles filtering on post list table
629 *
630 * @since 1.0.0
631 *
632 * @updated 2.1.0
633 */
634 public function filter_list_dropdowns() {
635 global $typenow;
636 if ( 'knowledgebase' == $typenow ) {
637 $selected_product = ( isset( $_GET['kb_product'] ) ? sanitize_text_field( wp_unslash( $_GET['kb_product'] ) ) : '' );
638 wp_dropdown_categories( array(
639 'show_option_all' => esc_html__( 'Show all Knowledge Bases', 'basepress' ),
640 'taxonomy' => 'knowledgebase_cat',
641 'name' => 'kb_product',
642 'orderby' => 'name',
643 'selected' => $selected_product,
644 'show_count' => true,
645 'hide_empty' => false,
646 'hierarchical' => true,
647 'depth' => 1,
648 ) );
649 $this->get_sections_dropdown_filter( $selected_product );
650 $this->echo_filter_script();
651 }
652 }
653
654 /**
655 * Get sections dropdown list. This is used during Ajax as well
656 *
657 * @since 2.1.0
658 *
659 * @param $selected_product
660 */
661 private function get_sections_dropdown_filter( $selected_product ) {
662 $selected_product = ( $selected_product ? $selected_product : -1 );
663 $selected_section = ( isset( $_GET['kb_section'] ) ? sanitize_text_field( wp_unslash( $_GET['kb_section'] ) ) : '' );
664 $list = wp_dropdown_categories( array(
665 'taxonomy' => 'knowledgebase_cat',
666 'name' => 'kb_section',
667 'child_of' => $selected_product,
668 'orderby' => 'name',
669 'selected' => $selected_section,
670 'show_count' => true,
671 'pad_counts' => false,
672 'hide_empty' => false,
673 'hierarchical' => true,
674 'depth' => 10,
675 'echo' => 0,
676 ) );
677 $option_all_text = esc_html__( 'Show all sections', 'basepress' );
678 $selected = ( !$selected_section ? ' selected' : '' );
679 $list = preg_replace( '/(<select.*>)/', "\$0\n\t<option value='0'{$selected}>{$option_all_text}</option>", $list );
680 echo $list;
681 }
682
683 /**
684 * Get sections dropdown list during Ajax
685 *
686 * @since 2.1.0
687 */
688 public function basepress_get_sections_filter() {
689 $selected_product = ( isset( $_REQUEST['selected_product'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['selected_product'] ) ) : '' );
690 ob_start();
691 $this->get_sections_dropdown_filter( $selected_product );
692 echo ob_get_clean();
693 wp_die();
694 }
695
696 /**
697 * Echoes the JS for the articles filtering
698 *
699 * @since 2.1.0
700 */
701 private function echo_filter_script() {
702 ?>
703 <script type="text/javascript">
704 jQuery( '#kb_product' ).change( function(){
705 jQuery( '#kb_section option:first').attr('selected','selected');
706
707 var product = jQuery( this ).val();
708
709 jQuery.ajax({
710 type: 'POST',
711 url: ajaxurl,
712 data: {
713 action: 'basepress_get_sections_filter',
714 selected_product: product
715 },
716 success: function( response ){
717 jQuery( '#kb_section' ).replaceWith( response );
718 }
719 });
720 });
721 </script>
722 <?php
723 }
724
725 /**
726 * Filters articles by products on post list table
727 *
728 * @since version 1.0.0
729 *
730 * @updated 2.1.0
731 *
732 * @param $query
733 * @return mixed
734 */
735 public function filter_list_query( $query ) {
736 global $pagenow;
737 $post_type = 'knowledgebase';
738 $taxonomy = 'knowledgebase_cat';
739 $q_vars =& $query->query_vars;
740 if ( 'edit.php' == $pagenow && isset( $q_vars['post_type'] ) && $q_vars['post_type'] == $post_type && (isset( $_REQUEST['kb_section'] ) || isset( $_REQUEST['kb_product'] )) ) {
741 $product = ( isset( $_REQUEST['kb_product'] ) && 0 != (int) $_REQUEST['kb_product'] ? (int) $_REQUEST['kb_product'] : 0 );
742 $section = ( isset( $_REQUEST['kb_section'] ) && 0 != (int) $_REQUEST['kb_section'] ? (int) $_REQUEST['kb_section'] : 0 );
743 if ( $product || $section ) {
744 $term_id = ( $section ? $section : $product );
745 $include_children = ( $section ? false : true );
746 $q_vars['tax_query'] = array(array(
747 'taxonomy' => $taxonomy,
748 'field' => 'term_id',
749 'terms' => $term_id,
750 'include_children' => $include_children,
751 ));
752 }
753 }
754 return $query;
755 }
756
757 /**
758 * Adds the Product and Section columns
759 *
760 * @since version 1.0.0
761 *
762 * @param $columns
763 * @return array
764 */
765 public function add_custom_columns( $columns ) {
766 unset($columns['taxonomy-knowledgebase_cat']);
767 $first_columns = array_slice(
768 $columns,
769 0,
770 3,
771 true
772 );
773 $last_columns = array_slice(
774 $columns,
775 3,
776 null,
777 true
778 );
779 $new_columns = array();
780 $new_columns['basepress-product'] = esc_html__( 'Knowledge Base', 'basepress' );
781 $new_columns['basepress-section'] = esc_html__( 'Section', 'basepress' );
782 $columns = array_merge( $first_columns, $new_columns, $last_columns );
783 return $columns;
784 }
785
786 /**
787 * Generates the values for the Product and Section columns
788 *
789 * @since version 1.0.0
790 *
791 * @updated 2.1.0
792 *
793 * @param $column
794 * @param $post_id
795 */
796 public function manage_custom_columns( $column, $post_id ) {
797 switch ( $column ) {
798 case 'basepress-product':
799 $term = get_the_terms( $post_id, 'knowledgebase_cat' );
800 if ( $term ) {
801 $product = $this->get_product( $term[0] );
802 $link = get_admin_url() . 'edit.php?post_type=knowledgebase&kb_product=' . $product->term_id;
803 $link .= '&kb_section=0';
804 echo '<a href="' . esc_url( $link ) . '">' . esc_html( $product->name ) . '</a>';
805 }
806 break;
807 case 'basepress-section':
808 $term = wp_get_post_terms( $post_id, 'knowledgebase_cat', array() );
809 if ( empty( $term ) ) {
810 break;
811 }
812 //Skip terms with parent 0 as they are products
813 if ( 0 == $term[0]->parent ) {
814 break;
815 }
816 $product = $this->get_product( $term[0] );
817 $link = get_admin_url() . 'edit.php?post_type=knowledgebase&kb_product=' . $product->term_id;
818 $link .= '&kb_section=' . $term[0]->term_id;
819 echo '<a href="' . esc_url( $link ) . '">' . esc_html( $term[0]->name ) . '</a>';
820 break;
821 }
822 }
823
824 /**
825 * Finds the product from the section
826 *
827 * @since version 1.0.0
828 *
829 * @param $term
830 * @return array|null|WP_Error|WP_Term
831 */
832 private function get_product( $term ) {
833 while ( 0 != $term->parent ) {
834 $term = get_term( $term->parent, 'knowledgebase_cat' );
835 }
836 return $term;
837 }
838
839 /**
840 * Saves knowledge base post metas and checks the assigned section
841 *
842 * @since version 1.0.0
843 * @updated 2.14.11
844 *
845 * @param $post_id
846 * @param $post
847 */
848 public function save_articles_data( $post_id, $post ) {
849 //Make sure the article is assigned to a single section. This is necessary for the quick/bulk edits
850 if ( isset( $_REQUEST['tax_input'] ) && isset( $_REQUEST['tax_input']['knowledgebase_cat'] ) ) {
851 if ( in_array( 'none', $_REQUEST['tax_input']['knowledgebase_cat'] ) ) {
852 wp_delete_object_term_relationships( $post_id, 'knowledgebase_cat' );
853 } else {
854 if ( count( $_REQUEST['tax_input']['knowledgebase_cat'] ) > 1 ) {
855 wp_delete_object_term_relationships( $post_id, 'knowledgebase_cat' );
856 wp_set_post_terms( $post_id, end( $_REQUEST['tax_input']['knowledgebase_cat'] ), 'knowledgebase_cat' );
857 }
858 }
859 }
860 //Save all metadata
861 if ( 'publish' == $post->post_status ) {
862 $views = get_post_meta( $post_id, 'basepress_views', true );
863 if ( !$views ) {
864 update_post_meta( $post_id, 'basepress_views', 0 );
865 }
866 }
867 }
868
869 /**
870 * Outputs the custom box for articles quick edit
871 *
872 * @since 2.14.11
873 *
874 * @param $column_name
875 * @param $post_type
876 */
877 public function quick_edit_custom_box_kb( $column_name, $post_type ) {
878 if ( 'knowledgebase' !== $post_type ) {
879 return;
880 }
881 if ( !in_array( $column_name, array('basepress-section') ) ) {
882 return;
883 }
884 ?>
885 <fieldset class="inline-edit-col-right inline-edit-knowledgebase">
886 <div class="inline-edit-col inline-edit-<?php
887 echo esc_attr( $column_name );
888 ?>">
889 <label class="inline-edit-group">
890 <span class="title"><?php
891 esc_html_e( 'Section', 'basepress' );
892 ?></span>
893 <?php
894 echo $this->get_bulk_edit_sections_list();
895 //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
896 ?>
897 </label>
898 </div>
899 </fieldset>
900 <?php
901 }
902
903 /**
904 * Get list of sections for quick edit
905 *
906 * @since 2.14.11
907 *
908 * @return string|string[]|null
909 */
910 public function get_bulk_edit_sections_list() {
911 $list = wp_dropdown_categories( array(
912 'taxonomy' => 'knowledgebase_cat',
913 'child_of' => '',
914 'hide_empty' => 0,
915 'echo' => 0,
916 'hierarchical' => 1,
917 'class' => 'basepress_section_mb',
918 'name' => 'tax_input[knowledgebase_cat][]',
919 'id' => 'quick_edit_kb_cat_' . $this->bulk_edit_id_counter++,
920 'selected' => '',
921 ) );
922 $extra_options = "\n\t<option disabled selected>" . esc_html__( '&mdash; No Change &mdash;' ) . '</option><option disabled>─────────</option>';
923 $extra_options .= '<option value="none">' . esc_html__( 'None', 'basepress' ) . '</option>';
924 $list = preg_replace( '/(<select.*>)/', "\$0{$extra_options}", $list );
925 $list = str_replace( 'level-0"', 'level-0" disabled ', $list );
926 return $list;
927 }
928
929 /**
930 * Add admin notice if the articles has missing data like section and template
931 *
932 * @since 1.7.6
933 *
934 * @return mixed
935 */
936 public function missing_data_notice() {
937 global $post, $basepress_utils;
938 $action = ( isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : '' );
939 if ( 'edit' == $action && $post && 'knowledgebase' == $post->post_type && 'auto-draft' != $post->post_status ) {
940 $post_type = $post->post_type;
941 if ( 'edit' == $action && 'knowledgebase' == $post_type ) {
942 $options = $basepress_utils->get_options();
943 $missing_options = array();
944 $post_terms = get_the_terms( $post->ID, 'knowledgebase_cat' );
945 $post_terms = ( !empty( $post_terms ) ? $post_terms[0] : false );
946 $post_meta = get_post_meta( $post->ID, 'basepress_template_name', true );
947 /* if( empty( $post_terms ) && ! apply_filters( 'basepress_remove_missing_section_notice', false ) ){
948 $missing_options[] = esc_html__( 'Section', 'basepress' );
949 } */
950 if ( empty( $post_meta ) && !apply_filters( 'basepress_remove_missing_template_notice', false ) ) {
951 if ( !isset( $options['force_sidebar_position'] ) ) {
952 $missing_options[] = esc_html__( 'Template', 'basepress' );
953 }
954 }
955 if ( !empty( $missing_options ) ) {
956 $class = 'notice notice-error is-dismissible';
957 $message = esc_html__( 'This article was saved without the following data:', 'basepress' ) . ' ';
958 $missing_options = implode( ', ', $missing_options );
959 printf(
960 '<div class="%1$s"><p>%2$s%3$s</p></div>',
961 esc_attr( $class ),
962 esc_html( $message ),
963 esc_html( $missing_options )
964 );
965 }
966 }
967 }
968 }
969
970 /**
971 * Removes Feeds and oEmbed links in site header
972 *
973 * @since 1.8.9
974 */
975 public function remove_cpt_feeds() {
976 global $wp_query;
977 if ( 'knowledgebase' == get_post_type() || isset( $wp_query->query_vars['post_type'] ) && 'knowledgebase' == $wp_query->query_vars['post_type'] || is_tax( 'knowledgebase_cat' ) ) {
978 remove_action( 'wp_head', 'feed_links_extra', 3 );
979 remove_action( 'wp_head', 'feed_links', 2 );
980 remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
981 }
982 }
983
984 /**
985 * Add Knowledge Base state to entry page in page list screen
986 *
987 * @since 2.1.0
988 *
989 * @param $post_states
990 * @param $post
991 * @return mixed
992 */
993 public function set_display_post_states( $post_states, $post ) {
994 global $basepress_utils;
995 $options = $basepress_utils->get_options();
996 if ( isset( $options['entry_page'] ) && $options['entry_page'] == $post->ID ) {
997 $post_states['basepress_entry_page'] = esc_html__( 'Knowledge Base Page', 'basepress' );
998 }
999 return $post_states;
1000 }
1001
1002 }
1003
1004 //End Class
1005 new Basepress_CPT();
1006 }