404page – your smart custom 404 error page - Version 11.4.0

Version Description

new block, new shortcode, new function

Download this release

Release Info

Developer petersplugins
Plugin Icon 128x128 404page – your smart custom 404 error page
Version 11.4.0
Comparing to
See all releases

Code changes from version 11.3.1 to 11.4.0

Files changed (10) hide show
  1. 404page.php +1 -1
  2. assets/js/block.js +84 -0
  3. block.json +24 -0
  4. block.php +35 -0
  5. functions.php +117 -78
  6. inc/class-404page-block-editor.php +14 -1
  7. inc/class-404page.php +1253 -1192
  8. loader.php +15 -1
  9. readme.txt +34 -3
  10. shortcodes.php +19 -0
404page.php CHANGED
@@ -9,7 +9,7 @@
9
  * Plugin Name: 404page - your smart custom 404 error page
10
  * Plugin URI: https://petersplugins.com/404page/
11
  * Description: Custom 404 the easy way! Set any page as custom 404 error page. No coding needed. Works with (almost) every Theme.
12
- * Version: 11.3.1
13
  * Author: Peter Raschendorfer
14
  * Author URI: https://petersplugins.com
15
  * Text Domain: 404page
9
  * Plugin Name: 404page - your smart custom 404 error page
10
  * Plugin URI: https://petersplugins.com/404page/
11
  * Description: Custom 404 the easy way! Set any page as custom 404 error page. No coding needed. Works with (almost) every Theme.
12
+ * Version: 11.4.0
13
  * Author: Peter Raschendorfer
14
  * Author URI: https://petersplugins.com
15
  * Text Domain: 404page
