Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 814 lines (673 sloc) 24.817 kb
8ff4206 Mohammad Jangda v1
mjangda authored
1 <?php
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
2
8ff4206 Mohammad Jangda v1
mjangda authored
3 /**
4 * Plugin Name: Liveblog
1fc681a Nikolay Bachiyski Add metadata URLs
nb authored
5 * Plugin URI: http://wordpress.org/extend/plugins/liveblog/
8ff4206 Mohammad Jangda v1
mjangda authored
6 * Description: Blogging: at the speed of live.
1c424e2 Mohammad Jangda Bump version to 1.1
mjangda authored
7 * Version: 1.1
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
8 * Author: WordPress.com VIP, Automattic
1fc681a Nikolay Bachiyski Add metadata URLs
nb authored
9 * Author URI: http://vip.wordpress.com/
10 * Text Domain: liveblog
8ff4206 Mohammad Jangda v1
mjangda authored
11 */
12
13 if ( ! class_exists( 'WPCOM_Liveblog' ) ) :
14
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
15 /**
16 * The main Liveblog class used to setup everything this plugin needs.
17 *
18 * Liveblog currently uses a custom comment-type to circumvent post cache
19 * issues frequently experienced by other live-blog implimentations. It comes
20 * with a simple and effective templating mechanism, complete with all of the
21 * CSS, JS, and AJAX needed to make this a turn-key installation.
22 *
23 * This class is a big container for a bunch of static methods, similar to a
24 * factory but without object inheritance or instantiation.
8413aff John James Jacoby Uploading:
johnjamesjacoby authored
25 *
5ad84ba Nikolay Bachiyski Fix typo
nb authored
26 * Things yet to be implemented:
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
27 *
28 * -- Change "Read More" to "View Liveblog"
29 * -- Manual refresh button
30 * -- Allow marking of liveblog as ended
31 * -- Allow comment modifications; need to store modified date as comment_meta
32 */
33 final class WPCOM_Liveblog {
34
35 /** Constants *************************************************************/
8ff4206 Mohammad Jangda v1
mjangda authored
36
1c424e2 Mohammad Jangda Bump version to 1.1
mjangda authored
37 const version = '1.1';
1d5d7f9 Nikolay Bachiyski Flush the rewrite rules on rules update
nb authored
38 const rewrites_version = 1;
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
39 const key = 'liveblog';
40 const url_endpoint = 'liveblog';
41 const edit_cap = 'publish_posts';
42 const nonce_key = 'liveblog_nonce';
8ff4206 Mohammad Jangda v1
mjangda authored
43
ed1991c Nikolay Bachiyski Shut the auto-update only on consecutive failures
nb authored
44 const refresh_interval = 3; // how often should we refresh
45 const max_consecutive_retries = 100; // max number of failed tries before polling is disabled
46 const delay_threshold = 5; // how many failed tries after which we should increase the refresh interval
47 const delay_multiplier = 2; // by how much should we inscrease the refresh interval
8ff4206 Mohammad Jangda v1
mjangda authored
48
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
49 /** Variables *************************************************************/
50
51 private static $post_id = null;
52 private static $entry_query = null;
53 private static $do_not_cache_response = false;
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
54
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
55 /** Load Methods **********************************************************/
56
57 /**
58 * @uses add_action() to hook methods into WordPress actions
59 * @uses add_filter() to hook methods into WordPress filters
60 */
61 public static function load() {
e5351e5 Mohammad Jangda load_plugin_textdomain so the plugin can be translated.
mjangda authored
62 load_plugin_textdomain( 'liveblog', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
63
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
64 self::includes();
65 self::add_actions();
66 self::add_filters();
67 self::add_admin_actions();
68 }
69
70 /**
71 * Include the necessary files
72 */
73 private static function includes() {
74 require( dirname( __FILE__ ) . '/classes/class-wpcom-liveblog-entry.php' );
75 require( dirname( __FILE__ ) . '/classes/class-wpcom-liveblog-entry-query.php' );
e691fe3 John James Jacoby Include ms.php theme-side for its filesize and available space functions...
johnjamesjacoby authored
76
77 // Manually include ms.php theme-side in multisite environments because
78 // we need its filesize and available space functions.
79 if ( ! is_admin() && is_multisite() ) {
8f5fb3c John James Jacoby Remove preceding slash. ABSPATH is already slashed for us.
johnjamesjacoby authored
80 require_once( ABSPATH . 'wp-admin/includes/ms.php' );
e691fe3 John James Jacoby Include ms.php theme-side for its filesize and available space functions...
johnjamesjacoby authored
81 }
02b5fdb Nikolay Bachiyski Add convertor from extend read me to github readme
nb authored
82
83 if ( defined( 'WP_CLI' ) && WP_CLI ) {
84 require( dirname( __FILE__ ) . '/classes/class-wpcom-liveblog-wp-cli.php' );
85 }
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
86 }
8ff4206 Mohammad Jangda v1
mjangda authored
87
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
88 /**
89 * Hook actions in that run on every page-load
90 *
91 * @uses add_action()
92 */
93 private static function add_actions() {
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
94 add_action( 'init', array( __CLASS__, 'init' ) );
7994c3e Nikolay Bachiyski Re-add the rewrite rules on permalink change
nb authored
95 add_action( 'init', array( __CLASS__, 'add_rewrite_rules' ) );
96 add_action( 'permalink_structure_changed', array( __CLASS__, 'add_rewrite_rules' ) );
4221823 Nikolay Bachiyski Flush the rewrite rules later
nb authored
97 // flush the rewrite rules a lot later so that we don't interfere with other plugins using rewrite rules
98 add_action( 'init', array( __CLASS__, 'flush_rewrite_rules' ), 1000 );
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
99 add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
8ff4206 Mohammad Jangda v1
mjangda authored
100 add_action( 'wp_ajax_liveblog_insert_entry', array( __CLASS__, 'ajax_insert_entry' ) );
5aee585 Nikolay Bachiyski Add ajax endpoint for preview
nb authored
101 add_action( 'wp_ajax_liveblog_preview_entry', array( __CLASS__, 'ajax_preview_entry' ) );
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
102 }
8ff4206 Mohammad Jangda v1
mjangda authored
103
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
104 /**
105 * Hook filters in that run on every page-load
106 *
107 * @uses add_filter()
108 */
109 private static function add_filters() {
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
110 add_filter( 'template_redirect', array( __CLASS__, 'handle_request' ) );
111 add_filter( 'comment_class', array( __CLASS__, 'add_comment_class' ) );
8ff4206 Mohammad Jangda v1
mjangda authored
112 }
113
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
114 /**
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
115 * Hook actions in that run on every admin page-load
116 *
117 * @uses add_action()
118 * @uses is_admin()
119 */
120 private static function add_admin_actions() {
121
122 // Bail if not in admin area
123 if ( ! is_admin() )
124 return;
125
126 add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_box' ) );
127 add_action( 'save_post', array( __CLASS__, 'save_meta_box' ) );
128 }
129
130 /** Public Methods ********************************************************/
131
132 /**
133 * Liveblog initialization functions.
134 *
135 * This is where Liveblog sets up any additional things it needs to run
136 * inside of WordPress. Where some plugins would register post types or
137 * taxonomies, we modify endpoints and add post type support for Liveblog.
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
138 */
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
139 public static function init() {
140 /**
141 * Add liveblog support to the 'post' post type. This is done here so
142 * we can possibly introduce this to other post types later.
143 */
8ff4206 Mohammad Jangda v1
mjangda authored
144 add_post_type_support( 'post', self::key );
145 }
146
7994c3e Nikolay Bachiyski Re-add the rewrite rules on permalink change
nb authored
147 public function add_rewrite_rules() {
1d5d7f9 Nikolay Bachiyski Flush the rewrite rules on rules update
nb authored
148 add_rewrite_endpoint( self::url_endpoint, EP_PERMALINK );
7994c3e Nikolay Bachiyski Re-add the rewrite rules on permalink change
nb authored
149 }
1d5d7f9 Nikolay Bachiyski Flush the rewrite rules on rules update
nb authored
150
7994c3e Nikolay Bachiyski Re-add the rewrite rules on permalink change
nb authored
151 public static function flush_rewrite_rules() {
1d5d7f9 Nikolay Bachiyski Flush the rewrite rules on rules update
nb authored
152 if ( get_option( 'liveblog_rewrites_version' ) != self::rewrites_version ) {
153 flush_rewrite_rules();
154 update_option( 'liveblog_rewrites_version', self::rewrites_version );
155 }
156 }
157
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
158 /**
159 * This is where a majority of the magic happens.
160 *
161 * Hooked to template_redirect, this method tries to add anything it can to
162 * the current post output. If nothing needs to be added, we redirect back
163 * to the permalink.
164 *
165 * @return If request has been handled
166 */
167 public static function handle_request() {
168
8ff4206 Mohammad Jangda v1
mjangda authored
169 if ( ! self::is_viewing_liveblog_post() )
170 return;
171
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
172 self::$post_id = get_the_ID();
b28b38d Nikolay Bachiyski Rename the query class to match WP conventions
nb authored
173 self::$entry_query = new WPCOM_Liveblog_Entry_Query( self::$post_id, self::key );
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
174
4872b2a Nikolay Bachiyski Use front-end AJAX URLs for same-origin requests
nb authored
175 if ( self::is_initial_page_request() )
8ff4206 Mohammad Jangda v1
mjangda authored
176 add_filter( 'the_content', array( __CLASS__, 'add_liveblog_to_content' ) );
4872b2a Nikolay Bachiyski Use front-end AJAX URLs for same-origin requests
nb authored
177 else
178 self::handle_ajax_request();
179 }
180
181 private static function handle_ajax_request() {
182
183 $endpoint_suffix = get_query_var( self::url_endpoint );
184
185 if ( !$endpoint_suffix ) {
186 // we redirect, because if somebody accessed <permalink>/liveblog
187 // they probably did that in the URL bar, not via AJAX
188 wp_safe_redirect( get_permalink() );
189 exit();
8ff4206 Mohammad Jangda v1
mjangda authored
190 }
2b0244c Nikolay Bachiyski Remove trailing whitespace
nb authored
191 wp_cache_delete( self::key . '_entries_asc_' . self::$post_id, 'liveblog' );
8ff4206 Mohammad Jangda v1
mjangda authored
192
4872b2a Nikolay Bachiyski Use front-end AJAX URLs for same-origin requests
nb authored
193 $suffix_to_method = array(
194 '\d+/\d+' => 'ajax_entries_between',
195 'insert' => 'ajax_insert_entry',
196 'preview' => 'ajax_preview_entry',
197 );
198
199 $response_method = 'ajax_unknown';
200
201 foreach( $suffix_to_method as $suffix_re => $method ) {
202 if ( preg_match( "%^$suffix_re/?%", $endpoint_suffix ) ) {
203 $response_method = $method;
204 break;
205 }
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
206 }
207
4872b2a Nikolay Bachiyski Use front-end AJAX URLs for same-origin requests
nb authored
208 self::$response_method();
209
8ff4206 Mohammad Jangda v1
mjangda authored
210 }
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
211
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
212 /**
213 * Look for any new Liveblog entries, and return them via JSON
214 */
215 public static function ajax_entries_between() {
216
217 // Set some defaults
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
218 $latest_timestamp = 0;
219 $entries_for_json = array();
558008c Nikolay Bachiyski Use the same current timestamp at all places
nb authored
220
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
221 // Look for entry boundaries
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
222 list( $start_timestamp, $end_timestamp ) = self::get_timestamps_from_query();
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
223
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
224 // Bail if there is no end timestamp
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
225 if ( empty( $end_timestamp ) ) {
70b347a Mohammad Jangda Add missing textdomain to translated strings
mjangda authored
226 self::send_user_error( __( 'A timestamp is missing. Correct URL: <permalink>/liveblog/<from>/</to>/', 'liveblog' ) );
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
227 }
228
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
229 // Do not cache if it's too soon
bd6c841 Nikolay Bachiyski Use the Date header to get server-side date
nb authored
230 if ( $end_timestamp > time() )
6c66757 Nikolay Bachiyski Do not let the client cache future requests
nb authored
231 self::$do_not_cache_response = true;
232
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
233 // Get liveblog entries within the start and end boundaries
b28b38d Nikolay Bachiyski Rename the query class to match WP conventions
nb authored
234 $entries = self::$entry_query->get_between_timestamps( $start_timestamp, $end_timestamp );
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
235 if ( empty( $entries ) ) {
236 self::json_return( array(
237 'entries' => array(),
238 'latest_timestamp' => null
239 ) );
240 }
d79a82d Nikolay Bachiyski Gather common entry functionality in a new class
nb authored
241
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
242 /**
243 * Loop through each liveblog entry, set the most recent timestamp, and
244 * put the JSON data for each entry into a neat little array.
245 */
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
246 foreach( $entries as $entry ) {
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
247 $latest_timestamp = max( $latest_timestamp, $entry->get_timestamp() );
d79a82d Nikolay Bachiyski Gather common entry functionality in a new class
nb authored
248 $entries_for_json[] = $entry->for_json();
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
249 }
250
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
251 // Setup our data to return via JSON
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
252 $result_for_json = array(
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
253 'entries' => $entries_for_json,
254 'latest_timestamp' => $latest_timestamp,
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
255 );
256
e977561 Nikolay Bachiyski Get rid of extra json_return() arguments
nb authored
257 self::json_return( $result_for_json );
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
258 }
259
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
260 /** Private _is_ Methods **************************************************/
261
262 /**
263 * Are we viewing a liveblog post?
264 *
265 * @uses is_single()
266 * @uses is_liveblog_post()
267 * @return bool
268 */
269 private static function is_viewing_liveblog_post() {
270 return (bool) ( is_single() && self::is_liveblog_post() );
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
271 }
272
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
273 /**
274 * Is this the initial page request?
275 *
276 * Note that we do not use get_query_var() - it returns '' for all requests,
277 * which is valid for /post-name/liveblog/
278 *
279 * @global WP_Query $wp_query
280 * @return bool
281 */
282 private static function is_initial_page_request() {
8ff4206 Mohammad Jangda v1
mjangda authored
283 global $wp_query;
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
284
285 return (bool) ! isset( $wp_query->query_vars[self::key] );
8ff4206 Mohammad Jangda v1
mjangda authored
286 }
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
287
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
288 /**
289 * Is this an ajax request for the entries?
290 *
291 * @uses get_query_var() to check for the url_endpoint
292 * @return bool
293 */
294 private static function is_entries_ajax_request() {
295 return (bool) get_query_var( self::url_endpoint );
296 }
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
297
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
298 /**
299 * Is a given post_id a liveblog enabled post?
300 *
301 * @global WP_Post $post
302 * @param int $post_id
303 * @return bool
304 */
305 private static function is_liveblog_post( $post_id = null ) {
306 if ( empty( $post_id ) ) {
307 global $post;
308 $post_id = $post->ID;
309 }
310
311 return (bool) get_post_meta( $post_id, self::key, true );
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
312 }
313
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
314 /**
315 * Get timestamps from the current WP_Query
316 *
317 * Ensures that two timestamps exist, and returns a properly formatted empty
318 * array if not.
319 *
320 * @return array
321 */
322 private static function get_timestamps_from_query() {
323
324 // Look for timestamps and bail if none
25be635 Mohammad Jangda Support for non-pretty permalinks
mjangda authored
325 $stamps = rtrim( get_query_var( self::url_endpoint ), '/' );
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
326 if ( empty( $stamps ) )
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
327 return array( false, false );
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
328
329 // Get timestamps from the query variable
330 $timestamps = explode( '/', $stamps );
331
332 // Bail if there are not 2 timestamps
333 if ( 2 !== count( $timestamps ) )
334 return array( false, false );
335
336 // Return integer timestamps in an array
337 return array_map( 'intval', $timestamps );
1b8e8d5 Nikolay Bachiyski Streamline logic for getting recent entries
nb authored
338 }
339
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
340 /**
341 * Inserts, updates, or deletes a liveblog entry
342 */
343 public static function ajax_insert_entry() {
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
344
345 // Capability and Intention Checks
b58c3a0 Nikolay Bachiyski Remove leading underscores from function names
nb authored
346 self::ajax_current_user_can_edit_liveblog();
347 self::ajax_check_nonce();
8ff4206 Mohammad Jangda v1
mjangda authored
348
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
349 // Check POST data
ec017c4 Nikolay Bachiyski Kses posts content closer to the place we use it
nb authored
350 $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
351 $replaces_comment_id = isset( $_POST['replaces'] ) ? intval( $_POST['replaces'] ) : 0;
352 $entry_content = isset( $_POST['entry_content'] ) ? $_POST['entry_content'] : '';
8ff4206 Mohammad Jangda v1
mjangda authored
353
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
354 if ( empty( $post_id ) )
69f9cbb Nikolay Bachiyski Add more specific HTTP error codes
nb authored
355 self::send_user_error( __( 'Sorry, that post is not accepting Liveblog entries.', 'liveblog' ) );
8ff4206 Mohammad Jangda v1
mjangda authored
356
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
357 // Get the current user
8ff4206 Mohammad Jangda v1
mjangda authored
358 $user = wp_get_current_user();
359
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
360 // Insert new comment
361 $new_comment_id = wp_insert_comment( array(
362 'comment_post_ID' => $post_id,
ec017c4 Nikolay Bachiyski Kses posts content closer to the place we use it
nb authored
363 'comment_content' => wp_filter_post_kses( $entry_content ),
f157181 John James Jacoby Comment Status:
johnjamesjacoby authored
364 'comment_approved' => self::key,
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
365 'comment_type' => self::key,
366 'user_id' => $user->ID,
8ff4206 Mohammad Jangda v1
mjangda authored
367
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
368 'comment_author' => $user->display_name,
8ff4206 Mohammad Jangda v1
mjangda authored
369 'comment_author_email' => $user->user_email,
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
370 'comment_author_url' => $user->user_url,
371
8ff4206 Mohammad Jangda v1
mjangda authored
372 // Borrowed from core as wp_insert_comment does not generate them
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
373 'comment_author_IP' => preg_replace( '/[^0-9a-fA-F:., ]/', '',$_SERVER['REMOTE_ADDR'] ),
374 'comment_agent' => substr( $_SERVER['HTTP_USER_AGENT'], 0, 254 ),
375 ) );
2b0244c Nikolay Bachiyski Remove trailing whitespace
nb authored
376 wp_cache_delete( self::key . '_entries_asc_' . $post_id, 'liveblog' );
8ff4206 Mohammad Jangda v1
mjangda authored
377
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
378 // Bail if comment could not be saved
379 if ( empty( $new_comment_id ) || is_wp_error( $new_comment_id ) )
69f9cbb Nikolay Bachiyski Add more specific HTTP error codes
nb authored
380 self::send_server_error( __( 'Error posting entry', 'liveblog' ) );
8ff4206 Mohammad Jangda v1
mjangda authored
381
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
382 // Are we replacing an existing comment?
383 if ( !empty( $replaces_comment_id ) ) {
a2f3ce4 Nikolay Bachiyski Show inserted entry immediately
nb authored
384
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
385 //
a2723d4 Nikolay Bachiyski Commit entry modifications
nb authored
386 add_comment_meta( $new_comment_id, WPCOM_Liveblog_Entry::replaces_meta_key, $replaces_comment_id );
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
387
388 // Update an existing comment
389 if ( !empty( $entry_content ) ) {
390 wp_update_comment( array(
391 'comment_ID' => $replaces_comment_id,
ec017c4 Nikolay Bachiyski Kses posts content closer to the place we use it
nb authored
392 'comment_content' => wp_filter_post_kses( $entry_content ),
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
393 ) );
394
395 // Delete this comment
396 } else {
a2723d4 Nikolay Bachiyski Commit entry modifications
nb authored
397 wp_delete_comment( $replaces_comment_id );
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
398 }
a2723d4 Nikolay Bachiyski Commit entry modifications
nb authored
399 }
400
a2f3ce4 Nikolay Bachiyski Show inserted entry immediately
nb authored
401 $entry = WPCOM_Liveblog_Entry::from_comment( get_comment( $new_comment_id ) );
402
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
403 // Do not send latest_timestamp. If we send it the client won't get
404 // older entries. Since we send only the new one, we don't know if there
405 // weren't any entries in between.
406 self::json_return( array(
407 'entries' => array( $entry->for_json() ),
408 'latest_timestamp' => null
409 ) );
8ff4206 Mohammad Jangda v1
mjangda authored
410 }
411
5aee585 Nikolay Bachiyski Add ajax endpoint for preview
nb authored
412 function ajax_preview_entry() {
413 self::ajax_current_user_can_edit_liveblog();
414 self::ajax_check_nonce();
415 $entry_content = isset( $_REQUEST['entry_content'] ) ? $_REQUEST['entry_content'] : '';
416 $entry_content = wp_filter_post_kses( $entry_content );
417 $entry_content = WPCOM_Liveblog_Entry::render_content( $entry_content );
418 self::json_return( array( 'html' => $entry_content ) );
419 }
420
4872b2a Nikolay Bachiyski Use front-end AJAX URLs for same-origin requests
nb authored
421 public function ajax_unknown() {
422 self::send_user_error( __( 'Unknown liveblog action', 'liveblog' ) );
423 }
424
425
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
426 /** Comment Methods *******************************************************/
8ff4206 Mohammad Jangda v1
mjangda authored
427
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
428 /**
429 * Add a liveblog class to each comment, so they can be styled
430 *
431 * @param array $classes
432 * @return string
433 */
434 public static function add_comment_class( $classes ) {
8ff4206 Mohammad Jangda v1
mjangda authored
435 $classes[] = 'liveblog-entry';
436 return $classes;
437 }
438
f157181 John James Jacoby Comment Status:
johnjamesjacoby authored
439
440 /**
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
441 * Enqueue the necessary CSS and JS that liveblog needs to function.
442 *
443 * @return If not a liveblog post
444 */
445 public static function enqueue_scripts() {
446
8ff4206 Mohammad Jangda v1
mjangda authored
447 if ( ! self::is_viewing_liveblog_post() )
448 return;
449
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
450 wp_enqueue_style( self::key, plugins_url( 'css/liveblog.css', __FILE__ ) );
db742c8 Nikolay Bachiyski Highlight new posts
nb authored
451 wp_enqueue_script( self::key, plugins_url( 'js/liveblog.js', __FILE__ ), array( 'jquery', 'jquery-color' ), self::version, true );
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
452
8413aff John James Jacoby Uploading:
johnjamesjacoby authored
453 if ( self::current_user_can_edit_liveblog() ) {
a456b55 Nikolay Bachiyski Add Preview interface
nb authored
454 wp_enqueue_script( 'liveblog-publisher', plugins_url( 'js/liveblog-publisher.js', __FILE__ ), array( self::key, 'jquery-ui-tabs' ), self::version, true );
212e64d Zach Hookable Uploader
zbtirrell authored
455 wp_enqueue_script( 'liveblog-plupload', plugins_url( 'js/plupload.js', __FILE__ ), array( self::key, 'wp-plupload', 'jquery' ) );
c65e58f Nikolay Bachiyski Make the function name a verb
nb authored
456 self::add_default_plupload_settings();
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
457 }
8ff4206 Mohammad Jangda v1
mjangda authored
458
c0c3085 Nikolay Bachiyski Add spinner for inserting an entry
nb authored
459 if ( wp_script_is( 'jquery.spin', 'registered' ) ) {
460 wp_enqueue_script( 'jquery.spin' );
461 } else {
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
462 wp_enqueue_script( 'spin', plugins_url( 'js/spin.js', __FILE__ ), false, '1.2.4' );
463 wp_enqueue_script( 'jquery.spin', plugins_url( 'js/jquery.spin.js', __FILE__ ), array( 'jquery', 'spin' ) );
c0c3085 Nikolay Bachiyski Add spinner for inserting an entry
nb authored
464 }
465
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
466 wp_localize_script( self::key, 'liveblog_settings',
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
467 apply_filters( 'liveblog_settings', array(
468 'permalink' => get_permalink(),
469 'post_id' => get_the_ID(),
8ff4206 Mohammad Jangda v1
mjangda authored
470
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
471 'key' => self::key,
472 'nonce_key' => self::nonce_key,
473 'latest_entry_timestamp' => self::$entry_query->get_latest_timestamp(),
8ff4206 Mohammad Jangda v1
mjangda authored
474
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
475 'refresh_interval' => self::refresh_interval,
ed1991c Nikolay Bachiyski Shut the auto-update only on consecutive failures
nb authored
476 'max_consecutive_retries'=> self::max_consecutive_retries,
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
477 'delay_threshold' => self::delay_threshold,
478 'delay_multiplier' => self::delay_multiplier,
8ff4206 Mohammad Jangda v1
mjangda authored
479
358dc85 Nikolay Bachiyski Rename entriesurl to endpoint_url
nb authored
480 'endpoint_url' => self::get_entries_endpoint_url(),
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
481
482 // i18n
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
483 'update_nag_singular' => __( '%d new update', 'liveblog' ),
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
484 'update_nag_plural' => __( '%d new updates', 'liveblog' ),
28802ff Nikolay Bachiyski Delete entry functionality
nb authored
485 'delete_confirmation' => __( 'Do you really want do delete this entry? There is no way back.', 'liveblog' ),
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
486 ) )
487 );
8ff4206 Mohammad Jangda v1
mjangda authored
488 }
489
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
490 /**
8413aff John James Jacoby Uploading:
johnjamesjacoby authored
491 * Sets up some default Plupload settings so we can upload meda theme-side
492 *
493 * @global type $wp_scripts
494 */
c65e58f Nikolay Bachiyski Make the function name a verb
nb authored
495 private static function add_default_plupload_settings() {
8413aff John James Jacoby Uploading:
johnjamesjacoby authored
496 global $wp_scripts;
497
498 $defaults = array(
499 'runtimes' => 'html5,silverlight,flash,html4',
500 'file_data_name' => 'async-upload',
501 'multiple_queues' => true,
502 'max_file_size' => self::max_upload_size() . 'b',
503 'url' => admin_url( 'admin-ajax.php', 'relative' ),
504 'flash_swf_url' => includes_url( 'js/plupload/plupload.flash.swf' ),
505 'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
70b347a Mohammad Jangda Add missing textdomain to translated strings
mjangda authored
506 'filters' => array( array( 'title' => __( 'Allowed Files', 'liveblog' ), 'extensions' => '*') ),
8413aff John James Jacoby Uploading:
johnjamesjacoby authored
507 'multipart' => true,
508 'urlstream_upload' => true,
509 'multipart_params' => array(
510 'action' => 'upload-attachment',
511 '_wpnonce' => wp_create_nonce( 'media-form' )
512 )
513 );
514
515 $settings = array(
516 'defaults' => $defaults,
517 'browser' => array(
518 'mobile' => wp_is_mobile(),
519 'supported' => _device_can_upload(),
520 )
521 );
522
523 $script = 'var _wpPluploadSettings = ' . json_encode( $settings ) . ';';
524 $data = $wp_scripts->get_data( 'wp-plupload', 'data' );
525
526 if ( ! empty( $data ) )
527 $script = "$data\n$script";
528
529 $wp_scripts->add_data( 'wp-plupload', 'data', $script );
530 }
531
532 /**
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
533 * Get the URL of a specific liveblog entry.
534 *
535 * @return string
536 */
537 private static function get_entries_endpoint_url() {
0dccc81 Nikolay Bachiyski Add correct endpoing to draft URLs with query args
nb authored
538 $post_permalink = get_permalink( self::$post_id );
539 if ( false !== strpos( $post_permalink, '?p=' ) )
540 $url = add_query_arg( self::url_endpoint, '', $post_permalink ) . '='; // returns something like ?p=1&liveblog=
25be635 Mohammad Jangda Support for non-pretty permalinks
mjangda authored
541 else
0dccc81 Nikolay Bachiyski Add correct endpoing to draft URLs with query args
nb authored
542 $url = trailingslashit( trailingslashit( $post_permalink ) . self::url_endpoint ); // returns something like /2012/01/01/post/liveblog/
7330fcb Nikolay Bachiyski Allow the endpoint URL to be filtered
nb authored
543 $url = apply_filters( 'liveblog_endpoint_url', $url, self::$post_id );
25be635 Mohammad Jangda Support for non-pretty permalinks
mjangda authored
544 return $url;
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
545 }
cb778e4 Mohammad Jangda Load initial entries server-side rather than via AJAX for SEO benefit
mjangda authored
546
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
547 /** Display Methods *******************************************************/
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
548
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
549 /**
550 * Filter the_content and add the liveblog theme-side UI above the normal
551 * content area.
552 *
553 * @param string $content
554 * @return string
555 */
556 public static function add_liveblog_to_content( $content ) {
cb778e4 Mohammad Jangda Load initial entries server-side rather than via AJAX for SEO benefit
mjangda authored
557
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
558 $liveblog_output = '<div id="liveblog-container" class="'. self::$post_id .'">';
559 $liveblog_output .= self::get_editor_output();
560 $liveblog_output .= '<div id="liveblog-update-spinner"></div>';
561 $liveblog_output .= self::get_all_entry_output();
ba2c2ae Mohammad Jangda Switch around ids with classes just to be future-safe. Add a new wrapper...
mjangda authored
562 $liveblog_output .= '</div>';
8ff4206 Mohammad Jangda v1
mjangda authored
563
7205206 Nikolay Bachiyski Put the post contents before the liveblog
nb authored
564 return $content . $liveblog_output;
8ff4206 Mohammad Jangda v1
mjangda authored
565 }
566
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
567 /**
568 * Return the posting area for the end-user to liveblog from
569 *
570 * @return string
571 */
572 private static function get_editor_output() {
b58c3a0 Nikolay Bachiyski Remove leading underscores from function names
nb authored
573 if ( ! self::current_user_can_edit_liveblog() )
8ff4206 Mohammad Jangda v1
mjangda authored
574 return;
575
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
576 // Get the template part
577 return self::get_template_part( 'liveblog-form.php' );
8ff4206 Mohammad Jangda v1
mjangda authored
578 }
579
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
580 /**
581 * Get all the liveblog entries for this post
582 */
583 private static function get_all_entry_output() {
8413aff John James Jacoby Uploading:
johnjamesjacoby authored
584
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
585 // Get liveblog entries
9c2b7c3 Nikolay Bachiyski Order entries reverse-chronologically on initial load
nb authored
586 $entries = (array) self::$entry_query->get_all();
8ff4206 Mohammad Jangda v1
mjangda authored
587
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
588 // Get the template part
589 return self::get_template_part( 'liveblog-loop.php', compact( 'entries' ) );
590 }
591
592 /**
593 * Get the template part in an output buffer and return it
594 *
595 * @param string $template_name
596 * @param array $template_variables
597 */
598 public static function get_template_part( $template_name, $template_variables = array() ) {
599 ob_start();
600 extract( $template_variables );
601 include( dirname( __FILE__ ) . '/templates/' . $template_name );
602 return ob_get_clean();
8ff4206 Mohammad Jangda v1
mjangda authored
603 }
8413aff John James Jacoby Uploading:
johnjamesjacoby authored
604
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
605 /** Admin Methods *********************************************************/
606
607 /**
608 * Register the metabox with the supporting post-type
609 *
610 * @param string $post_type
611 */
612 public static function add_meta_box( $post_type ) {
613
614 // Bail if not supported
615 if ( ! post_type_supports( $post_type, self::key ) )
616 return;
8ff4206 Mohammad Jangda v1
mjangda authored
617
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
618 add_meta_box( self::key, __( 'Liveblog', 'liveblog' ), array( __CLASS__, 'display_meta_box' ) );
8ff4206 Mohammad Jangda v1
mjangda authored
619 }
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
620
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
621 /**
622 * Output the metabox
623 *
624 * @param WP_Post $post
625 */
626 public static function display_meta_box( $post ) {
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
627 ?>
628
8ff4206 Mohammad Jangda v1
mjangda authored
629 <label>
630 <input type="checkbox" name="is-liveblog" id="is-liveblog" value="1" <?php checked( self::is_liveblog_post( $post->ID ) ); ?> />
631 <?php esc_html_e( 'This is a liveblog', 'liveblog' ); ?>
632 </label>
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
633
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
634 <?php
635 wp_nonce_field( self::nonce_key, self::nonce_key, false );
8ff4206 Mohammad Jangda v1
mjangda authored
636 }
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
637
638 /**
639 * Save the metabox when the post is saved
640 *
641 * @param type $post_id
642 * @return type
643 */
644 public function save_meta_box( $post_id ) {
645
646 // Bail if no liveblog nonce
647 if ( empty( $_POST[self::nonce_key] ) || ! wp_verify_nonce( $_POST[self::nonce_key], self::nonce_key ) )
8ff4206 Mohammad Jangda v1
mjangda authored
648 return;
649
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
650 // Update liveblog beta
651 if ( ! empty( $_POST['is-liveblog'] ) )
8ff4206 Mohammad Jangda v1
mjangda authored
652 update_post_meta( $post_id, self::key, 1 );
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
653
654 // Delete liveblog meta
8ff4206 Mohammad Jangda v1
mjangda authored
655 else
656 delete_post_meta( $post_id, self::key );
657 }
2ba7f86 Nikolay Bachiyski Remove trailing whitespace
nb authored
658
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
659 /** Error Methods *********************************************************/
660
661 /**
662 * Can the current user edit liveblog data (non-ajax)
663 *
664 * @return bool
665 */
666 public static function current_user_can_edit_liveblog() {
667 return (bool) current_user_can( apply_filters( 'liveblog_edit_cap', self::edit_cap ) );
8ff4206 Mohammad Jangda v1
mjangda authored
668 }
669
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
670 /**
671 * Can the current user edit liveblog data (ajax)
672 *
673 * Sends an error if not
674 */
675 public static function ajax_current_user_can_edit_liveblog() {
b58c3a0 Nikolay Bachiyski Remove leading underscores from function names
nb authored
676 if ( ! self::current_user_can_edit_liveblog() ) {
69f9cbb Nikolay Bachiyski Add more specific HTTP error codes
nb authored
677 self::send_forbidden_error( __( "Cheatin', uh?", 'liveblog' ) );
8ff4206 Mohammad Jangda v1
mjangda authored
678 }
679 }
680
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
681 /**
682 * Check for valid intention, and send an error if there is none
683 *
684 * @param string $action
685 */
686 public static function ajax_check_nonce( $action = self::nonce_key ) {
8ff4206 Mohammad Jangda v1
mjangda authored
687 if ( ! isset( $_REQUEST[ self::nonce_key ] ) || ! wp_verify_nonce( $_REQUEST[ self::nonce_key ], $action ) ) {
69f9cbb Nikolay Bachiyski Add more specific HTTP error codes
nb authored
688 self::send_forbidden_error( __( 'Sorry, we could not authenticate you.', 'liveblog' ) );
8ff4206 Mohammad Jangda v1
mjangda authored
689 }
690 }
691
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
692 /** Feedback **************************************************************/
693
694 /**
695 * Send an error message
696 * @param type $message
697 */
69f9cbb Nikolay Bachiyski Add more specific HTTP error codes
nb authored
698 private static function send_server_error( $message ) {
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
699 self::status_header_with_message( 500, $message );
700 exit();
701 }
702
69f9cbb Nikolay Bachiyski Add more specific HTTP error codes
nb authored
703 private static function send_user_error( $message ) {
704 self::status_header_with_message( 406, $message );
705 exit();
706 }
707
708 private static function send_forbidden_error( $message ) {
709 self::status_header_with_message( 403, $message );
710 exit();
711 }
712
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
713 /**
714 * Encode some data and echo it (possibly without cached headers)
715 *
716 * @param array $data
717 */
718 private static function json_return( $data ) {
20c5b02 Nikolay Bachiyski Get rid of the intermediate layer completely
nb authored
719 $json_data = json_encode( $data );
8ff4206 Mohammad Jangda v1
mjangda authored
720
f2908df Nikolay Bachiyski Get rid if return functionality in json_return()
nb authored
721 header( 'Content-Type: application/json' );
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
722 if ( self::$do_not_cache_response )
6c66757 Nikolay Bachiyski Do not let the client cache future requests
nb authored
723 nocache_headers();
20c5b02 Nikolay Bachiyski Get rid of the intermediate layer completely
nb authored
724
725 echo $json_data;
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
726 exit();
d81e042 Nikolay Bachiyski Extract sending errors in its own method
nb authored
727 }
728
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
729 /**
730 * Modify the header and description in the global array
731 *
732 * @global array $wp_header_to_desc
733 * @param int $status
734 * @param string $message
735 */
736 private static function status_header_with_message( $status, $message ) {
2532397 Nikolay Bachiyski Send HTTP errors instead of the success flag
nb authored
737 global $wp_header_to_desc;
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
738
739 $status = absint( $status );
740 $official_message = isset( $wp_header_to_desc[$status] ) ? $wp_header_to_desc[$status] : '';
2532397 Nikolay Bachiyski Send HTTP errors instead of the success flag
nb authored
741 $wp_header_to_desc[$status] = $message;
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
742
2532397 Nikolay Bachiyski Send HTTP errors instead of the success flag
nb authored
743 status_header( $status );
cd6d574 John James Jacoby * Code clean up.
johnjamesjacoby authored
744
2532397 Nikolay Bachiyski Send HTTP errors instead of the success flag
nb authored
745 $wp_header_to_desc[$status] = $official_message;
746 }
8413aff John James Jacoby Uploading:
johnjamesjacoby authored
747
748 /** Plupload Helpers ******************************************************/
749
750 /**
751 * Convert hours to bytes
752 *
753 * @param unknown_type $size
754 * @return unknown
755 */
756 private static function convert_hr_to_bytes( $size ) {
757 $size = strtolower( $size );
758 $bytes = (int) $size;
759
760 if ( strpos( $size, 'k' ) !== false )
761 $bytes = intval( $size ) * 1024;
762 elseif ( strpos( $size, 'm' ) !== false )
763 $bytes = intval( $size ) * 1024 * 1024;
764 elseif ( strpos( $size, 'g' ) !== false )
765 $bytes = intval( $size ) * 1024 * 1024 * 1024;
766
767 return $bytes;
768 }
769
770 /**
771 * Convert bytes to hour
772 *
773 * @param string $bytes
774 * @return string
775 */
776 private static function convert_bytes_to_hr( $bytes ) {
777 $units = array( 0 => 'B', 1 => 'kB', 2 => 'MB', 3 => 'GB' );
778 $log = log( $bytes, 1024 );
779 $power = (int) $log;
780 $size = pow( 1024, $log - $power );
781
782 return $size . $units[$power];
783 }
784
785 /**
786 * Get the maximum upload file size
787 *
788 * @see wp_max_upload_size()
789 * @return string
790 */
791 private static function max_upload_size() {
792 $u_bytes = self::convert_hr_to_bytes( ini_get( 'upload_max_filesize' ) );
793 $p_bytes = self::convert_hr_to_bytes( ini_get( 'post_max_size' ) );
794 $bytes = apply_filters( 'upload_size_limit', min( $u_bytes, $p_bytes ), $u_bytes, $p_bytes );
795
796 return $bytes;
a6429c8 Nikolay Bachiyski Remove trailing whitespace
nb authored
797 }
8ff4206 Mohammad Jangda v1
mjangda authored
798 }
799
0a0ca28 John James Jacoby Weekend updates:
johnjamesjacoby authored
800 /**
801 * Load the one true WPCOM_Liveblog instance
802 *
803 * Loaded late on the 'plugins_loaded' hook to allow any other plugin to sneak
804 * in ahead of it, to add actions, filters, etc...
805 *
806 * @uses WPCOM_Liveblog::load()
807 */
808 function wpcom_liveblog_load() {
809 WPCOM_Liveblog::load();
810 }
811 add_action( 'plugins_loaded', 'wpcom_liveblog_load', 999 );
812
8ff4206 Mohammad Jangda v1
mjangda authored
813 endif;
Something went wrong with that request. Please try again.