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

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