assets/js/block.js ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ wp.blocks.registerBlockType( 'petersplugins/the-url', {
2
+
3
+ title: wp.i18n.__( 'URL causing 404 error', '404page' ),
4
+ icon: 'editor-unlink',
5
+ category: 'widgets',
6
+ attributes: {
7
+ urltype: {
8
+ type: 'string',
9
+ default: 'full'
10
+ },
11
+ alignment: {
12
+ type: 'string',
13
+ default: 'none'
14
+ }
15
+ },
16
+ supports: {
17
+ color: true
18
+ },
19
+
20
+ edit( props ) {
21
+
22
+ const attributes = props.attributes;
23
+ const setAttributes = props.setAttributes;
24
+ const blockProps = wp.blockEditor.useBlockProps();
25
+
26
+ const alignmentClass = (attributes.alignment != null) ? 'has-text-align-' + attributes.alignment : '';
27
+
28
+ function changeType( newUrltype ) {
29
+ setAttributes( { urltype: newUrltype } );
30
+ }
31
+
32
+ function changeAlignment( newAlignment ) {
33
+ setAttributes( {
34
+ alignment: newAlignment === undefined ? 'none' : newAlignment,
35
+ } );
36
+ };
37
+
38
+ return wp.element.createElement('div', { className: alignmentClass }, [
39
+
40
+ wp.element.createElement( 'p', blockProps, wp.i18n.__( 'The URL that is causing the 404 error is displayed here', '404page' ) ),
41
+
42
+ wp.element.createElement( wp.blockEditor.InspectorControls, {}, [
43
+
44
+ wp.element.createElement( wp.components.PanelBody, {
45
+
46
+ title: wp.i18n.__( 'Display', '404page' ),
47
+ initialOpen: true
48
+
49
+ }, [
50
+
51
+ wp.element.createElement( wp.components.SelectControl, {
52
+ value: attributes.urltype,
53
+ label: wp.i18n.__( 'Type', '404page' ),
54
+ onChange: changeType,
55
+ options: [
56
+ {value: 'page', label: wp.i18n.__( 'Page', '404page') },
57
+ {value: 'domainpath', label: wp.i18n.__( 'Domain Path', '404page' ) },
58
+ {value: 'full', label: wp.i18n.__( 'Full', '404page' ) },
59
+ ]
60
+ } )
61
+
62
+ ] )
63
+
64
+ ] ),
65
+
66
+ wp.element.createElement( wp.blockEditor.BlockControls, {}, [
67
+
68
+ wp.element.createElement( wp.blockEditor.AlignmentToolbar, {
69
+ value: attributes.alignment,
70
+ onChange: changeAlignment
71
+ } )
72
+
73
+ ] )
74
+ ] )
75
+ },
76
+
77
+ save() {
78
+
79
+ const blockProps = wp.blockEditor.useBlockProps.save();
80
+
81
+ return null;
82
+ }
83
+
84
+ });
block.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://schemas.wp.org/trunk/block.json",
3
+ "apiVersion": 2,
4
+ "name": "petersplugins/the-url",
5
+ "title": "URL causing 404 error",
6
+ "category": "widgets",
7
+ "icon": "editor-unlink",
8
+ "version": "11.4.0",
9
+ "textdomain": "404page",
10
+ "attributes": {
11
+ "urltype": {
12
+ "type": "string",
13
+ "default": "full"
14
+ },
15
+ "alignment": {
16
+ "type": "string",
17
+ "default": "none"
18
+ }
19
+ },
20
+ "supports": {
21
+ "color": true
22
+ },
23
+ "editorScript": "404page-block"
24
+ }
block.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The 404page Plugin Block
5
+ *
6
+ * @since 11.4.0
7
+ *
8
+ **/
9
+
10
+ // If this file is called directly, abort
11
+ if ( ! defined( 'WPINC' ) ) {
12
+ die;
13
+ }
14
+
15
+ add_action( 'init', function() {
16
+
17
+ if ( function_exists('register_block_type_from_metadata' ) ) {
18
+
19
+ wp_enqueue_script(
20
+ '404page-block',
21
+ pp_404page()->get_asset_url( 'js', 'block.js' ),
22
+ [ 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-components', 'wp-editor' ],
23
+ pp_404page()->get_plugin_version()
24
+ );
25
+
26
+ register_block_type_from_metadata( __DIR__, [
27
+ 'render_callback' => function( $atts ) {
28
+ $alignmentClass = ( $atts['alignment'] != null ) ? 'has-text-align-' . $atts['alignment'] : '';
29
+ return '<p ' . get_block_wrapper_attributes( [ 'class' => $alignmentClass ] ) . '>' . pp_404_get_the_url( $atts[ 'urltype' ] ) . '</p>';
30
+ }
31
+ ] );
32
+
33
+ }
34
+
35
+ } );
functions.php CHANGED
@@ -1,78 +1,117 @@
1
- <?php
2
-
3
- /**
4
- * The 404page Plugin Functions
5
- *
6
- * @since 11.0.0
7
- *
8
- **/
9
-
10
- // If this file is called directly, abort
11
- if ( ! defined( 'WPINC' ) ) {
12
- die;
13
- }
14
-
15
-
16
- // this function can be used by a theme to check if there's an active custom 404 page
17
- function pp_404_is_active() {
18
-
19
- return pp_404page()->pp_404_is_active();
20
-
21
- }
22
-
23
-
24
- // this function can be used by a theme to activate native support
25
- function pp_404_set_native_support() {
26
-
27
- pp_404page()->pp_404_set_native_support();
28
-
29
- }
30
-
31
-
32
- // this function can be used by a theme to get the title of the custom 404 page in native support
33
- function pp_404_get_the_title() {
34
-
35
- return pp_404page()->pp_404_get_the_title();
36
-
37
- }
38
-
39
-
40
- // this function can be used by a theme to print out the title of the custom 404 page in native support
41
- function pp_404_the_title() {
42
-
43
- pp_404page()->pp_404_the_title();
44
-
45
- }
46
-
47
-
48
- // this function can be used by a theme to get the content of the custom 404 page in native support
49
- function pp_404_get_the_content() {
50
-
51
- return pp_404page()->pp_404_get_the_content();
52
-
53
- }
54
-
55
-
56
- // this function can be used by a theme to print out the content of the custom 404 page in native support
57
- function pp_404_the_content() {
58
-
59
- return pp_404page()->pp_404_the_content();
60
-
61
- }
62
-
63
-
64
- // this function returns the page id of the selected 404 page - in a multilingual environment this returs the id of the page in the current language
65
- function pp_404_get_page_id() {
66
-
67
- return pp_404page()->get_page_id();
68
-
69
- }
70
-
71
-
72
- // this function returns an array of page ids in all languages
73
- function pp_404_get_all_page_ids() {
74
-
75
- return pp_404page()->get_all_page_ids();
76
-
77
- }
78
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The 404page Plugin Functions
5
+ *
6
+ * @since 11.0.0
7
+ *
8
+ **/
9
+
10
+ // If this file is called directly, abort
11
+ if ( ! defined( 'WPINC' ) ) {
12
+ die;
13
+ }
14
+
15
+
16
+ // this function can be used by a theme to check if there's an active custom 404 page
17
+ function pp_404_is_active() {
18
+
19
+ return pp_404page()->pp_404_is_active();
20
+
21
+ }
22
+
23
+
24
+ // this function can be used by a theme to activate native support
25
+ function pp_404_set_native_support() {
26
+
27
+ pp_404page()->pp_404_set_native_support();
28
+
29
+ }
30
+
31
+
32
+ // this function can be used by a theme to get the title of the custom 404 page in native support
33
+ function pp_404_get_the_title() {
34
+
35
+ return pp_404page()->pp_404_get_the_title();
36
+
37
+ }
38
+
39
+
40
+ // this function can be used by a theme to print out the title of the custom 404 page in native support
41
+ function pp_404_the_title() {
42
+
43
+ pp_404page()->pp_404_the_title();
44
+
45
+ }
46
+
47
+
48
+ // this function can be used by a theme to get the content of the custom 404 page in native support
49
+ function pp_404_get_the_content() {
50
+
51
+ return pp_404page()->pp_404_get_the_content();
52
+
53
+ }
54
+
55
+
56
+ // this function can be used by a theme to print out the content of the custom 404 page in native support
57
+ function pp_404_the_content() {
58
+
59
+ return pp_404page()->pp_404_the_content();
60
+
61
+ }
62
+
63
+
64
+ // this function returns the page id of the selected 404 page - in a multilingual environment this returs the id of the page in the current language
65
+ function pp_404_get_page_id() {
66
+
67
+ return pp_404page()->get_page_id();
68
+
69
+ }
70
+
71
+
72
+ // this function returns an array of page ids in all languages
73
+ function pp_404_get_all_page_ids() {
74
+
75
+ return pp_404page()->get_all_page_ids();
76
+
77
+ }
78
+
79
+ /**
80
+ * return URL that caused the 404 error for frontend
81
+ * used by shortcode and block
82
+ *
83
+ * @since 11.4.0
84
+ * @access public
85
+ * @param string $type
86
+ * @return string
87
+ */
88
+ function pp_404_get_the_url( $type ) {
89
+
90
+ $url = '';
91
+
92
+ if ( is_array( $type ) ) {
93
+
94
+ $type = $type[0];
95
+
96
+ }
97
+
98
+ if ( 'page' == $type ) {
99
+
100
+ $url = trim( pp_404page()->get_404_url( 'path' ), '/' );
101
+
102
+ } elseif ( 'domainpath' == $type ) {
103
+
104
+ $url = pp_404page()->get_404_url( 'host' ) . pp_404page()->get_404_url( 'path' );
105
+
106
+ } else {
107
+
108
+ $url = pp_404page()->get_404_url( 'full' );
109
+
110
+ }
111
+
112
+
113
+ return $url;
114
+
115
+ }
116
+
117
+ ?>
inc/class-404page-block-editor.php CHANGED
@@ -4,6 +4,7 @@
4
  * The 404page block editor plugin class
5
  *
6
  * @since 9
 
7
  */
8
 
9
  if ( ! defined( 'WPINC' ) ) {
@@ -44,7 +45,19 @@ if ( !class_exists( 'PP_404Page_BlockEditor' ) ) {
44
  <style type="text/css">
45
  .edit-post-layout__content:before { content: "<?php esc_html_e( 'You are currently editing your custom 404 error page', '404page'); ?>"; background-color: #333; color: #FFF; padding: 8px; font-size: 16px; display: block };
46
  </style>
47
- <?php
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  }
50
 
4
  * The 404page block editor plugin class
5
  *
6
  * @since 9
7
+ * partially rewritten in 11.4.0, because it stopped working
8
  */
9
 
10
  if ( ! defined( 'WPINC' ) ) {
45
  <style type="text/css">
46
  .edit-post-layout__content:before { content: "<?php esc_html_e( 'You are currently editing your custom 404 error page', '404page'); ?>"; background-color: #333; color: #FFF; padding: 8px; font-size: 16px; display: block };
47
  </style>
48
+ <?php /* since 11.4.0 */ ?>
49
+ <script type="text/javascript">
50
+ jQuery( document ).ready( function( $ ) {
51
+ var checkExist = setInterval( function() {
52
+ if ( $( '.edit-post-header-toolbar' ).length ) {
53
+ $( '.edit-post-header-toolbar' ).prepend( '<div style="margin-<?php echo ( is_rtl() ? 'right' : 'left' ); ?>: 24px; height: 32px; line-height: 32px; padding: 0 12px; background-color: #000; color: #fff">404</div>' );
54
+ clearInterval(checkExist);
55
+ }
56
+ }, 100);
57
+
58
+ } );
59
+ </script>
60
+ <?php
61
 
62
  }
63
 
inc/class-404page.php CHANGED
@@ -1,1193 +1,1254 @@
1
- <?php
2
-
3
- /**
4
- * The 404page core plugin class
5
- */
6
-
7
-
8
- // If this file is called directly, abort
9
- if ( ! defined( 'WPINC' ) ) {
10
- die;
11
- }
12
-
13
-
14
- // indicate that 404page plugin is active
15
- if ( ! defined( 'PP_404' ) ) {
16
- define( 'PP_404', true );
17
- }
18
-
19
-
20
- /**
21
- * The core plugin class
22
- */
23
- if ( !class_exists( 'PP_404Page' ) ) {
24
-
25
-
26
- class PP_404Page extends PPF08_Plugin {
27
-
28
-
29
- /**
30
- * Native Mode
31
- *
32
- * @since 11.0.0 - was part of previous settings class
33
- * @var bool
34
- * @access private
35
- */
36
- private $native;
37
-
38
- private $template;
39
- private $postid;
40
-
41
-
42
- /**
43
- * Admin Class
44
- *
45
- * @see class-404page-admin.php
46
- * @since 10
47
- * @var object
48
- * @access private
49
- */
50
- private $admin;
51
-
52
-
53
- /**
54
- * Block Editor Class
55
- *
56
- * @see class-404page-block-editor.php
57
- * @since 9
58
- * @var object
59
- * @access private
60
- */
61
- private $blockeditor;
62
-
63
-
64
- /**
65
- * Classic Editor Class
66
- *
67
- * @see class-404page-classic-editor.php
68
- * @since 9
69
- * @var object
70
- * @access private
71
- */
72
- private $classiceditor;
73
-
74
-
75
- /**
76
- * Deprecated Class
77
- *
78
- * @see class-404page-deprecated.php
79
- * @since 11.0.0
80
- * @var object
81
- * @access private
82
- */
83
- private $deprecated;
84
-
85
-
86
- /**
87
- * Init the Class
88
- *
89
- * @since 11.0.0
90
- * was part of __construct before
91
- */
92
- public function plugin_init() {
93
-
94
- // settings defaults
95
- // @since 11.0.0
96
- $defaults = array(
97
- 'page_id' => 0,
98
- 'hide' => false,
99
- 'fire_error' => true,
100
- 'force_error' => false,
101
- 'no_url_guessing' => false,
102
- 'http410_if_trashed' => false,
103
- 'http410_always' => false,
104
- 'method' => 'STD'
105
- );
106
-
107
- // since 11.0.0 we use add_settings_class() to load the settings
108
- $this->add_settings_class( 'PP_404Page_Settings', 'class-404page-settings', $this, $defaults );
109
-
110
- // @since 11.0.0
111
- $this->add_action( 'init' );
112
- }
113
-
114
-
115
- /**
116
- * do plugin init
117
- * this runs after init action has fired to ensure everything is loaded properly
118
- * was init() before 11.0.0
119
- */
120
- function action_init() {
121
-
122
- // moved from add_text_domain() in v 11.0.0
123
- load_plugin_textdomain( '404page' );
124
-
125
-
126
- // change old stuff to new stuff for backward compatibility
127
- // since v 11.0.0
128
- $this->deprecated = $this->add_sub_class_always( 'PP_404Page_Deprecated', 'class-404page-deprecated', $this );
129
-
130
- // load the following subclasses only on backend
131
- // using add_sub_class_backend() sinde v 11
132
- $this->admin = $this->add_sub_class_backend( 'PP_404Page_Admin', 'class-404page-admin', $this, $this->settings() );
133
- $this->blockeditor = $this->add_sub_class_backend( 'PP_404Page_BlockEditor', 'class-404page-block-editor', $this, $this->settings() );
134
- $this->classiceditor = $this->add_sub_class_backend( 'PP_404Page_ClassicEditor', 'class-404page-classic-editor', $this, $this->settings() );
135
-
136
- // as of v 2.2 always call set_mode
137
- // as of v 2.4 we do not need to add an init action hook
138
-
139
- if ( !is_admin() && $this->settings()->get( 'page_id' ) > 0 ) {
140
-
141
- // as of v 3.0 we once check if there's a 404 page set and not in all functions separately
142
- $this->set_mode();
143
- add_action( 'pre_get_posts', array ( $this, 'exclude_404page' ) );
144
- add_filter( 'get_pages', array ( $this, 'remove_404page_from_array' ), 10, 2 );
145
-
146
- // Stop URL guessing if activated
147
- if ( $this->settings()->get( 'no_url_guessing' ) ) {
148
- add_filter( 'redirect_canonical' ,array ( $this, 'no_url_guessing' ) );
149
- }
150
-
151
-
152
- // Remove 404 error page from XML sitemaps
153
- // only if "Send an 404 error if the page is accessed directly by its URL" is active
154
- if ( $this->settings()->get( 'fire_error' ) ) {
155
-
156
- // YOAST sitemap
157
- // @since 6
158
-
159
- // we do not need to check if Yoast Sitemap feature is activated because the filter only fires if it is active
160
- add_filter( 'wpseo_exclude_from_sitemap_by_post_ids', function ( $alreadyExcluded ) {
161
- // as of 11.0.5 we add the page ID to the array of already excluded pages
162
- // plus we exclude the 404 page in all languages
163
- return array_merge( $alreadyExcluded, $this->get_all_page_ids() );
164
- }, 999 );
165
-
166
-
167
- // Jetpack sitemap
168
- // @since 11.1.2
169
-
170
- // we do not need to check if Jetpack Sitemap feature is activated because the filter only fires if it is active
171
- add_filter( 'jetpack_sitemap_skip_post', function ( $skip, $post ) {
172
- if ( in_array( intval( $post->ID ), $this->get_all_page_ids() ) ) {
173
- $skip = true;
174
- }
175
- return $skip;
176
- }, 999, 2 );
177
-
178
- }
179
-
180
- }
181
-
182
-
183
- if ( class_exists( 'PP_404Page_Admin' ) ) {
184
-
185
- // load classes only if in admin
186
- // @since 10
187
- // using class_exists( 'PP_404Page_Admin' ) instead of is_admin() as of v 10.3 for compatibilty with iThemes Sync
188
-
189
- //$this->admin = new PP_404Page_Admin( $this, $this->settings );
190
- //$this->blockeditor = new PP_404Page_BlockEditor( $this );
191
- //$this->classiceditor = new PP_404Page_ClassicEditor( $this );
192
-
193
- // Remove 404 page from post list if activated
194
- // not moved to PP_404Page_Admin because we also need exclude_404page() in frontend
195
- if ( $this->settings()->get( 'hide' ) and $this->settings()->get( 'page_id' ) > 0 ) {
196
- add_action( 'pre_get_posts' ,array ( $this, 'exclude_404page' ) );
197
- }
198
-
199
- }
200
-
201
- }
202
-
203
-
204
- /**
205
- * init filters
206
- */
207
- function set_mode() {
208
-
209
- $this->settings()->set_method();
210
-
211
- if ( defined( 'CUSTOMIZR_VER' ) ) {
212
-
213
- // Customizr Compatibility Mode
214
-
215
- // @since 3.1
216
- add_filter( 'body_class', array( $this, 'add_404_body_class_customizr_mode' ) );
217
-
218
- add_filter( 'tc_404_header_content', array( $this, 'show404title_customizr_mode' ), 999 );
219
- add_filter( 'tc_404_content', array( $this, 'show404_customizr_mode' ), 999 );
220
- add_filter( 'tc_404_selectors', array( $this, 'show404articleselectors_customizr_mode' ), 999 );
221
-
222
- // send http 410 instead of http 404 if requested resource is in trash
223
- // @since 3.2
224
- // or always send http 410
225
- // @since 11.3.0
226
- if ( $this->settings()->get( 'http410_if_trashed' ) || $this->settings()->get( 'http410_always' ) ) {
227
-
228
- add_action( 'template_redirect', array( $this, 'maybe_send_410' ) );
229
-
230
- }
231
-
232
- } elseif ( $this->settings()->get( 'method' ) != 'STD' ) {
233
-
234
- // Compatibility Mode
235
- // as of v 2.4 we use the the_posts filter instead of posts_results, because the posts array is internally processed after posts_results fires
236
- add_filter( 'the_posts', array( $this, 'show404_compatiblity_mode' ), 999 );
237
-
238
- // as of v 2.5 we remove the filter if the DW Question & Answer plugin by DesignWall (https://www.designwall.com/wordpress/plugins/dw-question-answer/) is active and we're in the answers list
239
- add_filter( 'dwqa_prepare_answers', array( $this, 'remove_show404_compatiblity_mode' ), 999 );
240
-
241
- } else {
242
-
243
- // Standard Mode
244
- add_filter( '404_template', array( $this, 'show404_standard_mode' ), 999 );
245
-
246
- if ( $this->settings()->get( 'fire_error' ) ) {
247
-
248
- add_action( 'template_redirect', array( $this, 'do_404_header_standard_mode' ) );
249
-
250
- }
251
-
252
- // send http 410 instead of http 404 if requested resource is in trash
253
- // @since 3.2
254
- // or always send http 410
255
- // @since 11.3.0
256
- if ( $this->settings()->get( 'http410_if_trashed' ) || $this->settings()->get( 'http410_always' ) ) {
257
-
258
- add_action( 'template_redirect', array( $this, 'maybe_send_410' ) );
259
-
260
- }
261
-
262
- }
263
-
264
- }
265
-
266
-
267
- /**
268
- * show 404 page
269
- * Standard Mode
270
- */
271
- function show404_standard_mode( $template ) {
272
-
273
- global $wp_query;
274
-
275
- // @since 4
276
- // fix for an ugly bbPress problem
277
- // see https://wordpress.org/support/topic/not-fully-bbpress-compatible/
278
- // see https://bbpress.trac.wordpress.org/ticket/3161
279
- // if a bbPress member page is shown and the member has no topics created yet the 404_template filter hook fires
280
- // this is a bbPress problem but it has not been fixed since 6 months
281
- // so let's bypass the problem
282
- if ( function_exists( 'bbp_is_single_user' ) ) {
283
-
284
- if ( bbp_is_single_user() ) {
285
-
286
- return $template;
287
-
288
- }
289
-
290
- }
291
- // that's it
292
-
293
- if ( ! $this->is_native() ) {
294
-
295
- $this->disable_caching();
296
-
297
- $wp_query = null;
298
- $wp_query = new WP_Query();
299
- $wp_query->query( 'page_id=' . $this->get_page_id() );
300
-
301
-
302
- $wp_query->the_post();
303
- $template = get_page_template();
304
- rewind_posts();
305
- add_filter( 'body_class', array( $this, 'add_404_body_class' ) );
306
- }
307
- $this->maybe_force_404();
308
- $this->do_404page_action();
309
- return $template;
310
-
311
- }
312
-
313
-
314
- /**
315
- * show 404 page
316
- * Compatibility Mode
317
- */
318
- function show404_compatiblity_mode( $posts ) {
319
-
320
- global $wp_query;
321
-
322
- // remove the filter so we handle only the first query - no custom queries
323
- remove_filter( 'the_posts', array( $this, 'show404_compatiblity_mode' ), 999 );
324
-
325
- // @since 4
326
- // fix for an ugly bbPress problem
327
- // see show404_standard_mode()
328
- if ( function_exists( 'bbp_is_single_user' ) ) {
329
-
330
- if ( bbp_is_single_user() ) {
331
-
332
- return $posts;
333
-
334
- }
335
-
336
- }
337
- // that's it
338
-
339
- $pageid = $this->get_page_id();
340
- if ( ! $this->is_native() ) {
341
-
342
- // as of v 10 we also check if $wp_query->query[error] == 404
343
- // this is necessary to bypass a WordPress bug
344
- // if permalink setting is something like e.g. /blog/%postname%/ the $posts is not empty
345
- // bug reported https://core.trac.wordpress.org/ticket/46000
346
-
347
- // as of v 11.0.3 we also check for REST_REQUEST to not create a 404 page in case of REST API call
348
- // as of v 11.2.4 we also check for DOING_CRON
349
- if ( ( empty( $posts ) || ( isset( $wp_query->query['error'] ) && $wp_query->query['error'] == 404 ) ) && is_main_query() && !is_robots() && !is_home() && !is_feed() && !is_search() && !is_archive() && ( !defined('DOING_AJAX') || !DOING_AJAX ) && ( !defined('DOING_CRON') || !DOING_CRON ) && ( !defined('REST_REQUEST') || !REST_REQUEST ) ) {
350
-
351
- // as of v2.1 we do not alter the posts argument here because this does not work with SiteOrigin's Page Builder Plugin, template_include filter introduced
352
- $this->postid = $pageid;
353
-
354
- // as of v 2.4 we use the the_posts filter instead of posts_results
355
- // therefore we have to reset $wp_query
356
- // resetting $wp_query also forces us to remove the pre_get_posts action plus the get_pages filter
357
-
358
- remove_action( 'pre_get_posts', array ( $this, 'exclude_404page' ) );
359
- remove_filter( 'get_pages', array ( $this, 'remove_404page_from_array' ), 10, 2 );
360
-
361
- $this->disable_caching();
362
-
363
- $wp_query = null;
364
- $wp_query = new WP_Query();
365
-
366
- // @since 8
367
- // added suppress_filters for compatibilty with current WPML version
368
- $wp_query->query( array( 'page_id' => $pageid, 'suppress_filters' => true ) );
369
-
370
-
371
- $wp_query->the_post();
372
- $this->template = get_page_template();
373
- $posts = $wp_query->posts;
374
- $wp_query->rewind_posts();
375
-
376
- add_action( 'wp', array( $this, 'do_404_header' ) );
377
- add_filter( 'body_class', array( $this, 'add_404_body_class' ) );
378
- add_filter( 'template_include', array( $this, 'change_404_template' ), 999 );
379
-
380
-
381
-
382
- $this->maybe_force_404();
383
- $this->do_404page_action();
384
-
385
- } elseif ( 1 == count( $posts ) && 'page' == $posts[0]->post_type ) {
386
-
387
- // Do a 404 if the 404 page is opened directly
388
- if ( $this->settings()->get( 'fire_error' ) ) {
389
- $curpageid = $posts[0]->ID;
390
-
391
- if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
392
-
393
- // WPML is active - get the post ID of the default language
394
- global $sitepress;
395
- $curpageid = apply_filters( 'wpml_object_id', $curpageid, 'page', $sitepress->get_default_language() );
396
- $pageid = apply_filters( 'wpml_object_id', $pageid, 'page', $sitepress->get_default_language() );
397
-
398
- } elseif ( function_exists( 'pll_get_post' ) ) {
399
-
400
- // was defined( 'POLYLANG_VERSION' ) before version 11.2.3
401
-
402
- // Polylang is active - get the post ID of the default language
403
- $curpageid = pll_get_post( $curpageid, pll_default_language() );
404
- $pageid = pll_get_post( $pageid, pll_default_language() );
405
-
406
- }
407
-
408
- if ( $pageid == $curpageid ) {
409
- add_action( 'wp', array( $this, 'do_404_header' ) );
410
- add_filter( 'body_class', array( $this, 'add_404_body_class' ) );
411
- $this->maybe_force_404();
412
- $this->do_404page_action();
413
- }
414
- }
415
-
416
- }
417
- } else {
418
- $this->maybe_force_404();
419
- $this->do_404page_action();
420
- }
421
- return $posts;
422
- }
423
-
424
- /**
425
- * disable caching for known caching plugins
426
- *
427
- * @since 11.2.0
428
- */
429
- function disable_caching() {
430
-
431
- // WP Super Cache
432
- if ( defined( 'WPCACHEHOME' ) ) {
433
-
434
- global $cache_enabled;
435
-
436
- // is caching active?
437
- if ( $cache_enabled ) {
438
-
439
- define( 'DONOTCACHEPAGE', true );
440
-
441
- }
442
-
443
- }
444
-
445
-
446
- // W3 Total Cache
447
- if ( defined( 'W3TC' ) ) {
448
-
449
- if ( class_exists( 'W3TC\Dispatcher' ) ) {
450
-
451
- // is caching active?
452
- if ( W3TC\Dispatcher::config()->get_boolean( 'pgcache.enabled' ) ) {
453
-
454
- define( 'DONOTCACHEPAGE', true );
455
-
456
- }
457
-
458
- }
459
-
460
- }
461
-
462
- }
463
-
464
-
465
- /**
466
- * for DW Question & Answer plugin
467
- * this function is called by the dwqa_prepare_answers filter
468
- */
469
- function remove_show404_compatiblity_mode( $args ) {
470
- remove_filter( 'the_posts', array( $this, 'show404_compatiblity_mode' ), 999 );
471
- return $args;
472
- }
473
-
474
-
475
- /**
476
- * this function overrides the page template in compatibilty mode
477
- */
478
- function change_404_template( $template ) {
479
-
480
- // we have to check if the template file is there because if the theme was changed maybe a wrong template is stored in the database
481
- $new_template = locate_template( array( $this->template ) );
482
- if ( '' != $new_template ) {
483
- return $new_template ;
484
- }
485
- return $template;
486
- }
487
-
488
-
489
- /**
490
- * send 404 HTTP header
491
- * Standard Mode
492
- */
493
- function do_404_header_standard_mode() {
494
- if ( is_page() && get_the_ID() == $this->settings()->get( 'page_id' ) && !is_404() ) {
495
- status_header( 404 );
496
- nocache_headers();
497
- $this->maybe_force_404();
498
- $this->do_404page_action();
499
- }
500
- }
501
-
502
-
503
- /**
504
- * send 404 HTTP header
505
- * Compatibility Mode
506
- */
507
- function do_404_header() {
508
- // remove the action so we handle only the first query - no custom queries
509
- remove_action( 'wp', array( $this, 'do_404_header' ) );
510
-
511
- // send http 410 instead of http 404 if requested resource is in trash
512
- // @since 3.2
513
-
514
- if ( $this->settings()->get( 'http410_if_trashed' ) && $this->is_url_in_trash( rawurldecode ( ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) ) {
515
-
516
- status_header( 410 );
517
-
518
- } else {
519
-
520
- status_header( 404 );
521
-
522
- }
523
- nocache_headers();
524
- }
525
-
526
-
527
- /**
528
- * add body classes
529
- */
530
- function add_404_body_class( $classes ) {
531
-
532
- // as of v 3.1 we first check if the class error404 already exists
533
- if ( ! in_array( 'error404', $classes ) ) {
534
-
535
- $classes[] = 'error404';
536
-
537
- }
538
-
539
- // debug class
540
- // @since 3.1
541
- $debug_class = 'pp404-';
542
- if ( $this->is_native() ) {
543
- $debug_class .= 'native';
544
- } elseif ( defined( 'CUSTOMIZR_VER' ) ) {
545
- $debug_class .= 'customizr';
546
- } elseif ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
547
- $debug_class .= 'wpml';
548
- } elseif ( $this->settings()->get( 'method' ) != 'STD' ) {
549
- $debug_class .= 'cmp';
550
- } else {
551
- $debug_class .= 'std';
552
- }
553
- $classes[] = $debug_class;
554
-
555
- return $classes;
556
- }
557
-
558
-
559
- /**
560
- * add body classes customizr mode
561
- * @since 3.1
562
- */
563
- function add_404_body_class_customizr_mode( $classes ) {
564
-
565
- if ( is_404() ) {
566
-
567
- $classes = $this->add_404_body_class( $classes );
568
-
569
- }
570
-
571
- return $classes;
572
- }
573
-
574
-
575
- /**
576
- * show title
577
- * Customizr Compatibility Mode
578
- */
579
- function show404title_customizr_mode( $title ) {
580
- if ( ! $this->is_native() ) {
581
- return '<h1 class="entry-title">' . get_the_title( $this->get_page_id() ) . '</h1>';
582
- } else {
583
- return $title;
584
- }
585
- }
586
-
587
-
588
- /**
589
- * show content
590
- * Customizr Compatibility Mode
591
- */
592
- function show404_customizr_mode( $content ) {
593
- if ( ! $this->is_native() ) {
594
- return '<div class="entry-content">' . apply_filters( 'the_content', get_post_field( 'post_content', $this->get_page_id() ) ) . '</div>';
595
- } else {
596
- return $content;
597
- }
598
- $this->do_404page_action();
599
- }
600
-
601
-
602
- /**
603
- * change article selectors
604
- * Customizr Compatibility Mode
605
- */
606
- function show404articleselectors_customizr_mode( $selectors ) {
607
- if ( ! $this->is_native() ) {
608
- return 'id="post-' . $this->get_page_id() . '" class="' . join( ' ', get_post_class( 'row-fluid', $this->get_page_id() ) ) . '"';
609
- } else {
610
- return $selectors;
611
- }
612
- }
613
-
614
-
615
- /**
616
- * do we have to force a 404 in wp_head?
617
- */
618
- function maybe_force_404() {
619
- if ( $this->settings()->get( 'force_error' ) ) {
620
- add_action( 'wp_head', array( $this, 'force_404_start' ), 9.9 );
621
- add_action( 'wp_head', array( $this, 'force_404_end' ), 99 );
622
- }
623
- }
624
-
625
-
626
- /**
627
- * Force 404 in wp_head start
628
- * potentially dangerous!
629
- */
630
- function force_404_start() {
631
- global $wp_query;
632
- $wp_query->is_404 = true;
633
- }
634
-
635
-
636
- /**
637
- * Force 404 in wp_head end
638
- * potentially dangerous!
639
- */
640
- function force_404_end() {
641
- global $wp_query;
642
- $wp_query->is_404 = false;
643
- }
644
-
645
-
646
- /**
647
- * disable URL autocorrect guessing
648
- */
649
- function no_url_guessing( $redirect_url ) {
650
- if ( is_404() && !isset($_GET['p']) ) {
651
- $redirect_url = false;
652
- }
653
- return $redirect_url;
654
- }
655
-
656
-
657
- /**
658
- * send http 410 instead of http 404 in case the requested URL can be found in trash
659
- * @since 3.2
660
- * or always send http 410
661
- * @since 11.3.0
662
-
663
- */
664
- function maybe_send_410() {
665
-
666
- // we don't do anything if there is no 404
667
- if ( is_404() ) {
668
-
669
- //if ( $this->is_url_in_trash( rawurldecode ( ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) ) {
670
-
671
- if ( $this->settings()->get( 'http410_always' ) || ( $this->settings()->get( 'http410_if_trashed' ) && $this->is_url_in_trash( rawurldecode ( ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) ) ) {
672
-
673
- status_header( 410 );
674
-
675
- }
676
- }
677
-
678
- }
679
-
680
-
681
- /**
682
- * hide the 404 page from the list of pages
683
- */
684
-
685
- function exclude_404page( $query ) {
686
-
687
- $pageid = $this->get_page_id();
688
-
689
- if ( $pageid > 0 ) {
690
-
691
- global $pagenow;
692
-
693
- $post_type = $query->get( 'post_type' );
694
-
695
- // as of v 2.3 we check the post_type on front end
696
- // as of v 2.5 we also hide the page from search results on front end
697
- if( ( is_admin() && ( 'edit.php' == $pagenow && !current_user_can( 'create_users' ) ) ) || ( ! is_admin() && ( is_search() || ( !empty( $post_type) && ( ('page' === $post_type || 'any' === $post_type) || ( is_array( $post_type ) && in_array( 'page', $post_type ) ) ) ) ) ) ) {
698
-
699
- // as of v 2.4 we hide all translations in admin for WPML
700
- // as of v 2.5 we hide all translations from search results on front end for WPML
701
- if ( is_admin() || ( ! is_admin() && is_search() ) ) {
702
-
703
- $pageids = $this->get_all_page_ids();
704
-
705
- } else {
706
-
707
- $pageids = array( $pageid );
708
-
709
- }
710
-
711
- // as of v 2.3 we add the ID of the 404 page to post__not_in
712
- // using just $query->set() overrides existing settings but not adds a new setting
713
- $query->set( 'post__not_in', array_merge( (array)$query->get( 'post__not_in', array() ), $pageids ) );
714
-
715
- }
716
-
717
- }
718
-
719
- }
720
-
721
-
722
- /**
723
- * remove the 404 page from get_pages result array
724
- */
725
- function remove_404page_from_array( $pages, $r ) {
726
-
727
- $pageid = $this->get_page_id();
728
-
729
- if ( $pageid > 0 ) {
730
-
731
- for ( $i = 0; $i < sizeof( $pages ); $i++ ) {
732
-
733
- if ( $pages[$i]->ID == $pageid ) {
734
-
735
- unset( $pages[$i] );
736
- break;
737
-
738
- }
739
-
740
- }
741
-
742
- }
743
-
744
- return array_values( $pages );
745
-
746
- }
747
-
748
-
749
- /**
750
- * check if the requested url is found in trash
751
- * @since 3.2
752
- * based on WP core function url_to_postid()
753
- */
754
- function is_url_in_trash( $url ) {
755
-
756
- global $wp_rewrite;
757
- global $wp;
758
-
759
- // First, check to see if there is a 'p=N' or 'page_id=N' to match against
760
- if ( preg_match( '#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values ) ) {
761
-
762
- $id = absint( $values[2] );
763
-
764
- if ( $id ) {
765
-
766
- if ( 'trash' == get_post_status( $id ) ) {
767
-
768
- return true;
769
-
770
- } else {
771
-
772
- return false;
773
-
774
- }
775
-
776
- }
777
-
778
- }
779
-
780
- // Check to see if we are using rewrite rules
781
- $rewrite = $wp_rewrite->wp_rewrite_rules();
782
-
783
- // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
784
- if ( empty( $rewrite ) ) {
785
-
786
- return false;
787
-
788
- }
789
-
790
- // Get rid of the #anchor
791
- $url_split = explode('#', $url);
792
- $url = $url_split[0];
793
-
794
- // Get rid of URL ?query=string
795
- $url_split = explode('?', $url);
796
- $url = $url_split[0];
797
-
798
- // Add 'www.' if it is absent and should be there
799
- if ( false !== strpos( home_url(), '://www.' ) && false === strpos( $url, '://www.' ) ) {
800
-
801
- $url = str_replace('://', '://www.', $url);
802
-
803
- }
804
-
805
- // Strip 'www.' if it is present and shouldn't be
806
- if ( false === strpos( home_url(), '://www.' ) ) {
807
-
808
- $url = str_replace('://www.', '://', $url);
809
-
810
- }
811
-
812
- // Strip 'index.php/' if we're not using path info permalinks
813
- if ( !$wp_rewrite->using_index_permalinks() ) {
814
-
815
- $url = str_replace( $wp_rewrite->index . '/', '', $url );
816
-
817
- }
818
-
819
-
820
- if ( false !== strpos( trailingslashit( $url ), home_url( '/' ) ) ) {
821
-
822
- // Chop off http://domain.com/[path]
823
- $url = str_replace(home_url(), '', $url);
824
-
825
- } else {
826
-
827
- // Chop off /path/to/blog
828
- $home_path = parse_url( home_url( '/' ) );
829
- $home_path = isset( $home_path['path'] ) ? $home_path['path'] : '' ;
830
- $url = preg_replace( sprintf( '#^%s#', preg_quote( $home_path ) ), '', trailingslashit( $url ) );
831
-
832
- }
833
-
834
- // Trim leading and lagging slashes
835
- $url = trim($url, '/');
836
-
837
- $request = $url;
838
- $post_type_query_vars = array();
839
-
840
- foreach ( get_post_types( array() , 'objects' ) as $post_type => $t ) {
841
-
842
- if ( ! empty( $t->query_var ) ) {
843
-
844
- $post_type_query_vars[ $t->query_var ] = $post_type;
845
-
846
- }
847
- }
848
-
849
- // Look for matches.
850
- $request_match = $request;
851
- foreach ( (array)$rewrite as $match => $query) {
852
-
853
- // If the requesting file is the anchor of the match, prepend it
854
- // to the path info.
855
- if ( !empty( $url ) && ( $url != $request ) && ( strpos( $match, $url ) === 0 ) ) {
856
-
857
- $request_match = $url . '/' . $request;
858
-
859
- }
860
-
861
- if ( preg_match( "#^$match#", $request_match, $matches ) ) {
862
-
863
- if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) {
864
-
865
- // This is a verbose page match, let's check to be sure about it.
866
- if ( ! get_page_by_path( $matches[ $varmatch[1] ] ) ) {
867
-
868
- continue;
869
-
870
- }
871
- }
872
-
873
- // Got a match.
874
-
875
- // Trim the query of everything up to the '?'.
876
- $query = preg_replace( "!^.+\?!", '', $query );
877
-
878
- // Substitute the substring matches into the query.
879
- $query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) );
880
-
881
- // Filter out non-public query vars
882
- parse_str( $query, $query_vars );
883
- $query = array();
884
-
885
- foreach ( (array) $query_vars as $key => $value ) {
886
-
887
- if ( in_array( $key, $wp->public_query_vars ) ) {
888
-
889
- $query[$key] = $value;
890
-
891
- if ( isset( $post_type_query_vars[$key] ) ) {
892
-
893
- $query['post_type'] = $post_type_query_vars[$key];
894
- $query['name'] = $value;
895
-
896
- }
897
-
898
- }
899
-
900
- }
901
-
902
- // Magic
903
- if ( isset( $query['pagename'] ) ) {
904
-
905
- $query['pagename'] .= '__trashed' ;
906
-
907
- }
908
-
909
- if ( isset( $query['name'] ) ) {
910
-
911
- $query['name'] .= '__trashed' ;
912
-
913
- }
914
-
915
- $query['post_status'] = array( 'trash' );
916
-
917
- // Resolve conflicts between posts with numeric slugs and date archive queries.
918
- $query = wp_resolve_numeric_slug_conflicts( $query );
919
-
920
- // Do the query
921
- $query = new WP_Query( $query );
922
-
923
- if ( $query->found_posts == 1 ) {
924
-
925
- return true;
926
-
927
- } else {
928
-
929
- return false;
930
-
931
- }
932
-
933
- }
934
-
935
- }
936
-
937
- return false;
938
-
939
- }
940
-
941
-
942
- /**
943
- * get the id of the 404 page in the current language if WPML or Polylang is active
944
- * public since v 11.1.0 to use it in pp_404_get_page_id()
945
- */
946
- public function get_page_id() {
947
-
948
- $pageid = $this->settings()->get( 'page_id' );
949
-
950
- if ( $pageid > 0 ) {
951
-
952
- if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
953
-
954
- // WPML is active
955
- $pageid = apply_filters( 'wpml_object_id', $pageid, 'page', true );
956
-
957
- } elseif ( function_exists( 'pll_get_post' ) ) {
958
-
959
- // was defined( 'POLYLANG_VERSION' ) before version 11.2.3
960
-
961
- // Polylang is active
962
- $translatedpageid = pll_get_post( $pageid );
963
- if ( !empty( $translatedpageid ) && 'publish' == get_post_status( $translatedpageid ) ) {
964
- $pageid = $translatedpageid;
965
- }
966
-
967
- }
968
-
969
- }
970
-
971
- return $pageid;
972
-
973
- }
974
-
975
-
976
- /**
977
- * get 404 pages in all available languages
978
- * if WPML is active this function returns an array of all page ids in all available languages
979
- * otherwise it returns the page id as array
980
- * introduced in v 2.4
981
- * public since v9 to access it from other classes
982
- */
983
- public function get_all_page_ids() {
984
-
985
- if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
986
-
987
- // WPML is active
988
- // get an array for all translations
989
- $pageid = $this->settings()->get( 'page_id' );
990
- $pages = array( $pageid );
991
-
992
- if ( $pageid > 0 ) {
993
-
994
- $languages = apply_filters( 'wpml_active_languages', NULL );
995
-
996
- if ( !empty( $languages ) ) {
997
-
998
- foreach( $languages as $l ) {
999
-
1000
- $p = apply_filters( 'wpml_object_id', $pageid, 'page', false, $l['language_code'] );
1001
-
1002
- if ( $p ) {
1003
-
1004
- $pages[] = $p;
1005
-
1006
- }
1007
-
1008
- }
1009
-
1010
- }
1011
-
1012
- }
1013
-
1014
- $pageids = array_unique( $pages, SORT_NUMERIC );
1015
-
1016
- } else {
1017
-
1018
- $pageids = array( $this->settings()->get( 'page_id' ) );
1019
-
1020
- }
1021
-
1022
- return $pageids;
1023
-
1024
- }
1025
-
1026
-
1027
- /**
1028
- * fire 404page_after_404 hook to make plugin expandable
1029
- */
1030
- function do_404page_action() {
1031
-
1032
- do_action( '404page_after_404' );
1033
-
1034
- }
1035
-
1036
-
1037
- /**
1038
- * uninstall plugin
1039
- */
1040
- function uninstall() {
1041
-
1042
- if( is_multisite() ) {
1043
-
1044
- $this->uninstall_network();
1045
-
1046
- } else {
1047
-
1048
- $this->uninstall_single();
1049
-
1050
- }
1051
-
1052
- }
1053
-
1054
-
1055
- /**
1056
- * uninstall network wide
1057
- */
1058
- function uninstall_network() {
1059
-
1060
- global $wpdb;
1061
- $activeblog = $wpdb->blogid;
1062
- $blogids = $wpdb->get_col( esc_sql( 'SELECT blog_id FROM ' . $wpdb->blogs ) );
1063
-
1064
- foreach ( $blogids as $blogid ) {
1065
-
1066
- switch_to_blog( $blogid );
1067
- $this->uninstall_single();
1068
-
1069
- }
1070
-
1071
- switch_to_blog( $activeblog );
1072
-
1073
- }
1074
-
1075
-
1076
- /**
1077
- * uninstall for a single blog
1078
- */
1079
- function uninstall_single() {
1080
-
1081
- $this->data_remove();
1082
- $this->settings()->remove();
1083
-
1084
- }
1085
-
1086
-
1087
- /**
1088
- * functions for theme usage
1089
- */
1090
-
1091
- // check if there's a custom 404 page set
1092
- function pp_404_is_active() {
1093
-
1094
- return ( $this->settings()->get( 'page_id' ) > 0 );
1095
-
1096
- }
1097
-
1098
- // activate the native theme support
1099
- function pp_404_set_native_support() {
1100
-
1101
- $this->set_native();
1102
-
1103
- }
1104
-
1105
- // get the title - native theme support
1106
- function pp_404_get_the_title() {
1107
-
1108
- $title = '';
1109
-
1110
- if ( $this->settings()->get( 'page_id' ) > 0 && $this->is_native() ) {
1111
-
1112
- $title = get_the_title( $this->get_page_id() );
1113
-
1114
- }
1115
-
1116
- return $title;
1117
-
1118
- }
1119
-
1120
- // print title - native theme support
1121
- function pp_404_the_title() {
1122
-
1123
- echo esc_html( $this->pp_404_get_the_title() );
1124
-
1125
- }
1126
-
1127
- // get the content - native theme support
1128
- function pp_404_get_the_content() {
1129
-
1130
- $content = '';
1131
-
1132
- if ( $this->settings()->get( 'page_id' ) > 0 && $this->is_native() ) {
1133
-
1134
- $content = apply_filters( 'the_content', get_post_field( 'post_content', $this->get_page_id() ) );
1135
-
1136
- }
1137
-
1138
- return $content;
1139
-
1140
- }
1141
-
1142
- // print content - native theme support
1143
- function pp_404_the_content() {
1144
-
1145
- echo esc_html( $this->pp_404_get_the_content() );
1146
-
1147
- }
1148
-
1149
-
1150
- /**
1151
- * set native mode
1152
- *
1153
- * @since 11.0.0 - was part of previous settings class
1154
- * @access public
1155
- */
1156
- public function set_native() {
1157
-
1158
- $this->native = true;
1159
-
1160
- }
1161
-
1162
-
1163
- /**
1164
- * is native mode ative
1165
- *
1166
- * @since 11.0.0 - was part of previous settings class
1167
- * @access public
1168
- */
1169
- public function is_native() {
1170
-
1171
- return ( true === $this->native );
1172
-
1173
- }
1174
-
1175
-
1176
- /**
1177
- * get settings class
1178
- *
1179
- * @since 11.3.0
1180
- * @access public
1181
- * @return object
1182
- */
1183
- public function admin() {
1184
-
1185
- return $this->admin;
1186
- }
1187
-
1188
-
1189
- }
1190
-
1191
- }
1192
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1193
  ?>
