/** * Main AJAX callback * * @param object The post fragment object */ function callback($post_data) { if (!property_exists($post_data, 'postID') || !property_exists($post_data, 'isSticky')) { self::die_failure('invalid_message', __('Insufficient information provided.', 'o2')); } $post = get_post(absint($post_data->postID)); if (!$post) { self::die_failure('post_not_found', __('Post not found.', 'o2')); } if (!current_user_can('edit_post', $post->ID)) { self::die_failure('cannot_edit_post_sticky', __('You are not allowed to edit this post sticky.', 'o2')); } if ($post_data->isSticky) { stick_post($post->ID); } else { unstick_post($post->ID); } // Bump the post to make it update in polling o2_Fragment::bump_post($post->ID); $retval = array('isSticky' => is_sticky($post->ID)); self::die_success($retval); }
public static function poll() { // This is a super lightweight API to get posts and comments from WP // It's intended for use with o2 // @todo Allow requesting a specific post or comment, and a post with all comments // Need to sort things because they're queried separately function o2_date_sort($a, $b) { if ($a->unixtime == $b->unixtime) { return 0; } return $a->unixtime > $b->unixtime ? -1 : 1; } $ok_to_serve_data = true; $ok_to_serve_data = apply_filters('o2_read_api_ok_to_serve_data', $ok_to_serve_data); $data = array(); if ($ok_to_serve_data) { $posts = self::get_posts(); $comments = self::get_comments(); // Clean up posts and comments $data = array(); if (count($posts)) { foreach ($posts as $post) { $data[] = o2_Fragment::get_fragment($post); } } if (count($comments)) { foreach ($comments as $comment) { $data[] = o2_Fragment::get_fragment($comment); } } // Shuffle up and deal usort($data, 'o2_date_sort'); } // Let the client know if the user is logged in or not $is_logged_in = is_user_logged_in(); // Generate an updated nonce (they expire after all, and our "app" may be open for a long time) $new_nonce = wp_create_nonce('o2_nonce'); if ($is_logged_in) { // @todo change to another way, and one that is less costly - see also below // $current_user_id = get_current_user_id(); // update_user_meta( $current_user_id, 'o2_last_poll_gmt', time() ); } $response = array("data" => $data, "newNonce" => $new_nonce, "loggedIn" => $is_logged_in); // Check for unloaded scripts and styles if there are polled posts if (!empty($data)) { // Attach scripts if (isset($_REQUEST['scripts'])) { // Parse and sanitize the script handles already output if (!is_array($_REQUEST['scripts'])) { $_REQUEST['scripts'] = explode(',', $_REQUEST['scripts']); } $initial_scripts = is_array($_REQUEST['scripts']) ? array_map('sanitize_text_field', $_REQUEST['scripts']) : null; if (is_array($initial_scripts)) { global $wp_scripts; if (!$wp_scripts instanceof WP_Scripts) { $wp_scripts = new WP_Scripts(); } // Identify new scripts needed by the polled posts $polled_scripts = array_diff($wp_scripts->done, $initial_scripts); // If new scripts are needed, extract relevant data from $wp_scripts if (!empty($polled_scripts)) { $response['scripts'] = array(); foreach ($polled_scripts as $handle) { // Abort if the handle doesn't match a registered script if (!isset($wp_scripts->registered[$handle])) { continue; } // Provide basic script data $script_data = array('handle' => $handle, 'footer' => is_array($wp_scripts->in_footer) && in_array($handle, $wp_scripts->in_footer), 'extra_data' => $wp_scripts->print_extra_script($handle, false)); // Base source $src = $wp_scripts->registered[$handle]->src; // Take base_url into account if (strpos($src, '//') === 0) { $src = is_ssl() ? 'https:' . $src : 'http:' . $src; } // Deal with root-relative URLs if (strpos($src, '/') === 0) { $src = $wp_scripts->base_url . $src; } if (strpos($src, 'http') !== 0) { $src = $wp_scripts->base_url . $src; } // Version and additional arguments if (null === $wp_scripts->registered[$handle]->ver) { $ver = ''; } else { $ver = $wp_scripts->registered[$handle]->ver ? $wp_scripts->registered[$handle]->ver : $wp_scripts->default_version; } if (isset($wp_scripts->args[$handle])) { $ver = $ver ? $ver . '&' . $wp_scripts->args[$handle] : $wp_scripts->args[$handle]; } // Full script source with version info $script_data['src'] = add_query_arg('ver', $ver, $src); // Add script to data that will be returned to o2 array_push($response['scripts'], $script_data); } } } } // Attach styles if (isset($_REQUEST['styles'])) { // Parse and sanitize the script handles already output if (!is_array($_REQUEST['styles'])) { $_REQUEST['styles'] = explode(',', $_REQUEST['styles']); } // Parse and sanitize the style handles already output $initial_styles = is_array($_REQUEST['styles']) ? array_map('sanitize_text_field', $_REQUEST['styles']) : null; if (is_array($initial_styles)) { global $wp_styles; // Identify new styles needed by the polled posts $polled_styles = array_diff($wp_styles->done, $initial_styles); // If new styles are needed, extract relevant data from $wp_styles if (!empty($polled_styles)) { $response['styles'] = array(); foreach ($polled_styles as $handle) { // Abort if the handle doesn't match a registered stylesheet if (!isset($wp_styles->registered[$handle])) { continue; } // Provide basic style data $styles_data = array('handle' => $handle, 'media' => 'all'); // Base source $src = $wp_styles->registered[$handle]->src; // Take base_url into account if (strpos($src, 'http') !== 0) { $src = $wp_styles->base_url . $src; } // Version and additional arguments if (null === $wp_styles->registered[$handle]->ver) { $ver = ''; } else { $ver = $wp_styles->registered[$handle]->ver ? $wp_styles->registered[$handle]->ver : $wp_styles->default_version; } if (isset($wp_styles->args[$handle])) { $ver = $ver ? $ver . '&' . $wp_styles->args[$handle] : $wp_styles->args[$handle]; } // Full script source with version info $script_data['src'] = add_query_arg('ver', $ver, $src); // @todo Handle parsing conditional comments // Parse requested media context for stylesheet if (isset($wp_styles->registered[$handle]->args)) { $style_data['media'] = esc_attr($wp_styles->registered[$handle]->args); } // Add script to data that will be returned to o2 array_push($response['styles'], $style_data); } } } } } wp_send_json_success($response); }
function update_object_content($object_type, $object_ID, $updated_content) { $updated_object = false; if ('post' == $object_type) { o2_Fragment::bump_post($object_ID, $updated_content); $updated_object = get_post($object_ID); } else { $comment_data = array('comment_ID' => $object_ID, 'comment_content' => $updated_content); // purposefully throw away the comment global otherwise get_comment will return // the pre-updated copy from $GLOBALS if (isset($GLOBALS['comment'])) { unset($GLOBALS['comment']); } wp_update_comment($comment_data); update_comment_meta($object_ID, 'o2_comment_gmt_modified', time()); $updated_object = get_comment($object_ID); // update the global $GLOBALS['comment'] = $updated_object; } return $updated_object; }
function o2_live_comments_widget_footer_bootstrap() { // embed in the footer the most recent N comments and M posts based // on the widget settings $comment_count = 0; $post_count = 0; $settings_array = get_option('widget_o2-live-comments-widget'); if (is_array($settings_array)) { foreach ((array) $settings_array as $settings) { if (is_array($settings)) { if (isset($settings['kind']) && isset($settings['number'])) { $kind = $settings['kind']; $count = intval($settings['number']); if (0 > $count) { $count = 0; } if ("both" == $kind) { $comment_count = max($comment_count, $count); $post_count = max($post_count, $count); } else { if ("comment" == $kind) { $comment_count = max($comment_count, $count); } else { if ("post" == $kind) { $post_count = max($post_count, $count); } } } } } } } $live_bootstrap = array(); // Bootstrap comments, as needed based on widget settings if (0 < $comment_count) { $comments = get_comments(array('status' => 'approve', 'number' => $comment_count)); // instead of using get_fragment, which is terribly verbose, we use lighterweight emitters here foreach ((array) $comments as $comment) { $comment_ID = $comment->comment_ID; $comment_post_ID = $comment->comment_post_ID; $title = html_entity_decode(get_the_title($comment->comment_post_ID)); $comment_bootstrap = array('unixtime' => strtotime($comment->comment_date_gmt), 'title' => $title, 'domRef' => "#comment-" . $comment_ID, 'permalink' => get_permalink($comment_post_ID) . '#comment-' . $comment_ID, 'type' => 'comment', 'externalID' => $comment_ID, 'postID' => $comment->comment_post_ID); $commentor = o2_Fragment::get_comment_author_properties($comment); $live_bootstrap[] = array_merge($comment_bootstrap, $commentor); } } // Bootstrap posts, as needed based on widget settings if (0 < $post_count) { $posts = get_posts(array('post_status' => 'publish', 'number' => $post_count)); foreach ((array) $posts as $post) { $post_ID = $post->ID; $title = html_entity_decode($post->post_title); $post_bootstrap = array('unixtime' => strtotime($post->post_date_gmt), 'title' => $title, 'domRef' => "#post-" . $post_ID, 'permalink' => get_permalink($post_ID), 'type' => 'post', 'externalID' => $post_ID); $poster = o2_Fragment::get_post_user_properties($post); $live_bootstrap[] = array_merge($post_bootstrap, $poster); } } echo "<script class='o2-live-widget-bootstrap-data' type='application/json' style='display:none'>"; echo json_encode($live_bootstrap); echo "</script>\n"; // split this and the widgets init out into a separate class. maybe rename the containing directory to simply live-comments }
/** * Main AJAX callback * * Determine if a post's next state is valid, set it, and return the audit log fragment as success, or * an error message as failure * * @param object The post fragment object */ function callback($post_data) { if (!property_exists($post_data, 'postID') || !property_exists($post_data, 'nextState')) { self::die_failure('invalid_message', __('Insufficient information provided.', 'o2')); } $post = get_post(absint($post_data->postID)); if (!$post) { self::die_failure('post_not_found', __('Post not found.', 'o2')); } if (!current_user_can('edit_post', $post->ID)) { self::die_failure('cannot_edit_post_resolution', __('You are not allowed to edit this post resolution', 'o2')); } if (in_array($post_data->nextState, self::get_state_slugs())) { $next_state = sanitize_key($post_data->nextState); } else { self::die_failure('invalid_post_resolution', __('Invalid post resolution.', 'o2')); } $success = self::change_post_state($post->ID, $next_state); if (is_wp_error($success)) { self::die_failure('cannot_set_post_resolution', __('Unable to set post resolution.', 'o2')); } else { $audit_log = self::get_audit_log_fragment($success); } $post_terms = o2_Fragment::get_post_terms($post->ID); $retval = array('currentState' => $next_state, 'auditLog' => $audit_log); self::die_success($retval); }
/** * Called from the end of the core commenting process. If we get here * then the comment was posted successfully, so just output it and die. */ public static function comment_success($comment) { do_action('o2_writeapi_comment_created', $comment->comment_ID); self::die_success(o2_Fragment::get_fragment($comment)); }
/** * Called from the end of the core commenting process. If we get here * then the comment was posted successfully, so just output it and die. */ public static function comment_success($comment) { self::die_success(o2_Fragment::get_fragment($comment)); }
public function ajax_get_o2_userdata() { // returns the o2 userdata for the given userLogin, or an error if a bad userLogin was given // note: both priv and nopriv hit this, since o2s can be read by nopriv users $ok_to_serve_data = apply_filters('o2_read_api_ok_to_serve_data', true); if (!$ok_to_serve_data) { wp_send_json_error(array('errorText' => 'Unauthorized')); } if (isset($_GET['userlogin'])) { // V1 userlogin (singular) // it will be OK to remove this case after the new V2 has been deployed // for a day or two, so as to not disrupt V1 clients that are still active $user_login = $_GET['userlogin']; $user = get_user_by('login', $user_login); if (false === $user) { wp_send_json_error(array('errorText' => 'Invalid request')); } // Otherwise, create and send the model $user_data = get_userdata($user->ID); $user_model = o2_Fragment::get_model_from_userdata($user_data); wp_send_json_success($user_model); } else { // V2 userlogins (array of 1 or more) $user_logins = $_GET['userlogins']; $user_models = array(); foreach ((array) $user_logins as $user_login) { $user = get_user_by('login', $user_login); if (false != $user) { $user_data = get_userdata($user->ID); $user_models[] = o2_Fragment::get_model_from_userdata($user_data); } } if (0 == count($user_models)) { wp_send_json_error(array('errorText' => 'Invalid request')); } wp_send_json_success($user_models); } }
public static function get_fragment_from_comment($my_comment) { add_filter('home_url', array('o2_Fragment', 'home_url'), 10, 4); // Update the global comment variable for methods like get_comment_ID used by comment_like_current_user_likes global $comment; if (isset($comment)) { $old_comment = $comment; } $comment = $my_comment; // Update the global post variable for methods like $wp_embed->autoembed that need it global $post; if (isset($post)) { $old_post = $post; } $post = get_post($my_comment->comment_post_ID); $comment_ID = $my_comment->comment_ID; $permalink = get_permalink($my_comment->comment_post_ID) . '#comment-' . $comment_ID; $permalink = apply_filters('o2_comment_permalink', $permalink, $comment_ID); $approved = '1' == $my_comment->comment_approved; $is_trashed = 'trash' == $my_comment->comment_approved; $previously_deleted = get_comment_meta($comment_ID, 'o2_comment_prev_deleted', true); // Update the $comment_depth global with our findings so that get_comment_class will // work correctly global $comment_depth; $comment_depth = o2_Fragment::get_depth_for_comment($comment); // These calls with empty args will rely on global $comment $edit_comment_link = get_edit_comment_link(); $comment_class_array = get_comment_class(); $comment_class = !empty($comment_class_array); if ($comment_class) { // Let's add trashed and deleted classes to the comment for styling. if ($is_trashed) { $comment_class_array[] = 'o2-trashed'; } if ($previously_deleted) { $comment_class_array[] = 'o2-deleted'; } $comment_class = join(' ', $comment_class_array); } $comment_created = get_comment_meta($comment_ID, 'o2_comment_created', true); if (empty($comment_created)) { $comment_created = strtotime($comment->comment_date_gmt); } // Convert HTML entities in the comment_content back to their actual characters // So that the JSON encoded value is the same thing the user edited in their // comment in the first place. // In this way, if/when they edit the comment, they don't get things like <p> // in the editor, but get <p> instead $raw_content = htmlspecialchars_decode($my_comment->comment_content, ENT_QUOTES); $raw_post_title = html_entity_decode(get_the_title($my_comment->comment_post_ID)); // Mentions list($mentions, $mention_context) = self::get_mention_data('comment', $comment_ID, $raw_content); $fragment = array('type' => 'comment', 'id' => $comment_ID, 'postID' => $my_comment->comment_post_ID, 'postTitleRaw' => $raw_post_title, 'cssClasses' => $comment_class, 'parentID' => $my_comment->comment_parent, 'contentRaw' => $raw_content, 'contentFiltered' => apply_filters('comment_text', $my_comment->comment_content, $my_comment, array()), 'permalink' => $permalink, 'unixtime' => strtotime($my_comment->comment_date_gmt), 'loginRedirectURL' => wp_login_url($permalink), 'approved' => $approved, 'isTrashed' => $is_trashed, 'prevDeleted' => $previously_deleted, 'editURL' => $edit_comment_link, 'depth' => $comment_depth, 'commentDropdownActions' => o2_get_comment_actions('dropdown', $comment, $comment_depth), 'commentFooterActions' => o2_get_comment_actions('footer', $comment, $comment_depth), 'commentTrashedActions' => o2_get_comment_actions('trashed_dropdown', $comment, $comment_depth), 'mentions' => $mentions, 'mentionContext' => $mention_context, 'commentCreated' => $comment_created, 'hasChildren' => (bool) get_comment_meta($comment_ID, 'o2_comment_has_children', true)); // Get author properties (and bootstrap the rest of the model) // e.g. userLogin or noprivUserName, noprivUserHash and noprivUserURL for nopriv commentor $comment_author_properties = self::get_comment_author_properties($my_comment); $fragment = array_merge($fragment, $comment_author_properties); // Put the original globals back if (isset($old_comment)) { $comment = $old_comment; } if (isset($old_post)) { $post = $old_post; } remove_filter('home_url', array('o2_Fragment', 'home_url'), 10, 4); $fragment = apply_filters('o2_comment_fragment', $fragment, $comment_ID); // Force UTF8 to avoid JSON encode issues $fragment = self::to_utf8($fragment); return $fragment; }