Ejemplo n.º 1
0
 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);
 }
Ejemplo n.º 2
0
 public function on_ajax()
 {
     if (empty($_POST) || !isset($_POST['data'])) {
         wp_send_json_error(array('errorText' => 'Invalid request.  No data. (o2 List Creator)'));
     }
     $data = $_POST['data'];
     $object_ID = isset($data['objectID']) ? $data['objectID'] : '';
     $nonce = isset($data['nonce']) ? $data['nonce'] : '';
     if (!wp_verify_nonce($nonce, 'o2_nonce')) {
         wp_send_json_error(array('errorText' => 'Invalid request.  Bad nonce.  Please refresh the page and try again.'));
     }
     $object_type = isset($data['objectType']) ? $data['objectType'] : '';
     // Check user's permission to do anything to this object
     if ('post' == $object_type) {
         if (!$this->current_user_can_edit_checklist('post', $object_ID)) {
             wp_send_json_error(array('errorText' => 'Invalid request.  The current user cannot edit checklists in this post.'));
         }
     } else {
         if ('comment' == $object_type) {
             if (!$this->current_user_can_edit_checklist('comment', $object_ID)) {
                 wp_send_json_error(array('errorText' => 'Invalid request.  The current user cannot edit checklists in this comment.'));
             }
         } else {
             wp_send_json_error(array('errorText' => 'Invalid request.  Unrecognized checklist object type.'));
         }
     }
     $item_hash = isset($data['itemHash']) ? $data['itemHash'] : '';
     $item_hash_instance = isset($data['itemHashInstance']) ? $data['itemHashInstance'] : '';
     $command = isset($data['command']) ? $data['command'] : '';
     $arg1 = isset($data['arg1']) ? $data['arg1'] : '';
     $arg2 = isset($data['arg2']) ? $data['arg2'] : '';
     $content = $this->get_object_content($object_type, $object_ID);
     $content = $this->preserve_text($content);
     $content_array = preg_split('/(\\r\\n|\\r|\\n)/', $content);
     $updated_content_array = array();
     $this->clear_line_hashes();
     $line_to_insert_later = '';
     foreach ((array) $content_array as $content_line) {
         // get the line's hash and hash instance
         $line_hash = self::hash_line($content_line);
         $instance = $this->add_line_hash($line_hash);
         // is it the hash and hash instance we're looking for?
         if ($line_hash == $item_hash && $instance == $item_hash_instance) {
             if ('delete' == $command) {
                 // nothing to do, just don't append the content_line
                 do_action('o2_checklists_command', $command, $object_type, $object_ID, $content_line);
             } else {
                 if ('update' == $command) {
                     $arg1 = wp_kses($arg1, $this->allowed_tags_in_tasks);
                     $content_line = preg_replace($this->task_item_regex, '${1}${2}${3}' . $arg1, $content_line);
                     $updated_content_array[] = $content_line;
                     do_action('o2_checklists_command', $command, $object_type, $object_ID, $content_line);
                 } else {
                     if ('add' == $command) {
                         $updated_content_array[] = $content_line;
                         $arg1 = wp_kses($arg1, $this->allowed_tags_in_tasks);
                         $added_content_line = preg_replace($this->task_item_regex, '${1}o${3}' . $arg1, $content_line);
                         $updated_content_array[] = $added_content_line;
                         do_action('o2_checklists_command', $command, $object_type, $object_ID, $added_content_line);
                     } else {
                         if ('check' == $command) {
                             $user_mention = $this->get_current_user_mention();
                             if ('true' == $arg1) {
                                 // check (x) the item
                                 $content_line = preg_replace($this->task_item_regex, '${1}x${3}${4}', $content_line);
                                 // add their name to the end of the string if it is not there already
                                 if ($user_mention != substr($content_line, -strlen($user_mention))) {
                                     $content_line .= $user_mention;
                                 }
                                 do_action('o2_checklists_command', 'check', $object_type, $object_ID, $content_line);
                             } else {
                                 // uncheck (o) the item
                                 $content_line = preg_replace($this->task_item_regex, '${1}o${3}${4}', $content_line);
                                 // if the user mention exists in the string, remove it
                                 if (false !== strpos($content_line, $user_mention)) {
                                     $content_line = str_replace($user_mention, '', $content_line);
                                 }
                                 do_action('o2_checklists_command', 'uncheck', $object_type, $object_ID, $content_line);
                             }
                             $updated_content_array[] = $content_line;
                         } else {
                             if ('moveAfter' == $command) {
                                 // for this pass, just don't append the content_line - we'll insert it in the correct spot in a second
                                 $line_to_insert_later = $content_line;
                                 do_action('o2_checklists_command', $command, $object_type, $object_ID, $content_line);
                             } else {
                                 if ('moveBefore' == $command) {
                                     // for this pass, just don't append the content_line - we'll insert it in the correct spot in a second
                                     $line_to_insert_later = $content_line;
                                     do_action('o2_checklists_command', $command, $object_type, $object_ID, $content_line);
                                 } else {
                                     // unrecognized command - do nothing to the line - just pass it through
                                     $updated_content_array[] = $content_line;
                                 }
                             }
                         }
                     }
                 }
             }
         } else {
             $updated_content_array[] = $content_line;
         }
     }
     if (!empty($line_to_insert_later)) {
         // insert the moved item in the correct spot
         // $arg1 contains the hash the item to insert before/after
         // $arg2 contains the instance
         $content_array = $updated_content_array;
         $updated_content_array = array();
         $this->clear_line_hashes();
         foreach ((array) $content_array as $content_line) {
             // get the line's hash and hash instance
             $line_hash = self::hash_line($content_line);
             $instance = $this->add_line_hash($line_hash);
             // is it the hash and hash instance we're looking for?
             if ($line_hash == $arg1 && $instance == $arg2) {
                 if ('moveAfter' == $command) {
                     $updated_content_array[] = $content_line;
                     $updated_content_array[] = $line_to_insert_later;
                 } elseif ('moveBefore' == $command) {
                     $updated_content_array[] = $line_to_insert_later;
                     $updated_content_array[] = $content_line;
                 }
             } else {
                 // just append and move on
                 $updated_content_array[] = $content_line;
             }
         }
     }
     $updated_content = implode("\n", $updated_content_array);
     $updated_content = $this->restore_text($updated_content);
     // update the object in the database
     $updated_object = $this->update_object_content($object_type, $object_ID, $updated_content);
     $fragment = o2_Fragment::get_fragment($updated_object);
     if (empty($fragment)) {
         wp_send_json_error(array('errorText' => 'Internal error.  Empty fragment.  (o2 List Creator)'));
     }
     $partial_fragment = array_intersect_key($fragment, $this->key_whitelist);
     wp_send_json_success($partial_fragment);
 }