1
+ <?php
2
+
3
+ /**
4
+ * The 404page core plugin class
5
+ */
6
+
7
+
8
+ // If this file is called directly, abort
9
+ if ( ! defined( 'WPINC' ) ) {
10
+ die;
11
+ }
12
+
13
+
14
+ // indicate that 404page plugin is active
15
+ if ( ! defined( 'PP_404' ) ) {
16
+ define( 'PP_404', true );
17
+ }
18
+
19
+
20
+ /**
21
+ * The core plugin class
22
+ */
23
+ if ( !class_exists( 'PP_404Page' ) ) {
24
+
25
+
26
+ class PP_404Page extends PPF08_Plugin {
27
+
28
+
29
+ /**
30
+ * Native Mode
31
+ *
32
+ * @since 11.0.0 - was part of previous settings class
33
+ * @var bool
34
+ * @access private
35
+ */
36
+ private $native;
37
+
38
+ private $template;
39
+ private $postid;
40
+
41
+
42
+ /**
43
+ * Admin Class
44
+ *
45
+ * @see class-404page-admin.php
46
+ * @since 10
47
+ * @var object
48
+ * @access private
49
+ */
50
+ private $admin;
51
+
52
+
53
+ /**
54
+ * Block Editor Class
55
+ *
56
+ * @see class-404page-block-editor.php
57
+ * @since 9
58
+ * @var object
59
+ * @access private
60
+ */
61
+ private $blockeditor;
62
+
63
+
64
+ /**
65
+ * Classic Editor Class
66
+ *
67
+ * @see class-404page-classic-editor.php
68
+ * @since 9
69
+ * @var object
70
+ * @access private
71
+ */
72
+ private $classiceditor;
73
+
74
+
75
+ /**
76
+ * Deprecated Class
77
+ *
78
+ * @see class-404page-deprecated.php
79
+ * @since 11.0.0
80
+ * @var object
81
+ * @access private
82
+ */
83
+ private $deprecated;
84
+
85
+
86
+ /**
87
+ * URL that caused the 404 error
88
+ *
89
+ * @since 11.4.0
90
+ * @var array
91
+ * @access private
92
+ */
93
+ private $error_url;
94
+
95
+
96
+ /**
97
+ * Init the Class
98
+ *
99
+ * @since 11.0.0
100
+ * was part of __construct before
101
+ */
102
+ public function plugin_init() {
103
+
104
+ // settings defaults
105
+ // @since 11.0.0
106
+ $defaults = array(
107
+ 'page_id' => 0,
108
+ 'hide' => false,
109
+ 'fire_error' => true,
110
+ 'force_error' => false,
111
+ 'no_url_guessing' => false,
112
+ 'http410_if_trashed' => false,
113
+ 'http410_always' => false,
114
+ 'method' => 'STD'
115
+ );
116
+
117
+ // since 11.0.0 we use add_settings_class() to load the settings
118
+ $this->add_settings_class( 'PP_404Page_Settings', 'class-404page-settings', $this, $defaults );
119
+
120
+ // @since 11.0.0
121
+ $this->add_action( 'init' );
122
+ }
123
+
124
+
125
+ /**
126
+ * do plugin init
127
+ * this runs after init action has fired to ensure everything is loaded properly
128
+ * was init() before 11.0.0
129
+ */
130
+ function action_init() {
131
+
132
+ // moved from add_text_domain() in v 11.0.0
133
+ load_plugin_textdomain( '404page' );
134
+
135
+
136
+ // change old stuff to new stuff for backward compatibility
137
+ // since v 11.0.0
138
+ $this->deprecated = $this->add_sub_class_always( 'PP_404Page_Deprecated', 'class-404page-deprecated', $this );
139
+
140
+ // load the following subclasses only on backend
141
+ // using add_sub_class_backend() sinde v 11
142
+ $this->admin = $this->add_sub_class_backend( 'PP_404Page_Admin', 'class-404page-admin', $this, $this->settings() );
143
+ $this->blockeditor = $this->add_sub_class_backend( 'PP_404Page_BlockEditor', 'class-404page-block-editor', $this, $this->settings() );
144
+ $this->classiceditor = $this->add_sub_class_backend( 'PP_404Page_ClassicEditor', 'class-404page-classic-editor', $this, $this->settings() );
145
+
146
+ // as of v 2.2 always call set_mode
147
+ // as of v 2.4 we do not need to add an init action hook
148
+
149
+ if ( !is_admin() && $this->settings()->get( 'page_id' ) > 0 ) {
150
+
151
+ // as of v 3.0 we once check if there's a 404 page set and not in all functions separately
152
+ $this->set_mode();
153
+ add_action( 'pre_get_posts', array ( $this, 'exclude_404page' ) );
154
+ add_filter( 'get_pages', array ( $this, 'remove_404page_from_array' ), 10, 2 );
155
+
156
+ // Stop URL guessing if activated
157
+ if ( $this->settings()->get( 'no_url_guessing' ) ) {
158
+ add_filter( 'redirect_canonical' ,array ( $this, 'no_url_guessing' ) );
159
+ }
160
+
161
+
162
+ // Remove 404 error page from XML sitemaps
163
+ // only if "Send an 404 error if the page is accessed directly by its URL" is active
164
+ if ( $this->settings()->get( 'fire_error' ) ) {
165
+
166
+ // YOAST sitemap
167
+ // @since 6
168
+
169
+ // we do not need to check if Yoast Sitemap feature is activated because the filter only fires if it is active
170
+ add_filter( 'wpseo_exclude_from_sitemap_by_post_ids', function ( $alreadyExcluded ) {
171
+ // as of 11.0.5 we add the page ID to the array of already excluded pages
172
+ // plus we exclude the 404 page in all languages
173
+ return array_merge( $alreadyExcluded, $this->get_all_page_ids() );
174
+ }, 999 );
175
+
176
+
177
+ // Jetpack sitemap
178
+ // @since 11.1.2
179
+
180
+ // we do not need to check if Jetpack Sitemap feature is activated because the filter only fires if it is active
181
+ add_filter( 'jetpack_sitemap_skip_post', function ( $skip, $post ) {
182
+ if ( in_array( intval( $post->ID ), $this->get_all_page_ids() ) ) {
183
+ $skip = true;
184
+ }
185
+ return $skip;
186
+ }, 999, 2 );
187
+
188
+ }
189
+
190
+ }
191
+
192
+
193
+ if ( class_exists( 'PP_404Page_Admin' ) ) {
194
+
195
+ // load classes only if in admin
196
+ // @since 10
197
+ // using class_exists( 'PP_404Page_Admin' ) instead of is_admin() as of v 10.3 for compatibilty with iThemes Sync
198
+
199
+ //$this->admin = new PP_404Page_Admin( $this, $this->settings );
200
+ //$this->blockeditor = new PP_404Page_BlockEditor( $this );
201
+ //$this->classiceditor = new PP_404Page_ClassicEditor( $this );
202
+
203
+ // Remove 404 page from post list if activated
204
+ // not moved to PP_404Page_Admin because we also need exclude_404page() in frontend
205
+ if ( $this->settings()->get( 'hide' ) and $this->settings()->get( 'page_id' ) > 0 ) {
206
+ add_action( 'pre_get_posts' ,array ( $this, 'exclude_404page' ) );
207
+ }
208
+
209
+ }
210
+
211
+ }
212
+
213
+
214
+ /**
215
+ * init filters
216
+ */
217
+ function set_mode() {
218
+
219
+ $this->settings()->set_method();
220
+
221
+ if ( defined( 'CUSTOMIZR_VER' ) ) {
222
+
223
+ // Customizr Compatibility Mode
224
+
225
+ // @since 3.1
226
+ add_filter( 'body_class', array( $this, 'add_404_body_class_customizr_mode' ) );
227
+
228
+ add_filter( 'tc_404_header_content', array( $this, 'show404title_customizr_mode' ), 999 );
229
+ add_filter( 'tc_404_content', array( $this, 'show404_customizr_mode' ), 999 );
230
+ add_filter( 'tc_404_selectors', array( $this, 'show404articleselectors_customizr_mode' ), 999 );
231
+
232
+ // send http 410 instead of http 404 if requested resource is in trash
233
+ // @since 3.2
234
+ // or always send http 410
235
+ // @since 11.3.0
236
+ if ( $this->settings()->get( 'http410_if_trashed' ) || $this->settings()->get( 'http410_always' ) ) {
237
+
238
+ add_action( 'template_redirect', array( $this, 'maybe_send_410' ) );
239
+
240
+ }
241
+
242
+ } elseif ( $this->settings()->get( 'method' ) != 'STD' ) {
243
+
244
+ // Compatibility Mode
245
+ // as of v 2.4 we use the the_posts filter instead of posts_results, because the posts array is internally processed after posts_results fires
246
+ add_filter( 'the_posts', array( $this, 'show404_compatiblity_mode' ), 999 );
247
+
248
+ // as of v 2.5 we remove the filter if the DW Question & Answer plugin by DesignWall (https://www.designwall.com/wordpress/plugins/dw-question-answer/) is active and we're in the answers list
249
+ add_filter( 'dwqa_prepare_answers', array( $this, 'remove_show404_compatiblity_mode' ), 999 );
250
+
251
+ } else {
252
+
253
+ // Standard Mode
254
+ add_filter( '404_template', array( $this, 'show404_standard_mode' ), 999 );
255
+
256
+ if ( $this->settings()->get( 'fire_error' ) ) {
257
+
258
+ add_action( 'template_redirect', array( $this, 'do_404_header_standard_mode' ) );
259
+
260
+ }
261
+
262
+ // send http 410 instead of http 404 if requested resource is in trash
263
+ // @since 3.2
264
+ // or always send http 410
265
+ // @since 11.3.0
266
+ if ( $this->settings()->get( 'http410_if_trashed' ) || $this->settings()->get( 'http410_always' ) ) {
267
+
268
+ add_action( 'template_redirect', array( $this, 'maybe_send_410' ) );
269
+
270
+ }
271
+
272
+ }
273
+
274
+ }
275
+
276
+
277
+ /**
278
+ * show 404 page
279
+ * Standard Mode
280
+ */
281
+ function show404_standard_mode( $template ) {
282
+
283
+ global $wp_query;
284
+
285
+ // @since 4
286
+ // fix for an ugly bbPress problem
287
+ // see https://wordpress.org/support/topic/not-fully-bbpress-compatible/
288
+ // see https://bbpress.trac.wordpress.org/ticket/3161
289
+ // if a bbPress member page is shown and the member has no topics created yet the 404_template filter hook fires
290
+ // this is a bbPress problem but it has not been fixed since 6 months
291
+ // so let's bypass the problem
292
+ if ( function_exists( 'bbp_is_single_user' ) ) {
293
+
294
+ if ( bbp_is_single_user() ) {
295
+
296
+ return $template;
297
+
298
+ }
299
+
300
+ }
301
+ // that's it
302
+
303
+ // save URL that caused the 404 - since 11.4.0
304
+ $this->set_404_url();
305
+
306
+ if ( ! $this->is_native() ) {
307
+
308
+ $this->disable_caching();
309
+
310
+ $wp_query = null;
311
+ $wp_query = new WP_Query();
312
+ $wp_query->query( 'page_id=' . $this->get_page_id() );
313
+
314
+
315
+ $wp_query->the_post();
316
+ $template = get_page_template();
317
+ rewind_posts();
318
+ add_filter( 'body_class', array( $this, 'add_404_body_class' ) );
319
+ }
320
+ $this->maybe_force_404();
321
+ $this->do_404page_action();
322
+ return $template;
323
+
324
+ }
325
+
326
+
327
+ /**
328
+ * show 404 page
329
+ * Compatibility Mode
330
+ */
331
+ function show404_compatiblity_mode( $posts ) {
332
+
333
+ global $wp_query;
334
+
335
+ // remove the filter so we handle only the first query - no custom queries
336
+ remove_filter( 'the_posts', array( $this, 'show404_compatiblity_mode' ), 999 );
337
+
338
+ // @since 4
339
+ // fix for an ugly bbPress problem
340
+ // see show404_standard_mode()
341
+ if ( function_exists( 'bbp_is_single_user' ) ) {
342
+
343
+ if ( bbp_is_single_user() ) {
344
+
345
+ return $posts;
346
+
347
+ }
348
+
349
+ }
350
+ // that's it
351
+
352
+ $pageid = $this->get_page_id();
353
+ if ( ! $this->is_native() ) {
354
+
355
+ // as of v 10 we also check if $wp_query->query[error] == 404
356
+ // this is necessary to bypass a WordPress bug
357
+ // if permalink setting is something like e.g. /blog/%postname%/ the $posts is not empty
358
+ // bug reported https://core.trac.wordpress.org/ticket/46000
359
+
360
+ // as of v 11.0.3 we also check for REST_REQUEST to not create a 404 page in case of REST API call
361
+ // as of v 11.2.4 we also check for DOING_CRON
362
+ if ( ( empty( $posts ) || ( isset( $wp_query->query['error'] ) && $wp_query->query['error'] == 404 ) ) && is_main_query() && !is_robots() && !is_home() && !is_feed() && !is_search() && !is_archive() && ( !defined('DOING_AJAX') || !DOING_AJAX ) && ( !defined('DOING_CRON') || !DOING_CRON ) && ( !defined('REST_REQUEST') || !REST_REQUEST ) ) {
363
+
364
+ // save URL that caused the 404 - since 11.4.0
365
+ $this->set_404_url();
366
+
367
+ // as of v2.1 we do not alter the posts argument here because this does not work with SiteOrigin's Page Builder Plugin, template_include filter introduced
368
+ $this->postid = $pageid;
369
+
370
+ // as of v 2.4 we use the the_posts filter instead of posts_results
371
+ // therefore we have to reset $wp_query
372
+ // resetting $wp_query also forces us to remove the pre_get_posts action plus the get_pages filter
373
+
374
+ remove_action( 'pre_get_posts', array ( $this, 'exclude_404page' ) );
375
+ remove_filter( 'get_pages', array ( $this, 'remove_404page_from_array' ), 10, 2 );
376
+
377
+ $this->disable_caching();
378
+
379
+ $wp_query = null;
380
+ $wp_query = new WP_Query();
381
+
382
+ // @since 8
383
+ // added suppress_filters for compatibilty with current WPML version
384
+ $wp_query->query( array( 'page_id' => $pageid, 'suppress_filters' => true ) );
385
+
386
+
387
+ $wp_query->the_post();
388
+ $this->template = get_page_template();
389
+ $posts = $wp_query->posts;
390
+ $wp_query->rewind_posts();
391
+
392
+ add_action( 'wp', array( $this, 'do_404_header' ) );
393
+ add_filter( 'body_class', array( $this, 'add_404_body_class' ) );
394
+ add_filter( 'template_include', array( $this, 'change_404_template' ), 999 );
395
+
396
+
397
+
398
+ $this->maybe_force_404();
399
+ $this->do_404page_action();
400
+
401
+ } elseif ( 1 == count( $posts ) && 'page' == $posts[0]->post_type ) {
402
+
403
+ // Do a 404 if the 404 page is opened directly
404
+ if ( $this->settings()->get( 'fire_error' ) ) {
405
+
406
+ // save URL that caused the 404 - since 11.4.0
407
+ $this->set_404_url();
408
+
409
+ $curpageid = $posts[0]->ID;
410
+
411
+ if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
412
+
413
+ // WPML is active - get the post ID of the default language
414
+ global $sitepress;
415
+ $curpageid = apply_filters( 'wpml_object_id', $curpageid, 'page', $sitepress->get_default_language() );
416
+ $pageid = apply_filters( 'wpml_object_id', $pageid, 'page', $sitepress->get_default_language() );
417
+
418
+ } elseif ( function_exists( 'pll_get_post' ) ) {
419
+
420
+ // was defined( 'POLYLANG_VERSION' ) before version 11.2.3
421
+
422
+ // Polylang is active - get the post ID of the default language
423
+ $curpageid = pll_get_post( $curpageid, pll_default_language() );
424
+ $pageid = pll_get_post( $pageid, pll_default_language() );
425
+
426
+ }
427
+
428
+ if ( $pageid == $curpageid ) {
429
+ add_action( 'wp', array( $this, 'do_404_header' ) );
430
+ add_filter( 'body_class', array( $this, 'add_404_body_class' ) );
431
+ $this->maybe_force_404();
432
+ $this->do_404page_action();
433
+ }
434
+ }
435
+
436
+ }
437
+ } else {
438
+ $this->maybe_force_404();
439
+ $this->do_404page_action();
440
+ }
441
+ return $posts;
442
+ }
443
+
444
+ /**
445
+ * disable caching for known caching plugins
446
+ *
447
+ * @since 11.2.0
448
+ */
449
+ function disable_caching() {
450
+
451
+ // WP Super Cache
452
+ if ( defined( 'WPCACHEHOME' ) ) {
453
+
454
+ global $cache_enabled;
455
+
456
+ // is caching active?
457
+ if ( $cache_enabled ) {
458
+
459
+ define( 'DONOTCACHEPAGE', true );
460
+
461
+ }
462
+
463
+ }
464
+
465
+
466
+ // W3 Total Cache
467
+ if ( defined( 'W3TC' ) ) {
468
+
469
+ if ( class_exists( 'W3TC\Dispatcher' ) ) {
470
+
471
+ // is caching active?
472
+ if ( W3TC\Dispatcher::config()->get_boolean( 'pgcache.enabled' ) ) {
473
+
474
+ define( 'DONOTCACHEPAGE', true );
475
+
476
+ }
477
+
478
+ }
479
+
480
+ }
481
+
482
+ }
483
+
484
+
485
+ /**
486
+ * for DW Question & Answer plugin
487
+ * this function is called by the dwqa_prepare_answers filter
488
+ */
489
+ function remove_show404_compatiblity_mode( $args ) {
490
+ remove_filter( 'the_posts', array( $this, 'show404_compatiblity_mode' ), 999 );
491
+ return $args;
492
+ }
493
+
494
+
495
+ /**
496
+ * this function overrides the page template in compatibilty mode
497
+ */
498
+ function change_404_template( $template ) {
499
+
500
+ // we have to check if the template file is there because if the theme was changed maybe a wrong template is stored in the database
501
+ $new_template = locate_template( array( $this->template ) );
502
+ if ( '' != $new_template ) {
503
+ return $new_template ;
504
+ }
505
+ return $template;
506
+ }
507
+
508
+
509
+ /**
510
+ * send 404 HTTP header
511
+ * Standard Mode
512
+ */
513
+ function do_404_header_standard_mode() {
514
+ if ( is_page() && get_the_ID() == $this->settings()->get( 'page_id' ) && !is_404() ) {
515
+ // save URL that caused the 404 - since 11.4.0
516
+ $this->set_404_url();
517
+
518
+ status_header( 404 );
519
+ nocache_headers();
520
+ $this->maybe_force_404();
521
+ $this->do_404page_action();
522
+ }
523
+ }
524
+
525
+
526
+ /**
527
+ * send 404 HTTP header
528
+ * Compatibility Mode
529
+ */
530
+ function do_404_header() {
531
+ // remove the action so we handle only the first query - no custom queries
532
+ remove_action( 'wp', array( $this, 'do_404_header' ) );
533
+
534
+ // send http 410 instead of http 404 if requested resource is in trash
535
+ // @since 3.2
536
+
537
+ if ( $this->settings()->get( 'http410_if_trashed' ) && $this->is_url_in_trash( $this->get_404_url() ) ) {
538
+
539
+ status_header( 410 );
540
+
541
+ } else {
542
+
543
+ status_header( 404 );
544
+
545
+ }
546
+ nocache_headers();
547
+ }
548
+
549
+
550
+ /**
551
+ * add body classes
552
+ */
553
+ function add_404_body_class( $classes ) {
554
+
555
+ // as of v 3.1 we first check if the class error404 already exists
556
+ if ( ! in_array( 'error404', $classes ) ) {
557
+
558
+ $classes[] = 'error404';
559
+
560
+ }
561
+
562
+ // debug class
563
+ // @since 3.1
564
+ $debug_class = 'pp404-';
565
+ if ( $this->is_native() ) {
566
+ $debug_class .= 'native';
567
+ } elseif ( defined( 'CUSTOMIZR_VER' ) ) {
568
+ $debug_class .= 'customizr';
569
+ } elseif ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
570
+ $debug_class .= 'wpml';
571
+ } elseif ( $this->settings()->get( 'method' ) != 'STD' ) {
572
+ $debug_class .= 'cmp';
573
+ } else {
574
+ $debug_class .= 'std';
575
+ }
576
+ $classes[] = $debug_class;
577
+
578
+ return $classes;
579
+ }
580
+
581
+
582
+ /**
583
+ * add body classes customizr mode
584
+ * @since 3.1
585
+ */
586
+ function add_404_body_class_customizr_mode( $classes ) {
587
+
588
+ if ( is_404() ) {
589
+
590
+ // save URL that caused the 404 - since 11.4.0
591
+ $this->set_404_url();
592
+
593
+ $classes = $this->add_404_body_class( $classes );
594
+
595
+ }
596
+
597
+ return $classes;
598
+ }
599
+
600
+
601
+ /**
602
+ * show title
603
+ * Customizr Compatibility Mode
604
+ */
605
+ function show404title_customizr_mode( $title ) {
606
+ if ( ! $this->is_native() ) {
607
+ return '<h1 class="entry-title">' . get_the_title( $this->get_page_id() ) . '</h1>';
608
+ } else {
609
+ return $title;
610
+ }
611
+ }
612
+
613
+
614
+ /**
615
+ * show content
616
+ * Customizr Compatibility Mode
617
+ */
618
+ function show404_customizr_mode( $content ) {
619
+ if ( ! $this->is_native() ) {
620
+ return '<div class="entry-content">' . apply_filters( 'the_content', get_post_field( 'post_content', $this->get_page_id() ) ) . '</div>';
621
+ } else {
622
+ return $content;
623
+ }
624
+ $this->do_404page_action();
625
+ }
626
+
627
+
628
+ /**
629
+ * change article selectors
630
+ * Customizr Compatibility Mode
631
+ */
632
+ function show404articleselectors_customizr_mode( $selectors ) {
633
+ if ( ! $this->is_native() ) {
634
+ return 'id="post-' . $this->get_page_id() . '" class="' . join( ' ', get_post_class( 'row-fluid', $this->get_page_id() ) ) . '"';
635
+ } else {
636
+ return $selectors;
637
+ }
638
+ }
639
+
640
+
641
+ /**
642
+ * do we have to force a 404 in wp_head?
643
+ */
644
+ function maybe_force_404() {
645
+ if ( $this->settings()->get( 'force_error' ) ) {
646
+ add_action( 'wp_head', array( $this, 'force_404_start' ), 9.9 );
647
+ add_action( 'wp_head', array( $this, 'force_404_end' ), 99 );
648
+ }
649
+ }
650
+
651
+
652
+ /**
653
+ * Force 404 in wp_head start
654
+ * potentially dangerous!
655
+ */
656
+ function force_404_start() {
657
+ global $wp_query;
658
+ $wp_query->is_404 = true;
659
+ }
660
+
661
+
662
+ /**
663
+ * Force 404 in wp_head end
664
+ * potentially dangerous!
665
+ */
666
+ function force_404_end() {
667
+ global $wp_query;
668
+ $wp_query->is_404 = false;
669
+ }
670
+
671
+
672
+ /**
673
+ * disable URL autocorrect guessing
674
+ */
675
+ function no_url_guessing( $redirect_url ) {
676
+ if ( is_404() && !isset($_GET['p']) ) {
677
+ $redirect_url = false;
678
+ }
679
+ return $redirect_url;
680
+ }
681
+
682
+
683
+ /**
684
+ * send http 410 instead of http 404 in case the requested URL can be found in trash
685
+ * @since 3.2
686
+ * or always send http 410
687
+ * @since 11.3.0
688
+
689
+ */
690
+ function maybe_send_410() {
691
+
692
+ // we don't do anything if there is no 404
693
+ if ( is_404() ) {
694
+
695
+ if ( $this->settings()->get( 'http410_always' ) || ( $this->settings()->get( 'http410_if_trashed' ) && $this->is_url_in_trash( $this->get_404_url() ) ) ) {
696
+
697
+ status_header( 410 );
698
+
699
+ }
700
+ }
701
+
702
+ }
703
+
704
+
705
+ /**
706
+ * hide the 404 page from the list of pages
707
+ */
708
+
709
+ function exclude_404page( $query ) {
710
+
711
+ $pageid = $this->get_page_id();
712
+
713
+ if ( $pageid > 0 ) {
714
+
715
+ global $pagenow;
716
+
717
+ $post_type = $query->get( 'post_type' );
718
+
719
+ // as of v 2.3 we check the post_type on front end
720
+ // as of v 2.5 we also hide the page from search results on front end
721
+ if( ( is_admin() && ( 'edit.php' == $pagenow && !current_user_can( 'create_users' ) ) ) || ( ! is_admin() && ( is_search() || ( !empty( $post_type) && ( ('page' === $post_type || 'any' === $post_type) || ( is_array( $post_type ) && in_array( 'page', $post_type ) ) ) ) ) ) ) {
722
+
723
+ // as of v 2.4 we hide all translations in admin for WPML
724
+ // as of v 2.5 we hide all translations from search results on front end for WPML
725
+ if ( is_admin() || ( ! is_admin() && is_search() ) ) {
726
+
727
+ $pageids = $this->get_all_page_ids();
728
+
729
+ } else {
730
+
731
+ $pageids = array( $pageid );
732
+
733
+ }
734
+
735
+ // as of v 2.3 we add the ID of the 404 page to post__not_in
736
+ // using just $query->set() overrides existing settings but not adds a new setting
737
+ $query->set( 'post__not_in', array_merge( (array)$query->get( 'post__not_in', array() ), $pageids ) );
738
+
739
+ }
740
+
741
+ }
742
+
743
+ }
744
+
745
+
746
+ /**
747
+ * remove the 404 page from get_pages result array
748
+ */
749
+ function remove_404page_from_array( $pages, $r ) {
750
+
751
+ $pageid = $this->get_page_id();
752
+
753
+ if ( $pageid > 0 ) {
754
+
755
+ for ( $i = 0; $i < sizeof( $pages ); $i++ ) {
756
+
757
+ if ( $pages[$i]->ID == $pageid ) {
758
+
759
+ unset( $pages[$i] );
760
+ break;
761
+
762
+ }
763
+
764
+ }
765
+
766
+ }
767
+
768
+ return array_values( $pages );
769
+
770
+ }
771
+
772
+
773
+ /**
774
+ * check if the requested url is found in trash
775
+ * @since 3.2
776
+ * based on WP core function url_to_postid()
777
+ */
778
+ function is_url_in_trash( $url ) {
779
+
780
+ global $wp_rewrite;
781
+ global $wp;
782
+
783
+ // First, check to see if there is a 'p=N' or 'page_id=N' to match against
784
+ if ( preg_match( '#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values ) ) {
785
+
786
+ $id = absint( $values[2] );
787
+
788
+ if ( $id ) {
789
+
790
+ if ( 'trash' == get_post_status( $id ) ) {
791
+
792
+ return true;
793
+
794
+ } else {
795
+
796
+ return false;
797
+
798
+ }
799
+
800
+ }
801
+
802
+ }
803
+
804
+ // Check to see if we are using rewrite rules
805
+ $rewrite = $wp_rewrite->wp_rewrite_rules();
806
+
807
+ // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
808
+ if ( empty( $rewrite ) ) {
809
+
810
+ return false;
811
+
812
+ }
813
+
814
+ // Get rid of the #anchor
815
+ $url_split = explode('#', $url);
816
+ $url = $url_split[0];
817
+
818
+ // Get rid of URL ?query=string
819
+ $url_split = explode('?', $url);
820
+ $url = $url_split[0];
821
+
822
+ // Add 'www.' if it is absent and should be there
823
+ if ( false !== strpos( home_url(), '://www.' ) && false === strpos( $url, '://www.' ) ) {
824
+
825
+ $url = str_replace('://', '://www.', $url);
826
+
827
+ }
828
+
829
+ // Strip 'www.' if it is present and shouldn't be
830
+ if ( false === strpos( home_url(), '://www.' ) ) {
831
+
832
+ $url = str_replace('://www.', '://', $url);
833
+
834
+ }
835
+
836
+ // Strip 'index.php/' if we're not using path info permalinks
837
+ if ( !$wp_rewrite->using_index_permalinks() ) {
838
+
839
+ $url = str_replace( $wp_rewrite->index . '/', '', $url );
840
+
841
+ }
842
+
843
+
844
+ if ( false !== strpos( trailingslashit( $url ), home_url( '/' ) ) ) {
845
+
846
+ // Chop off http://domain.com/[path]
847
+ $url = str_replace(home_url(), '', $url);
848
+
849
+ } else {
850
+
851
+ // Chop off /path/to/blog
852
+ $home_path = parse_url( home_url( '/' ) );
853
+ $home_path = isset( $home_path['path'] ) ? $home_path['path'] : '' ;
854
+ $url = preg_replace( sprintf( '#^%s#', preg_quote( $home_path ) ), '', trailingslashit( $url ) );
855
+
856
+ }
857
+
858
+ // Trim leading and lagging slashes
859
+ $url = trim($url, '/');
860
+
861
+ $request = $url;
862
+ $post_type_query_vars = array();
863
+
864
+ foreach ( get_post_types( array() , 'objects' ) as $post_type => $t ) {
865
+
866
+ if ( ! empty( $t->query_var ) ) {
867
+
868
+ $post_type_query_vars[ $t->query_var ] = $post_type;
869
+
870
+ }
871
+ }
872
+
873
+ // Look for matches.
874
+ $request_match = $request;
875
+ foreach ( (array)$rewrite as $match => $query) {
876
+
877
+ // If the requesting file is the anchor of the match, prepend it
878
+ // to the path info.
879
+ if ( !empty( $url ) && ( $url != $request ) && ( strpos( $match, $url ) === 0 ) ) {
880
+
881
+ $request_match = $url . '/' . $request;
882
+
883
+ }
884
+
885
+ if ( preg_match( "#^$match#", $request_match, $matches ) ) {
886
+
887
+ if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) {
888
+
889
+ // This is a verbose page match, let's check to be sure about it.
890
+ if ( ! get_page_by_path( $matches[ $varmatch[1] ] ) ) {
891
+
892
+ continue;
893
+
894
+ }
895
+ }
896
+
897
+ // Got a match.
898
+
899
+ // Trim the query of everything up to the '?'.
900
+ $query = preg_replace( "!^.+\?!", '', $query );
901
+
902
+ // Substitute the substring matches into the query.
903
+ $query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) );
904
+
905
+ // Filter out non-public query vars
906
+ parse_str( $query, $query_vars );
907
+ $query = array();
908
+
909
+ foreach ( (array) $query_vars as $key => $value ) {
910
+
911
+ if ( in_array( $key, $wp->public_query_vars ) ) {
912
+
913
+ $query[$key] = $value;
914
+
915
+ if ( isset( $post_type_query_vars[$key] ) ) {
916
+
917
+ $query['post_type'] = $post_type_query_vars[$key];
918
+ $query['name'] = $value;
919
+
920
+ }
921
+
922
+ }
923
+
924
+ }
925
+
926
+ // Magic
927
+ if ( isset( $query['pagename'] ) ) {
928
+
929
+ $query['pagename'] .= '__trashed' ;
930
+
931
+ }
932
+
933
+ if ( isset( $query['name'] ) ) {
934
+
935
+ $query['name'] .= '__trashed' ;
936
+
937
+ }
938
+
939
+ $query['post_status'] = array( 'trash' );
940
+
941
+ // Resolve conflicts between posts with numeric slugs and date archive queries.
942
+ $query = wp_resolve_numeric_slug_conflicts( $query );
943
+
944
+ // Do the query
945
+ $query = new WP_Query( $query );
946
+
947
+ if ( $query->found_posts == 1 ) {
948
+
949
+ return true;
950
+
951
+ } else {
952
+
953
+ return false;
954
+
955
+ }
956
+
957
+ }
958
+
959
+ }
960
+
961
+ return false;
962
+
963
+ }
964
+
965
+
966
+ /**
967
+ * get the id of the 404 page in the current language if WPML or Polylang is active
968
+ * public since v 11.1.0 to use it in pp_404_get_page_id()
969
+ */
970
+ public function get_page_id() {
971
+
972
+ $pageid = $this->settings()->get( 'page_id' );
973
+
974
+ if ( $pageid > 0 ) {
975
+
976
+ if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
977
+
978
+ // WPML is active
979
+ $pageid = apply_filters( 'wpml_object_id', $pageid, 'page', true );
980
+
981
+ } elseif ( function_exists( 'pll_get_post' ) ) {
982
+
983
+ // was defined( 'POLYLANG_VERSION' ) before version 11.2.3
984
+
985
+ // Polylang is active
986
+ $translatedpageid = pll_get_post( $pageid );
987
+ if ( !empty( $translatedpageid ) && 'publish' == get_post_status( $translatedpageid ) ) {
988
+ $pageid = $translatedpageid;
989
+ }
990
+
991
+ }
992
+
993
+ }
994
+
995
+ return $pageid;
996
+
997
+ }
998
+
999
+
1000
+ /**
1001
+ * get 404 pages in all available languages
1002
+ * if WPML is active this function returns an array of all page ids in all available languages
1003
+ * otherwise it returns the page id as array
1004
+ * introduced in v 2.4
1005
+ * public since v9 to access it from other classes
1006
+ */
1007
+ public function get_all_page_ids() {
1008
+
1009
+ if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
1010
+
1011
+ // WPML is active
1012
+ // get an array for all translations
1013
+ $pageid = $this->settings()->get( 'page_id' );
1014
+ $pages = array( $pageid );
1015
+
1016
+ if ( $pageid > 0 ) {
1017
+
1018
+ $languages = apply_filters( 'wpml_active_languages', NULL );
1019
+
1020
+ if ( !empty( $languages ) ) {
1021
+
1022
+ foreach( $languages as $l ) {
1023
+
1024
+ $p = apply_filters( 'wpml_object_id', $pageid, 'page', false, $l['language_code'] );
1025
+
1026
+ if ( $p ) {
1027
+
1028
+ $pages[] = $p;
1029
+
1030
+ }
1031
+
1032
+ }
1033
+
1034
+ }
1035
+
1036
+ }
1037
+
1038
+ $pageids = array_unique( $pages, SORT_NUMERIC );
1039
+
1040
+ } else {
1041
+
1042
+ $pageids = array( $this->settings()->get( 'page_id' ) );
1043
+
1044
+ }
1045
+
1046
+ return $pageids;
1047
+
1048
+ }
1049
+
1050
+
1051
+ /**
1052
+ * fire 404page_after_404 hook to make plugin expandable
1053
+ */
1054
+ function do_404page_action() {
1055
+
1056
+ do_action( '404page_after_404' );
1057
+
1058
+ }
1059
+
1060
+
1061
+ /**
1062
+ * uninstall plugin
1063
+ */
1064
+ function uninstall() {
1065
+
1066
+ if( is_multisite() ) {
1067
+
1068
+ $this->uninstall_network();
1069
+
1070
+ } else {
1071
+
1072
+ $this->uninstall_single();
1073
+
1074
+ }
1075
+
1076
+ }
1077
+
1078
+
1079
+ /**
1080
+ * uninstall network wide
1081
+ */
1082
+ function uninstall_network() {
1083
+
1084
+ global $wpdb;
1085
+ $activeblog = $wpdb->blogid;
1086
+ $blogids = $wpdb->get_col( esc_sql( 'SELECT blog_id FROM ' . $wpdb->blogs ) );
1087
+
1088
+ foreach ( $blogids as $blogid ) {
1089
+
1090
+ switch_to_blog( $blogid );
1091
+ $this->uninstall_single();
1092
+
1093
+ }
1094
+
1095
+ switch_to_blog( $activeblog );
1096
+
1097
+ }
1098
+
1099
+
1100
+ /**
1101
+ * uninstall for a single blog
1102
+ */
1103
+ function uninstall_single() {
1104
+
1105
+ $this->data_remove();
1106
+ $this->settings()->remove();
1107
+
1108
+ }
1109
+
1110
+
1111
+ /**
1112
+ * functions for theme usage
1113
+ */
1114
+
1115
+ // check if there's a custom 404 page set
1116
+ function pp_404_is_active() {
1117
+
1118
+ return ( $this->settings()->get( 'page_id' ) > 0 );
1119
+
1120
+ }
1121
+
1122
+ // activate the native theme support
1123
+ function pp_404_set_native_support() {
1124
+
1125
+ $this->set_native();
1126
+
1127
+ }
1128
+
1129
+ // get the title - native theme support
1130
+ function pp_404_get_the_title() {
1131
+
1132
+ $title = '';
1133
+
1134
+ if ( $this->settings()->get( 'page_id' ) > 0 && $this->is_native() ) {
1135
+
1136
+ $title = get_the_title( $this->get_page_id() );
1137
+
1138
+ }
1139
+
1140
+ return $title;
1141
+
1142
+ }
1143
+
1144
+ // print title - native theme support
1145
+ function pp_404_the_title() {
1146
+
1147
+ echo esc_html( $this->pp_404_get_the_title() );
1148
+
1149
+ }
1150
+
1151
+ // get the content - native theme support
1152
+ function pp_404_get_the_content() {
1153
+
1154
+ $content = '';
1155
+
1156
+ if ( $this->settings()->get( 'page_id' ) > 0 && $this->is_native() ) {
1157
+
1158
+ $content = apply_filters( 'the_content', get_post_field( 'post_content', $this->get_page_id() ) );
1159
+
1160
+ }
1161
+
1162
+ return $content;
1163
+
1164
+ }
1165
+
1166
+ // print content - native theme support
1167
+ function pp_404_the_content() {
1168
+
1169
+ echo esc_html( $this->pp_404_get_the_content() );
1170
+
1171
+ }
1172
+
1173
+
1174
+ /**
1175
+ * set native mode
1176
+ *
1177
+ * @since 11.0.0 - was part of previous settings class
1178
+ * @access public
1179
+ */
1180
+ public function set_native() {
1181
+
1182
+ $this->native = true;
1183
+
1184
+ }
1185
+
1186
+
1187
+ /**
1188
+ * is native mode ative
1189
+ *
1190
+ * @since 11.0.0 - was part of previous settings class
1191
+ * @access public
1192
+ */
1193
+ public function is_native() {
1194
+
1195
+ return ( true === $this->native );
1196
+
1197
+ }
1198
+
1199
+
1200
+ /**
1201
+ * get settings class
1202
+ *
1203
+ * @since 11.3.0
1204
+ * @access public
1205
+ * @return object
1206
+ */
1207
+ public function admin() {
1208
+
1209
+ return $this->admin;
1210
+ }
1211
+
1212
+
1213
+
1214
+ /**
1215
+ * save URL that caused the 404 error
1216
+ *
1217
+ * @since 11.4.0
1218
+ * @access private
1219
+ */
1220
+ private function set_404_url() {
1221
+
1222
+ $url = rawurldecode( ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1223
+
1224
+ $this->error_url = parse_url( $url );
1225
+ $this->error_url['full'] = $url;
1226
+
1227
+ return true;
1228
+
1229
+ }
1230
+
1231
+
1232
+ /**
1233
+ * get URL that caused the 404 error
1234
+ *
1235
+ * @since 11.4.0
1236
+ * @access public
1237
+ * @param string $what - array index to get
1238
+ * @return string
1239
+ */
1240
+ public function get_404_url( $what = 'full' ) {
1241
+
1242
+ if ( $this->error_url && is_array( $this->error_url ) && isset( $this->error_url[$what] ) && ! empty( $this->error_url[$what] ) ) {
1243
+
1244
+ return $this->error_url[$what];
1245
+
1246
+ }
1247
+
1248
+ }
1249
+
1250
+ }
1251
+
1252
+ }
1253
+
1254
  ?>
