PluginProbe
Hum / 1.2.7
Hum v1.2.7
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.7, at hum.php

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