Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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