loader.php CHANGED
@@ -32,6 +32,20 @@ require_once( plugin_dir_path( __FILE__ ) . '/inc/class-404page.php' );
32
  require_once( plugin_dir_path( __FILE__ ) . '/functions.php' );
33
 
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  /**
36
  * Main Function
37
  */
@@ -42,7 +56,7 @@ function pp_404page() {
42
  'slug' => pathinfo( dirname( __FILE__ ) . '/404page.php', PATHINFO_FILENAME ),
43
  'name' => '404page - your smart custom 404 error page',
44
  'shortname' => '404page',
45
- 'version' => '11.3.1'
46
  ) );
47
 
48
  }
32
  require_once( plugin_dir_path( __FILE__ ) . '/functions.php' );
33
 
34
 
35
+ /**
36
+ * Load Plugin Shortcodes
37
+ * @since 11.4.0
38
+ */
39
+ require_once( plugin_dir_path( __FILE__ ) . '/shortcodes.php' );
40
+
41
+
42
+ /**
43
+ * Load Plugin Block
44
+ * @since 11.4.0
45
+ */
46
+ require_once( plugin_dir_path( __FILE__ ) . '/block.php' );
47
+
48
+
49
  /**
50
  * Main Function
51
  */
56
  'slug' => pathinfo( dirname( __FILE__ ) . '/404page.php', PATHINFO_FILENAME ),