Ejemplo n.º 3
0
 /**
  * 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));
 }
Ejemplo n.º 4
0
 /**
  * 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));
 }
Ejemplo n.º 5
0
 /**
  * Embed JSONified post+comment data into each thread (post) for backbone consumption
  */
 public static function add_json_data($content)
 {
     global $post;
     // password protected post? return immediately (password protected pages are OK)
     if (!is_page() && !empty($post->post_password)) {
         return $content;
     }
     $conversation = array();
     if (is_single() || is_category() || is_archive() || is_author() || is_home() || is_page() || is_search()) {
         $conversation[] = o2_Fragment::get_fragment($post, array('find-adjacent' => is_single()));
         // Append the encoded conversation to the content in a hidden script block
         $content .= "<script class='o2-data' id='o2-data-{$post->ID}' data-post-id='{$post->ID}' type='application/json' style='display:none'>";
         $content .= json_encode($conversation);
         $content .= "</script>\n";
     }
     return $content;
 }
Ejemplo n.º 6
0
 /**
  * Returns an entry array for the specified conversation fragment
  * where fragment_type is 'post' or 'comment'
  */
 public static function get_fragment_from_post($my_post, $args = array())
 {
     remove_filter('the_content', array('o2', 'add_json_data'), 999999);
     // Avoid infinite loops
     remove_filter('the_excerpt', array('o2', 'add_json_data'), 999999);
     // Avoid infinite loops
     add_filter('home_url', array('o2_Fragment', 'home_url'), 10, 4);
     $post_ID = $my_post->ID;
     $post_tags = wp_get_post_tags($post_ID);
     $permalink = get_permalink($post_ID);
     // Get a set of classes to be used when displaying.
     // We force ->post_type because it's not included in core during
     // AJAX calls (is_admin())
     $post_class_array = get_post_class($my_post->post_type, $post_ID);
     $post_class = !empty($post_class_array);
     if ($post_class) {
         $post_class = join(' ', $post_class_array);
     }
     // Adjacent post information (prev next) only makes sense for some pages (e.g. is_single)
     // So, we only fetch adjacent post information if we were asked to (to avoid unnecessary db queries)
     $prev_post_title = '';
     $prev_post_url = '';
     $has_prev_post = false;
     $next_post_title = '';
     $next_post_url = '';
     $has_next_post = false;
     if (isset($args['find-adjacent']) && $args['find-adjacent']) {
         // We can't use the loop because get_fragment_from_post could be called outside of the loop (e.g. by a ajax request from backbone)
         // Set global so that core functions work as expected
         global $post;
         $old_post = $post;
         $post = $my_post;
         // temporarily add filters to avoid picking up password protected posts
         add_filter('get_previous_post_where', array('o2_Fragment', 'get_adjacent_post_where'));
         add_filter('get_next_post_where', array('o2_Fragment', 'get_adjacent_post_where'));
         $prev_post = get_previous_post();
         $has_prev_post = !empty($prev_post);
         if ($has_prev_post) {
             $prev_post_title = $prev_post->post_title;
             $prev_post_title = apply_filters('the_title', $prev_post_title);
             $prev_post_url = get_permalink($prev_post->ID);
         }
         $next_post = get_next_post();
         $has_next_post = !empty($next_post);
         if ($has_next_post) {
             $next_post_title = $next_post->post_title;
             $next_post_title = apply_filters('the_title', $next_post_title);
             $next_post_url = get_permalink($next_post->ID);
         }
         remove_filter('get_previous_post_where', array('o2_Fragment', 'get_adjacent_post_where'));
         remove_filter('get_next_post_where', array('o2_Fragment', 'get_adjacent_post_where'));
         // Set global scope back to whatever it was before
         $post = $old_post;
     }
     $raw_post_title = $my_post->post_title;
     $raw_content = $my_post->post_content;
     $extended_content = get_extended($raw_content);
     $title_was_generated_from_content = $raw_post_title == wp_trim_words($raw_content, 5);
     $post_format = get_post_format($post_ID);
     if (false === $post_format) {
         $post_format = 'standard';
     }
     // Filter the title
     $filtered_post_title = apply_filters('the_title', $raw_post_title);
     // Handle <!--more--> for same-page toggling
     global $more;
     if (!$more) {
         if (!empty($extended_content['extended'])) {
             // Set more text
             if (empty($extended_content['more_text'])) {
                 $extended_content['more_text'] = __('Show full post', 'o2');
             }
             // Set more link
             $more_text = strip_tags(wp_kses_no_null(trim($extended_content['more_text'])));
             $more_link = apply_filters('the_content_more_link', "<a href='{$permalink}#more-{$post_ID}' class='more-link'>{$more_text}</a>", $more_text);
             $extended_content = $extended_content['main'] . "\n\n" . $more_link . "\n<div class='o2-extended-more'>\n\n" . $extended_content['extended'] . "\n\n</div><!--.o2-extended-more-->\n";
             // No <!--more--> text
         } else {
             $extended_content = $extended_content['main'];
         }
     } else {
         $extended_content = $raw_content;
     }
     // When editing the content, SyntaxHighlighter does some magic to decode
     // HTML entities within [code] blocks.
     global $SyntaxHighlighter;
     if (is_a($SyntaxHighlighter, 'SyntaxHighlighter')) {
         $raw_content = $SyntaxHighlighter->decode_shortcode_contents($raw_content);
     }
     // An xpost is already filtered content
     $xpost_original_permalink = get_post_meta($post_ID, '_xpost_original_permalink', true);
     if (!empty($xpost_original_permalink)) {
         $filtered_content = $extended_content;
     } else {
         add_filter('the_content', 'o2_Fragment::o2_make_clickable', 9);
         $filtered_content = apply_filters('the_content', $extended_content);
         remove_filter('the_content', 'o2_Fragment::o2_make_clickable', 9);
     }
     $filtered_content = apply_filters('o2_filtered_content', $filtered_content, $my_post);
     // Mentions
     list($mentions, $mention_context) = self::get_mention_data('post', $post_ID, $raw_content);
     $comment_models = array();
     $approved_comments = get_comments(array('post_id' => $post_ID, 'order' => 'ASC', 'status' => 'approve'));
     $trashed_comments = get_comments(array('post_id' => $post_ID, 'order' => 'ASC', 'status' => 'trash', 'meta_query' => array(array('key' => 'o2_comment_has_children', 'compare' => 'EXISTS'))));
     /*
      * For bootstrapping data, we are only interested in approved comments and
      * trashed comments that have children for now.
      */
     $post_comments = array_merge($approved_comments, $trashed_comments);
     foreach ((array) $post_comments as $post_comment) {
         $comment_models[] = o2_Fragment::get_fragment($post_comment);
     }
     // Set a post date for previewing drafts
     $post_date_gmt = $my_post->post_date_gmt;
     if ('0000-00-00 00:00:00' === $post_date_gmt) {
         $post_date_gmt = $my_post->post_modified_gmt;
     }
     $modified = get_post_meta($post_ID, 'client-modified', true);
     if (!$modified) {
         $modified = strtotime($my_post->post_modified_gmt);
     }
     $post_actions = o2_get_post_actions($post_ID);
     $comments_open = comments_open($post_ID);
     // Password protected page?  Check the password before delivering the content
     $is_page = "page" == get_post_type($post_ID);
     $is_password_protected = !empty($my_post->post_password);
     if ($is_page && $is_password_protected && post_password_required($post_ID)) {
         $post_actions = array();
         $comment_models = array();
         $comments_open = false;
         $raw_content = "";
         $filtered_content = get_the_password_form($post_ID);
     }
     $fragment = array('type' => 'post', 'id' => $post_ID, 'postID' => $post_ID, 'cssClasses' => $post_class, 'parentID' => 0, 'titleRaw' => $raw_post_title, 'titleFiltered' => $filtered_post_title, 'titleWasGeneratedFromContent' => $title_was_generated_from_content, 'contentRaw' => $raw_content, 'contentFiltered' => $filtered_content, 'permalink' => $permalink, 'unixtime' => strtotime($post_date_gmt), 'unixtimeModified' => (int) $modified, 'entryHeaderMeta' => '', 'linkPages' => '', 'footerEntryMeta' => '', 'tagsRaw' => self::get_post_tags_raw($post_tags), 'tagsArray' => self::get_post_tags_array($post_tags), 'loginRedirectURL' => wp_login_url($permalink), 'hasPrevPost' => $has_prev_post, 'prevPostTitle' => $prev_post_title, 'prevPostURL' => $prev_post_url, 'hasNextPost' => $has_next_post, 'nextPostTitle' => $next_post_title, 'nextPostURL' => $next_post_url, 'commentsOpen' => $comments_open, 'is_xpost' => !empty($xpost_original_permalink), 'editURL' => get_edit_post_link($post_ID), 'postActions' => $post_actions, 'comments' => $comment_models, 'postFormat' => $post_format, 'postMeta' => array(), 'postTerms' => self::get_post_terms($post_ID), 'pluginData' => array(), 'isPage' => "page" == get_post_type($post_ID), 'mentions' => $mentions, 'mentionContext' => strip_tags($mention_context), 'isTrashed' => "trash" == get_post_status($post_ID));
     // Get author properties (and bootstrap the rest of the model)
     // e.g. userLogin
     $post_user_properties = self::get_post_user_properties($my_post);
     $fragment = array_merge($fragment, $post_user_properties);
     // @todo - add filter to move theme specific attributes out of here, and allow themes to add more attributes
     $fragment = apply_filters('o2_post_fragment', $fragment, $post_ID);
     // as they filter the templates
     add_filter('the_content', array('o2', 'add_json_data'), 999999);
     add_filter('the_excerpt', array('o2', 'add_json_data'), 999999);
     remove_filter('home_url', array('o2_Fragment', 'home_url'), 10, 4);
     // Force UTF8 to avoid JSON encode issues
     $fragment = self::to_utf8($fragment);
     return $fragment;
 }