PluginProbe
HTTP 410 (Gone) responses / 0.6
HTTP 410 (Gone) responses v0.6
1.2.1 trunk 0.1 0.2 0.3 0.4 0.5 0.6 0.6.1 0.7 0.7.1 0.7.2 0.8.1 0.8.2 1.0.2 1.0.3 1.1.0
wp-410 / wp-410.php

wp-410.php in HTTP 410 (Gone) responses 0.6, at wp-410.php

254 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: 410 for Wordpress
4 Plugin URI: http://rayofsolaris.net/code/410-for-wordpress
5 Description: Sends HTTP 410 (Gone) responses to requests for pages that no longer exist on your blog.
6 Version: 0.6
7 Author: Samir Shah
8 Author URI: http://rayofsolaris.net/
9 License: GPL2
10 */
11
12 if( ! defined( 'ABSPATH' ) )
13 exit;
14
15 class WP_410 {
16 const db_version = 3; // DB version, since 0.2
17 private $permalinks;
18 private $table;
19
20 function __construct() {
21 $this->permalinks = (bool) get_option('permalink_structure');
22 $this->table = $GLOBALS['wpdb']->prefix . '410_links';
23
24 add_action( 'plugins_loaded', array( $this, 'upgrade_check' ) );
25
26 if( is_admin() )
27 add_action( 'admin_menu', array( $this, 'settings_menu' ) );
28
29 add_action('delete_post', array( $this, 'note_deleted_post' ) );
30 add_action('wp_insert_post', array( $this, 'note_inserted_post' ));
31 add_action('template_redirect', array( $this, 'check_for_410' ) );
32 }
33
34 private function install_table() {
35 // remember, two spaces after PRIMARY KEY otherwise WP borks
36 $sql = "CREATE TABLE $this->table (
37 gone_id SMALLINT NOT NULL AUTO_INCREMENT,
38 gone_key VARCHAR(512) NOT NULL,
39 gone_regex VARCHAR(512) NOT NULL,
40 PRIMARY KEY (gone_id)
41 );";
42
43 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
44 dbDelta($sql);
45 }
46
47 private function get_links(){
48 global $wpdb;
49 return $wpdb->get_results( "SELECT gone_key, gone_regex FROM $this->table", OBJECT_K ); // indexed by gone_key
50 }
51
52 private function add_link( $key ){ // just supply the link
53 global $wpdb;
54 // build regex
55 $parts = preg_split( '/(\*)/', $key, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
56 foreach( $parts as &$part ) if( '*' != $part )
57 $part = preg_quote( $part, '|' );
58 $parts = str_replace( '*', '.*', $parts );
59 $regex = '|^' . implode( '', $parts ) . '$|i';
60
61 return $wpdb->insert( $this->table, array( 'gone_key' => $key, 'gone_regex' => $regex ) );
62 }
63
64 private function remove_link( $key ){
65 global $wpdb;
66 return $wpdb->query( $wpdb->prepare( "DELETE FROM $this->table WHERE gone_key = %s", array( $key ) ) );
67 }
68
69 function upgrade_check() {
70 $options_version = get_option( 'wp_410_options_version', 0 );
71
72 if( $options_version == self::db_version ) // nothing to do
73 return;
74
75 $old_links = get_option( 'wp_410_links_list', array() );
76 $new_links = array(); // just a simple array of links
77
78 if( 0 == $options_version ) // links were stored just as links
79 $new_links = array_map( 'rawurldecode', $old_links );
80 elseif( 1 == $options_version ) // links were stored as array( link => regex ), We only need the link
81 $new_links = array_map( 'rawurldecode', array_keys( $old_links ) );
82 else // moved to using the database in db_version 3
83 $new_links = array_keys( $old_links );
84
85 $this->install_table();
86 foreach( $new_links as $link )
87 $this->add_link( $link );
88
89 delete_option( 'wp_410_links_list' ); // remove old option
90 update_option( 'wp_410_options_version', self::db_version );
91 }
92
93 function settings_menu() {
94 add_submenu_page('plugins.php', '410 for WordPress', '410 for WordPress', 'manage_options', 'wp_410_settings', array( $this, 'settings_page') );
95 }
96
97 function settings_page() {
98 $links = $this->get_links();
99 $links_to_add = array();
100
101 if ( isset( $_POST['submit'] ) ) {
102 $failed_to_add = array();
103 if( !empty( $_POST['links_to_add'] ) ) {
104 foreach( preg_split( '/(\r?\n)+/', $_POST['links_to_add'], -1, PREG_SPLIT_NO_EMPTY ) as $k => $link ) {
105 $parts = parse_url( $link );
106 if( !$parts || empty( $parts['scheme'] ) || empty( $parts['host'] ) ) // invalid URL
107 $failed_to_add[] = '<code>' . htmlspecialchars( $link ) . '</code>';
108 else {
109 $this->add_link( $link );
110 $links[$link] = $link; // the value doesn't matter, we don't use it here
111 }
112 }
113 }
114
115 if( !empty( $_POST['old_links_to_remove'] ) ) {
116 foreach( $_POST['old_links_to_remove'] as $key ) {
117 if( isset( $links[$key] ) ) {
118 $this->remove_link( $key );
119 unset( $links[$key] );
120 }
121 }
122 }
123
124 ksort( $links );
125
126 echo '<div class="updated fade"><p>Options updated.</p></div>';
127
128 if( $failed_to_add )
129 echo '<div class="error"><p>The following entries could not be recognised as valid URLs, and were not added to the list.<br />- ' . implode( '<br />- ', $failed_to_add ) .'</p></div>';
130 }
131 ?>
132 <style>
133 .indent {padding-left: 2em}
134 .wp_gone_deleted label {text-decoration: line-through}
135 </style>
136 <div class="wrap">
137 <h2>410 for WordPress</h2>
138 <p>This plugin will issue a HTTP 410 response to articles that no longer exist on your blog. When an article is deleted, it records this and issues a 410 response when pages unique to that article are requested. This HTTP code informs robots visiting your site that the requested page has been permanently removed, and that they should stop trying to access it.</p>
139 <p><strong>Note: this plugin will only return a 410 response if Wordpress has not found something valid to display for the requested URL.</strong></p>
140 <form action="" method="post" id="wp-gone-captcha-settings">
141 <table class="form-table"><tbody>
142 <tr><th>Obsolete URLs</th><td>
143 <?php
144 if( empty( $links ) ) {
145 echo '<p>There are currently no obsolete URLs in this list. You can add some manually below.</p>';
146 }
147 else {
148 echo '<p>The following URLs are currently considered obsolete. To remove items from the list, check the corresponding box.</p>';
149 echo '<ul id="wp_gone_old_links" class="indent">';
150 foreach( array_keys( $links ) as $k ) {
151 $k = htmlspecialchars( $k );
152 echo "<li><input type='checkbox' name='old_links_to_remove[]' value='$k' id='old_link_$k' /> <label for='old_link_$k'><code>$k</code></label></li>";
153 }
154 echo '</ul>';
155 }
156 ?>
157 <p><small>If you create or update an article whose URL matches one of those above, that URL will automatically be removed from this list.</small></p>
158 </td></tr>
159 <tr><th>Manually add URLs</th><td>
160 <p>You can manually add items to the list by entering them below. Please enter one <strong>fully qualified</strong> URL per line.</p>
161 <p>Use <code>*</code> as a wildcard character. So <code>http://www.example.com/*/music/</code> will match all URLs ending in <code>/music/</code>.</p>
162 <textarea name="links_to_add" rows="8" cols="80"></textarea>
163 </td></tr>
164 <tr><th>410 reponse message</th><td>
165 <p>By default, the plugin issues the following plain-text message as part of the 410 response: <code>Sorry, the page you requested has been permanently removed.</code></p>
166 <?php
167 if( locate_template( '410.php' ) )
168 echo '<p><strong>A template file <code>410.php</code> has been detected in your theme directory. This file will be used to display 410 responses.</strong> To revert back to the default message, remove the file from your theme directory.</p>';
169 else
170 echo '<p>If you would like to use your own template instead, simply place a file called <code>410.php</code> in your theme directory, containing your template.</p>';
171 ?>
172 </td></tr>
173 </tbody></table>
174 <p class="submit"><input class="button-primary" type="submit" name="submit" value="Update settings" /></p>
175 </form>
176 </div>
177 <script>
178 jQuery(document).ready(function(){
179 jQuery('#wp_gone_old_links input').change( function(){
180 jQuery(this).parent().toggleClass('wp_gone_deleted', jQuery(this).is(':checked'));
181 });
182 });
183 </script>
184 <?php
185 }
186
187 function note_deleted_post( $id ) {
188 // NOTE: function wp_delete_post calls the delete_post action twice, so we store deleted IDs in an array to avoid repeating
189 // but still allow repeated use of the function for batch deletes
190 static $noted = array();
191 $id = (int) $id;
192
193 if( in_array( $id, $noted ) )
194 return;
195 $noted[] = $id;
196
197 $post = get_post( $id );
198
199 if( 'revision' == $post->post_type )
200 return;
201
202 $link = rawurldecode( get_permalink( $id ) );
203 if( $this->permalinks )
204 $link .= '*'; // catch everything like /page/ and /feed/
205
206 $this->add_link( $link );
207 }
208
209 function note_inserted_post( $id ) {
210 $post = get_post( $id );
211
212 if( 'revision' == $post->post_type || 'draft' == $post->post_status )
213 return;
214
215 // Check our list of URLs against the new/updated post's permalink, and if they match, scratch it from our list
216 $created_links = array();
217
218 $created_links[] = rawurldecode( get_permalink( $id ) );
219 $created_links[] = get_post_comments_feed_link( $id ); // back compat
220
221 if( $this->permalinks )
222 $created_links[0] .= '*';
223
224 $list = get_option( 'wp_410_links_list', array() );
225
226 foreach( $created_links as $link )
227 $this->remove_link( $link );
228 }
229
230 function check_for_410() {
231 // Don't mess if Wordpress has found something to display
232 if( !is_404() )
233 return;
234
235 $links = $this->get_links();
236 $req = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
237 $req = rawurldecode( $req );
238
239 foreach( $links as $link ) {
240 if( @preg_match( $link->gone_regex, $req ) ) {
241 status_header( 410 );
242 do_action( 'wp_410_response' ); // you can use this to customise the response message
243
244 if( ! locate_template( '410.php', true ) )
245 echo 'Sorry, the page you requested has been permanently removed.';
246
247 exit;
248 }
249 }
250 }
251 }
252
253 new WP_410();
254