PluginProbe
Hum / 1.2.1
Hum v1.2.1
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.1, at hum.php

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