PluginProbe
Hum / 1.2.2
Hum v1.2.2
trunk 1.0 1.1 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6
hum / hum.php

hum.php in Hum 1.2.2, at hum.php

473 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Hum
4 * Plugin URI: https://github.com/willnorris/wordpress-hum
5 * Description: Personal URL shortener for WordPress
6 * Author: Will Norris
7 * Author URI: https://willnorris.com/
8 * Version: 1.2.2
9 * License: MIT
10 * License URI: http://opensource.org/licenses/MIT
11 * Text Domain: hum
12 */
13
14 class Hum {
15
16 public function __construct() {
17 add_action( 'init', array( $this, 'init' ) );
18
19 register_activation_hook( __FILE__, 'flush_rewrite_rules' );
20 register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
21 }
22
23 /**
24 * Initialize the plugin, registering WordPress hooks.
25 */
26 public function init() {
27 load_plugin_textdomain( 'hum', null, basename( dirname( __FILE__ ) ) );
28
29 // if you have hum installed, then you probably actually care about short
30 // links, so we'll add it to the admin menu bar.
31 add_action( 'admin_bar_menu', 'wp_admin_bar_shortlink_menu', 90 );
32
33 add_action( 'query_vars', array( $this, 'query_vars' ) );
34 add_action( 'parse_request', array( $this, 'parse_request' ) );
35 add_filter( 'hum_redirect', array( $this, 'redirect_request' ), 10, 3 );
36 add_filter( 'hum_redirect_i', array( $this, 'redirect_request_i' ), 10, 2 );
37 add_filter( 'hum_process_redirect', array( $this, 'process_redirect' ), 10, 2 );
38 add_action( 'generate_rewrite_rules', array( $this, 'rewrite_rules' ) );
39 add_filter( 'pre_option_hum_shortlink_base', array( $this, 'config_shortlink_base' ) );
40 add_filter( 'pre_get_shortlink', array( $this, 'get_shortlink' ), 10, 4 );
41 add_filter( 'template_redirect', array( $this, 'legacy_redirect' ) );
42 add_filter( 'hum_legacy_id', array( $this, 'legacy_ftl_id' ), 10, 2 );
43 add_action( 'atom_entry', array( $this, 'shortlink_atom_entry' ) );
44
45 // Admin Settings
46 add_action( 'admin_init', array( $this, 'admin_init' ) );
47 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
48 }
49
50 /**
51 * Accept hum query variables.
52 */
53 public function query_vars( $vars ) {
54 $vars[] = 'hum';
55 return $vars;
56 }
57
58 /**
59 * Parse request for shortlink. This is the main entry point for handling
60 * short URLs.
61 *
62 * @uses apply_filters() Calls 'hum_redirect' filter
63 * @uses apply_filters() Calls 'hum_process_redirect' filter
64 *
65 * @param WP $wp the WordPress environment for the request
66 */
67 public function parse_request( $wp ) {
68 if ( array_key_exists( 'hum', $wp->query_vars ) ) {
69 $hum_path = $wp->query_vars['hum'];
70 if ( strpos( $hum_path, '/' ) !== false ) {
71 list($type, $id) = explode( '/', $hum_path, 2 );
72 } else {
73 $type = $hum_path;
74 $id = null;
75 }
76
77 $url = apply_filters( 'hum_redirect', null, $type, $id );
78
79 // hum hasn't handled the request yet, so try again but strip common
80 // punctuation that might appear after a URL in written text: . , )
81 if ( ! $url ) {
82 $clean_id = preg_replace( '/[\.,\)]+$/', '', $id );
83 if ( $id != $clean_id ) {
84 $url = apply_filters( 'hum_redirect', null, $type, $clean_id );
85 }
86 }
87
88 if ( $url ) {
89 do_action( 'hum_process_redirect', $url, $id );
90 }
91
92 // hum didn't handle request, so issue 404.
93 // manually setting query vars like this feels very fragile, but
94 // $wp_query->set_404() doesn't do what we need here.
95 $wp->query_vars['error'] = '404';
96 }
97 }
98
99 /**
100 * Process the redirect.
101 *
102 * @param string $url the permalink of the post
103 * @param string $id the requested post ID
104 */
105 public function process_redirect( $url, $id ) {
106 wp_redirect( $url, 301 );
107 exit;
108 }
109
110 /**
111 * Get the short URL types that are handled locally by WordPress.
112 *
113 * @uses apply_filters() Calls 'hum_local_types' with array of local types
114 *
115 * @return array local types
116 */
117 public function local_types() {
118 $local_types = array( 'b', 't', 'a', 'p' );
119 return apply_filters( 'hum_local_types', $local_types );
120 }
121
122 /**
123 * Attempt to handle redirect for the current shortlink.
124 *
125 * This redirects shortlinks that are for content hosted directly within
126 * WordPress. The 'id' portion of these URLs is expected to be the
127 * sexagesimal post ID.
128 *
129 * This also allows for simple redirect rules for shortlink prefixes. Users
130 * can provide a filter to perform simple URL redirect for a given type
131 * prefix. For example, to redirect all /w/ shortlinks to your personal
132 * PBworks wiki, you could use:
133 *
134 * add_filter('hum_redirect_base_w',
135 * create_function('', 'return "http://willnorris.pbworks.com/";'));
136 *
137 * @uses apply_filters() Calls 'hum_redirect_{$type}' action
138 * @uses apply_filters() Calls 'hum_redirect_base_{$type}' filter on redirect base URL
139 *
140 * @param string $url the short URL
141 * @param string $type the content-type prefix
142 * @param string $id the requested post ID
143 */
144 public function redirect_request( $url, $type, $id ) {
145 // locally hosted content
146 $local_types = $this->local_types();
147 if ( in_array( $type, $local_types ) ) {
148 $p = sxg_to_num( $id );
149 if ( $p ) {
150 $url = get_permalink( $p );
151 }
152 }
153
154 // simple redirects for entire base type
155 if ( ! $url ) {
156 $url = apply_filters( "hum_redirect_base_{$type}", false );
157 if ( $url ) {
158 $url = trailingslashit( $url ) . $id;
159 }
160 }
161
162 $url = apply_filters( "hum_redirect_{$type}", $url, $id );
163 return $url;
164 }
165
166 /**
167 * Handles /i/ URLs that have ISBN or ASIN subpaths by redirecting to Amazon.
168 *
169 * @uses apply_filters() Calls 'hum_redirect_i_{$subtype}' action
170 * @uses apply_filters() Calls 'amazon_domain' filter
171 * @uses apply_filters() Calls 'amazon_affiliate_id' filter
172 *
173 * @param string $url the short URL
174 * @param string $path subpath of URL (after /i/)
175 */
176 public function redirect_request_i( $url, $path ) {
177 list( $subtype, $id ) = explode( '/', $path, 2 );
178
179 if ( $subtype ) {
180 switch ( $subtype ) {
181 case 'a':
182 case 'asin':
183 case 'i':
184 case 'isbn':
185 $amazon_domain = apply_filters( 'amazon_domain', 'www.amazon.com' );
186 $amazon_id = apply_filters( 'amazon_affiliate_id', false );
187 if ( $amazon_id ) {
188 // valid partner shortlink, checked by
189 // https://partnernet.amazon.de/gp/associates/network/tools/link-checker/main.html
190 $url = 'http://' . $amazon_domain . '/dp/product/' . $id . '?tag=' . $amazon_id;
191 } else {
192 $url = 'http://' . $amazon_domain . '/dp/product/' . $id;
193 }
194 break;
195 }
196 $url = apply_filters( "hum_redirect_i_{$subtype}", $url, $id );
197 }
198 return $url;
199 }
200
201 /**
202 * Add rewrite rules for hum shortlinks.
203 *
204 * @param WP_Rewrite $wp_rewrite WordPress rewrite component.
205 */
206 public function rewrite_rules( $wp_rewrite ) {
207 $hum_rules = array(
208 '([a-z](/.*)?$)' => 'index.php?hum=$matches[1]',
209 );
210
211 $wp_rewrite->rules = $hum_rules + $wp_rewrite->rules;
212 }
213
214 /**
215 * Get the base URL for hum shortlinks. Defaults to the WordPress home url.
216 * Users can define HUM_SHORTLINK_BASE or provide a filter to use a custom
217 * domain for shortlinks.
218 *
219 * @uses apply_filters() Calls 'hum_shortlink_base' filter on base URL
220 *
221 * @return string
222 */
223 public function shortlink_base() {
224 $base = get_option( 'hum_shortlink_base' );
225 if ( empty( $base ) ) {
226 $base = home_url();
227 }
228 return apply_filters( 'hum_shortlink_base', $base );
229 }
230
231 /**
232 * Allow the constant named 'HUM_SHORTLINK_BASE' to override the base URL for shortlinks.
233 *
234 * @param string $url the short URL
235 */
236 public function config_shortlink_base( $url = '' ) {
237 if ( defined( 'HUM_SHORTLINK_BASE' ) ) {
238 return untrailingslashit( HUM_SHORTLINK_BASE );
239 }
240 return $url;
241 }
242
243 /**
244 * Get the shortlink for a post, page, attachment, or blog.
245 *
246 * @param string $link the current shortlink for the post
247 * @param int $id post ID
248 * @param string $context
249 * @param boolean $allow_slugs
250 * @return string
251 */
252 public function get_shortlink( $link, $id, $context, $allow_slugs ) {
253 $post_id = 0;
254 if ( 'query' == $context ) {
255 if ( is_front_page() ) {
256 $link = trailingslashit( $this->shortlink_base() );
257 } elseif ( is_singular() ) {
258 $post_id = get_queried_object_id();
259 }
260 } elseif ( 'post' == $context ) {
261 $post = get_post( $id );
262 $post_id = $post->ID;
263 }
264
265 if ( ! empty( $post_id ) ) {
266 $type = $this->type_prefix( $post_id );
267 $sxg_id = num_to_sxg( $post_id );
268 $link = trailingslashit( $this->shortlink_base() ) . $type . '/' . $sxg_id;
269 }
270
271 return $link;
272 }
273
274 /**
275 * Get the content-type prefix for the specified post.
276 *
277 * @see http://ttk.me/w/Whistle#design
278 * @uses apply_filters() Calls 'hum_type_prefix' on the content type prefix
279 *
280 * @param int|object $post A post
281 * @return string the content type prefix for the post
282 */
283 public function type_prefix( $post ) {
284 $prefix = 'b';
285
286 $post_type = get_post_type( $post );
287
288 if ( 'attachment' == $post_type ) {
289 // check if $post is a WP_Post or an ID
290 if ( is_numeric( $post ) ) {
291 $post_id = $post;
292 } else {
293 $post_id = $post->ID;
294 }
295
296 $mime_type = get_post_mime_type( $post_id );
297 $media_type = preg_replace( '/(\/[a-zA-Z]+)/i', '', $mime_type );
298
299 switch ( $media_type ) {
300 case 'audio':
301 case 'video':
302 $prefix = 'a'; break;
303 case 'image':
304 $prefix = 'p'; break;
305 }
306
307 // @todo add support for slides
308 } else {
309 $post_format = get_post_format( $post );
310 switch ( $post_format ) {
311 case 'aside':
312 case 'status':
313 case 'link':
314 $prefix = 't'; break;
315 case 'audio':
316 case 'video':
317 $prefix = 'a'; break;
318 case 'photo':
319 case 'gallery':
320 case 'image':
321 $prefix = 'p'; break;
322 }
323 }
324
325 return apply_filters( 'hum_type_prefix', $prefix, $post );
326 }
327
328 /**
329 * Support redirects from legacy short URL schemes. This allows users to migrate from other
330 * shortlink generaters, but still have hum support the old URLs.
331 *
332 * @uses do_action() Calls 'hum_legacy_id' with the post ID and shortlink path.
333 */
334 public function legacy_redirect() {
335 if ( is_404() ) {
336 global $wp;
337 $post_id = apply_filters( 'hum_legacy_id', 0, $wp->request );
338 if ( $post_id ) {
339 $url = get_permalink( $post_id );
340 if ( $url ) {
341 $url = apply_filters( 'hum_legacy_redirect', $url );
342 wp_redirect( $url, 301 );
343 exit;
344 }
345 }
346 }
347 }
348
349 /**
350 * Handle shortlinks generated by Friendly Twitter Links, which take the form
351 * /{id}, where {id} can be the base10 or base32 post ID.
352 *
353 * @param int $id post ID to filter on
354 * @param string $path URL path (without preceding slash) of the request
355 *
356 * @return string ID of post to redirect to
357 */
358 public function legacy_ftl_id( $id, $path ) {
359 if ( is_numeric( $path ) ) {
360 $post = get_post( $path );
361 } else {
362 $post_id = base_convert( $path, 32, 10 );
363 $post = get_post( $post_id );
364 }
365
366 if ( $post ) {
367 $id = $post->ID;
368 }
369
370 return $id;
371 }
372
373
374 // Admin Settings
375
376 /**
377 * Register admin settings for Hum.
378 */
379 public function admin_init() {
380 register_setting( 'general', 'hum_shortlink_base' );
381 }
382
383 /**
384 * Add admin settings fields for Hum.
385 */
386 public function admin_menu() {
387 add_settings_field( 'hum_shortlink_base', __( 'Shortlink Base (URL)', 'hum' ), array( $this, 'admin_shortlink_base' ), 'general' );
388 }
389
390 /**
391 * Admin UI for setting the shortlink base URL.
392 */
393 public function admin_shortlink_base() {
394 ?>
395 <input name="hum_shortlink_base" type="text" id="hum_shortlink_base"
396 value="<?php form_option( 'hum_shortlink_base' ); ?>"
397 <?php disabled( defined( 'HUM_SHORTLINK_BASE' ) ); ?>
398 class="regular-text code<?php if ( defined( 'HUM_SHORTLINK_BASE' ) ) { echo ' disabled'; } ?>" />
399 <p class="description">
400 <?php _e( 'If you have a custom domain you want to use for shortlinks, enter the address here.', 'hum' ); ?>
401 </p>
402
403 <script type="text/javascript">
404 // move adjacent to other URL properties
405 jQuery('input#hum_shortlink_base').parents('tr').insertAfter( jQuery('input#home').parents('tr') );
406 </script>
407 <?php
408 }
409
410 /**
411 * Add shortlink <link /> to Atom-Entry.
412 */
413 public function shortlink_atom_entry() {
414 $shortlink = wp_get_shortlink();
415 if ( $shortlink ) {
416 echo "\t\t" . '<link rel="shortlink" href="' . esc_attr( $shortlink ) . '" />' . PHP_EOL;
417 }
418 }
419 }
420
421 new Hum;
422
423
424 // New Base 60 - see http://ttk.me/w/NewBase60
425 //
426 // slightly modified from Cassis Project (http://cassisproject.com/)
427 // Copyright 2010 Tantek Çelik, used with permission under CC0 license (http://git.io/tZ8fjw)
428 //
429 // @codingStandardsIgnoreStart
430 if ( ! function_exists( 'num_to_sxg' ) ) :
431 /**
432 * Convert base-10 number to sexagesimal.
433 */
434 function num_to_sxg($n) {
435 $s = "";
436 $m = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz";
437 if ($n===null || $n===0) { return 0; }
438 while ($n>0) {
439 $d = $n % 60;
440 $s = $m[$d] . $s;
441 $n = ($n-$d)/60;
442 }
443 return $s;
444 }
445 endif;
446
447
448 if ( ! function_exists( 'sxg_to_num' ) ) :
449 /**
450 * Convert sexagesimal to base-10 number.
451 */
452 function sxg_to_num( $s ) {
453 $n = 0;
454 $j = strlen($s);
455 for ($i=0;$i<$j;$i++) { // iterate from first to last char of $s
456 $c = ord($s[$i]); // put current ASCII of char into $c
457 if ($c>=48 && $c<=57) { $c=$c-48; }
458 else if ($c>=65 && $c<=72) { $c-=55; }
459 else if ($c==73 || $c==108) { $c=1; } // typo capital I, lowercase l to 1
460 else if ($c>=74 && $c<=78) { $c-=56; }
461 else if ($c==79) { $c=0; } // error correct typo capital O to 0
462 else if ($c>=80 && $c<=90) { $c-=57; }
463 else if ($c==95) { $c=34; } // underscore
464 else if ($c>=97 && $c<=107) { $c-=62; }
465 else if ($c>=109 && $c<=122) { $c-=63; }
466 else { $c = 0; } // treat all other noise as 0
467 $n = 60*$n + $c;
468 }
469 return $n;
470 }
471 endif;
472 // @codingStandardsIgnoreEnd
473