57
  'name' => '404page - your smart custom 404 error page',
58
  'shortname' => '404page',
59
+ 'version' => '11.4.0'
60
  ) );
61
 
62
  }
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: petersplugins
3
  Tags: page, 404, error, error page, 404 page, page not found, page not found error, 404 error page, missing, broken link, template, 404 link, seo, custom 404, custom 404 page, custom 404 error, custom 404 error page, customize 404, customize 404 page, customize 404 error page, classicpress
4
  Requires at least: 4.0
5
- Tested up to: 5.9
6
- Stable tag: 11.3.1
7
  Requires PHP: 5.4
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -102,6 +102,22 @@ The only requirement for this plugin is that you change the Permalink Structure
102
  * [WP Super Cache](https://wordpress.org/plugins/wp-super-cache/) (Read more about [WP Super Cache Compatibility](http://petersplugins.com/docs/404page/#wp_super_cache))
103
  * [WPML WordPress Multilingual Plugin](https://wpml.org/) ([officially approved by WPML team](https://wpml.org/plugin/404page/))
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  == For developers ==
106
 
107
  = Action Hook =
@@ -116,6 +132,11 @@ The Plugin provides the following functions:
116
  * pp_404_is_active() to check if there is a custom 404 page selected and the selected page exists
117
  * pp_404_get_page_id() to get the ID of the 404 page
118
  * pp_404_get_all_page_ids() to get an array of page IDs in all languages
 
 
 
 
 
119
 
120
  [Read more](http://petersplugins.com/docs/404page/#functions)
121
 
@@ -152,6 +173,8 @@ This plugin is compatible with [ClassicPress](https://www.classicpress.net/).
152
  3. Select the created page as 404 error page
153
  4. The custom 404 error page in action
154
  5. The advanced plugin settings
 
 
155
 
156
  == Frequently Asked Questions ==
157
 
@@ -172,6 +195,11 @@ Please use the [Support Forum](https://wordpress.org/support/plugin/404page).
172
 
173
  == Changelog ==
174
 
 
 
 
 
 
175
  = 11.3.1 (2022-04-05) =
176
  * just cosmetics
177
  * Plugin Foundation updated to PPF08
@@ -369,9 +397,12 @@ Please use the [Support Forum](https://wordpress.org/support/plugin/404page).
369
 
370
  == Upgrade Notice ==
371
 
 
 
 
372
  = 11.3.1 =
 
373
 
374
- * internal improvements without functional changes
375
  = 11.3.0 =
376
  new option to always send an 410 instead of an 404
377
 
2
  Contributors: petersplugins
3
  Tags: page, 404, error, error page, 404 page, page not found, page not found error, 404 error page, missing, broken link, template, 404 link, seo, custom 404, custom 404 page, custom 404 error, custom 404 error page, customize 404, customize 404 page, customize 404 error page, classicpress
4
  Requires at least: 4.0
5
+ Tested up to: 6.0
6
+ Stable tag: 11.4.0
7
  Requires PHP: 5.4
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
102
  * [WP Super Cache](https://wordpress.org/plugins/wp-super-cache/) (Read more about [WP Super Cache Compatibility](http://petersplugins.com/docs/404page/#wp_super_cache))
103
  * [WPML WordPress Multilingual Plugin](https://wpml.org/) ([officially approved by WPML team](https://wpml.org/plugin/404page/))
104
 
105
+ == Block & Shortcode ==
106
+
107
+ = Block =
108
+ The Plugin offers a block "URL causing 404 error" for the block-based editor to show the URL that caused the error. The block offers three display options:
109
+
110
+ * "Page" to show the page including path ( e.g. does/not/exist )
111
+ * "Domain Path" to show the URL without protocol and parameters ( e.g. example.com/does/not/exist )
112
+ * "Full" to show the complete URL ( e.g. https://example.com/does/not/exist?p=1 )
113
+
114
+ = Shortcode =
115
+ The Plugin offers a shortcode "pp_404_url" for the classic editor to show the URL that caused the error. There are three possible options:
116
+
117
+ * [pp_404_url page] to show the page including path ( e.g. does/not/exist )
118
+ * [pp_404_url domainpath] to show the URL without protocol and parameters ( e.g. example.com/does/not/exist )
119
+ * [pp_404_url] or [pp_404_url full] to show the complete URL ( e.g. https://example.com/does/not/exist?p=1 )
120
+
121
  == For developers ==
122
 
123
  = Action Hook =
132
  * pp_404_is_active() to check if there is a custom 404 page selected and the selected page exists
133
  * pp_404_get_page_id() to get the ID of the 404 page
134
  * pp_404_get_all_page_ids() to get an array of page IDs in all languages
135
+ * pp_404_get_the_url( $type ) to get the URL that caused the 404 error
136
+ * Parameter $type string Optional
137
+ * "page" to get the page including path ( e.g. does/not/exist )
138
+ * "domainpath" to get the URL without protocol and parameters ( e.g. example.com/does/not/exist )
139
+ * "full" (default) to get the complete URL ( e.g. https://example.com/does/not/exist?p=1 )
140
 
141
  [Read more](http://petersplugins.com/docs/404page/#functions)
142
 
173
  3. Select the created page as 404 error page
174
  4. The custom 404 error page in action
175
  5. The advanced plugin settings
176
+ 6. The block for the block-based editor
177
+ 7. The block options
178
 
179
  == Frequently Asked Questions ==
180
 
195
 
196
  == Changelog ==
197
 
198
+ = 11.4.0 (2022-10-13) =
199
+ * Block added
200
+ * Shortcode added
201
+ * Function pp_404_get_the_url() added
202
+
203
  = 11.3.1 (2022-04-05) =
204
  * just cosmetics
205
  * Plugin Foundation updated to PPF08
397
 
398
  == Upgrade Notice ==
399
 
400
+ = 11.4.0 =
401
+ new block, new shortcode, new function
402
+
403
  = 11.3.1 =
404
+ internal improvements without functional changes
405
 
 
406
  = 11.3.0 =
407
  new option to always send an 410 instead of an 404
408
 
shortcodes.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The 404page Plugin Shortcodes
5
+ *
6
+ * @since 11.4.0
7
+ *
8
+ **/
9
+
10
+ // If this file is called directly, abort
11
+ if ( ! defined( 'WPINC' ) ) {
12
+ die;
13
+ }
14
+
15
+
16
+ // shortcode to show the url that caused the 404 error
17
+ add_shortcode( 'pp_404_url', 'pp_404_get_the_url' );
18
+
19
+ ?>