760 lines
26 KiB
PHP
760 lines
26 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Posts and Comments are all represented as "Fragments" within o2.
|
|
*/
|
|
class o2_Fragment {
|
|
|
|
/**
|
|
* Figures out if $data looks like a post or a comment, and gives you
|
|
* a Fragment either way.
|
|
*/
|
|
public static function get_fragment( $data, $args = array() ) {
|
|
if ( !is_object( $data ) )
|
|
return false;
|
|
|
|
if ( property_exists( $data, 'post_author' ) ) {
|
|
return self::get_fragment_from_post( $data, $args );
|
|
} else if ( property_exists( $data, 'comment_ID' ) ) {
|
|
return self::get_fragment_from_comment( $data );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Used in get_fragment_from_posts, avoids us picking up password protected posts
|
|
* in prev / next post navigation
|
|
*/
|
|
public static function get_adjacent_post_where( $where ) {
|
|
return $where . " AND ( p.post_password IS NULL OR p.post_password = '' )";
|
|
}
|
|
|
|
/**
|
|
* 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 = self::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->ID );
|
|
$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->ID );
|
|
$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, $my_post->ID );
|
|
|
|
// 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";
|
|
|
|
// If the post has more content, we need to go through the full `the_content` filter again
|
|
if ( isset( $args['the_content'] ) ) {
|
|
unset( $args['the_content'] );
|
|
}
|
|
|
|
// 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;
|
|
} elseif ( isset( $args['the_content'] ) ) {
|
|
$filtered_content = make_clickable( $args['the_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', /* fragment type - not to be confused with get_post_type */
|
|
'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 ), // @todo Put a login URL in o2.options and then add redirect via JS
|
|
'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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public static function get_depth_for_comment( $my_comment ) {
|
|
$depth = 1;
|
|
while ( $my_comment && 0 < $my_comment->comment_parent ) {
|
|
$depth = $depth + 1;
|
|
$my_comment = get_comment( $my_comment->comment_parent );
|
|
}
|
|
return $depth;
|
|
}
|
|
|
|
/**
|
|
* get_current_user_properties returns
|
|
* userLogin, userNicename, canEditPosts, canEditOthersPosts, and canPublishPosts for logged in user
|
|
* OR
|
|
* noprivUserName, noprivUserHash and noprivUserURL for nopriv user
|
|
*
|
|
* For logged in users, it also caches a complete user model for bootstrapping
|
|
*/
|
|
|
|
public static function get_current_user_properties() {
|
|
$current_user = wp_get_current_user();
|
|
|
|
if ( $current_user instanceof WP_User ) {
|
|
$user_id = $current_user->ID;
|
|
|
|
if ( 0 != $user_id ) {
|
|
$user_data = get_userdata( $user_id );
|
|
|
|
self::add_to_user_bootstrap( $user_data );
|
|
|
|
$return_result = array(
|
|
'userLogin' => $user_data->user_login,
|
|
'userNicename' => $user_data->user_nicename,
|
|
'noprivUserName' => '',
|
|
'noprivUserHash' => '',
|
|
'noprivUserURL' => '',
|
|
'canEditPosts' => user_can( $user_id, 'edit_posts' ),
|
|
'canEditOthersPosts' => user_can( $user_id, 'edit_others_posts' ),
|
|
'canPublishPosts' => user_can( $user_id, 'publish_posts' )
|
|
);
|
|
|
|
return $return_result;
|
|
} // if 0 != $user_id
|
|
}
|
|
|
|
// Try to get unlogged-in user details from cookie
|
|
$commenter = wp_get_current_commenter();
|
|
|
|
$return_result = array(
|
|
'userLogin' => '',
|
|
'userNicename' => '',
|
|
'noprivUserName' => esc_attr( $commenter['comment_author'] ),
|
|
'noprivUserEmail' => esc_attr( $commenter['comment_author_email'] ),
|
|
'noprivUserURL' => esc_url( $commenter['comment_author_url'] ),
|
|
'canEditPosts' => false,
|
|
'canEditOthersPosts' => false,
|
|
'canPublishPosts' => false
|
|
);
|
|
|
|
return $return_result;
|
|
}
|
|
|
|
public static function add_to_user_bootstrap( $user_data ) {
|
|
global $o2_userdata;
|
|
if ( ! is_array( $o2_userdata ) ) {
|
|
$o2_userdata = array();
|
|
}
|
|
|
|
$user_login = $user_data->user_login;
|
|
|
|
if ( ! isset( $o2_userdata[$user_login] ) ) {
|
|
$bootstrap_model = self::get_model_from_userdata( $user_data );
|
|
|
|
$o2_userdata[$user_login] = $bootstrap_model;
|
|
}
|
|
}
|
|
|
|
public static function get_model_from_userdata( $user_data ) {
|
|
$bootstrap_model = array(
|
|
'id' => $user_data->ID,
|
|
'type' => 'user',
|
|
'userLogin' => $user_data->user_login,
|
|
'userNicename' => $user_data->user_nicename,
|
|
'displayName' => $user_data->display_name,
|
|
'firstName' => $user_data->user_firstname,
|
|
'lastName' => $user_data->user_lastname,
|
|
'url' => get_author_posts_url( $user_data->ID ),
|
|
'urlTitle' => sprintf( __( 'Posts by %1$s (%2$s)', 'o2' ), $user_data->display_name, '@' . $user_data->user_nicename ),
|
|
'hash' => md5( strtolower( trim( $user_data->user_email ) ) ),
|
|
'modelClass' => '' // empty or o2-user-model-incomplete
|
|
);
|
|
|
|
$bootstrap_model = apply_filters( 'o2_user_model', $bootstrap_model, $user_data );
|
|
|
|
// Force UTF8 to avoid JSON encode issues
|
|
$bootstrap_model = self::to_utf8( $bootstrap_model );
|
|
|
|
return $bootstrap_model;
|
|
}
|
|
|
|
/**
|
|
* get_post_user_properties returns the userLogin for the author and
|
|
* bootstraps the rest of the user info
|
|
*/
|
|
|
|
public static function get_post_user_properties( $_post ) {
|
|
if ( ! $_post->post_author ) {
|
|
return array(
|
|
'userLogin' => '',
|
|
'userNicename' => ''
|
|
);
|
|
}
|
|
|
|
$user_id = $_post->post_author;
|
|
$user_data = get_userdata( $user_id );
|
|
self::add_to_user_bootstrap( $user_data );
|
|
|
|
$return_result = array(
|
|
'userLogin' => $user_data->user_login,
|
|
'userNicename' => $user_data->user_nicename
|
|
);
|
|
|
|
return $return_result;
|
|
}
|
|
|
|
public static function get_comment_author_properties( $_comment ) {
|
|
$return_result = array();
|
|
$user_id = $_comment->user_id;
|
|
|
|
if ( 0 != $user_id ) { // logged in commentor
|
|
$user_data = get_userdata( $user_id );
|
|
self::add_to_user_bootstrap( $user_data );
|
|
|
|
$return_result = array(
|
|
'userLogin' => $user_data->user_login,
|
|
'userNicename' => $user_data->user_nicename
|
|
);
|
|
} else { // no priv commentor
|
|
$comment_author_email_hash = !empty( $_comment->comment_author_email ) ? md5( strtolower( trim( $_comment->comment_author_email ) ) ) : '00000000000000000000000000000000';
|
|
|
|
$return_result = array(
|
|
'noprivUserName' => $_comment->comment_author,
|
|
'noprivUserHash' => $comment_author_email_hash,
|
|
'noprivUserURL' => $_comment->comment_author_url
|
|
);
|
|
}
|
|
|
|
return $return_result;
|
|
}
|
|
|
|
/*
|
|
* Returns a raw string of comma delimited tags, suitable for editing (e.g. tag1, tag2, tag3)
|
|
*/
|
|
public static function get_post_tags_raw( $post_tags ) {
|
|
$tag_names = array();
|
|
$result = '';
|
|
|
|
foreach ( (array) $post_tags as $tag ) {
|
|
$tag_names[] = $tag->name;
|
|
}
|
|
|
|
$result = implode( ', ', $tag_names );
|
|
|
|
return $result;
|
|
}
|
|
|
|
/*
|
|
* Returns an array of tags w/ properties, suitable for template driven rendering
|
|
*/
|
|
|
|
public static function get_post_tags_array( $post_tags ) {
|
|
$result = array();
|
|
|
|
foreach ( (array) $post_tags as $tag ) {
|
|
$result[] = array(
|
|
"label" => $tag->name,
|
|
"count" => $tag->count,
|
|
"link" => get_tag_link( $tag->term_id )
|
|
);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Wrapper for wp_get_post_tags() with static cache.
|
|
*/
|
|
public static function get_post_tags( $post_id ) {
|
|
static $post_tags_cache = array();
|
|
|
|
if ( ! isset( $post_tags_cache[ $post_id ] ) ) {
|
|
$post_tags_cache[ $post_id ] = wp_get_post_tags( $post_id );
|
|
}
|
|
|
|
return $post_tags_cache[ $post_id ];
|
|
}
|
|
|
|
/*
|
|
* Returns an array of the posts's taxonomy terms
|
|
*/
|
|
public static function get_post_terms( $post_id ) {
|
|
$post_type = get_post_type( $post_id );
|
|
$taxonomies = get_taxonomies( array(
|
|
'object_type' => array( $post_type ),
|
|
'_builtin' => true,
|
|
) );
|
|
$taxonomies = apply_filters( 'o2_get_post_terms_taxonomies', $taxonomies, $post_id, $post_type );
|
|
|
|
static $post_terms_cache = array();
|
|
|
|
$cache_key = md5( $post_id . serialize( $taxonomies ) );
|
|
if ( ! isset( $post_terms_cache[ $cache_key ] ) ) {
|
|
$retval = array();
|
|
foreach ( $taxonomies as $taxonomy ) {
|
|
$retval[ $taxonomy ] = array();
|
|
$terms = wp_get_post_terms( $post_id, $taxonomy );
|
|
foreach ( $terms as $term ) {
|
|
$arr = array(
|
|
'label' => $term->name,
|
|
'count' => $term->count,
|
|
);
|
|
$link = get_term_link( $term );
|
|
if ( ! is_wp_error( $link ) )
|
|
$arr['link'] = $link;
|
|
$retval[ $taxonomy ][] = $arr;
|
|
}
|
|
}
|
|
$post_terms_cache[ $cache_key ] = $retval;
|
|
} else {
|
|
$retval = $post_terms_cache[ $cache_key ];
|
|
}
|
|
|
|
$retval = apply_filters( 'o2_get_post_terms', $retval, $post_id, $taxonomies );
|
|
return $retval;
|
|
}
|
|
|
|
/*
|
|
* Returns a list of an array of the mentions within the content, as well as the mention context fragment
|
|
* @param string $object_type The object type: post or comment
|
|
* @param int $object_id The object id
|
|
* @param string $object_content The object content
|
|
* @return mixed
|
|
*/
|
|
public static function get_mention_data( $object_type, $object_id, $object_content ) {
|
|
$mentions = array();
|
|
$mention_context = '';
|
|
if ( class_exists( 'Jetpack_Mentions' ) ) {
|
|
if ( 'post' === $object_type ) {
|
|
$mentions = Jetpack_Mentions::get_post_mentions( $object_id );
|
|
} else {
|
|
$mentions = Jetpack_Mentions::get_comment_mentions( $object_id );
|
|
}
|
|
|
|
// Get mention context fragment for new or polled objects
|
|
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
|
|
$current_user = wp_get_current_user();
|
|
if ( $current_user instanceof WP_User && in_array( $current_user->user_login, $mentions ) ) {
|
|
$mention_context = Jetpack_Inline_Terms::find_term_context( "@{$current_user->user_login}", $object_content );
|
|
}
|
|
}
|
|
} else {
|
|
// @todo Internal mentions library for o2
|
|
}
|
|
|
|
return array( $mentions, $mention_context );
|
|
}
|
|
|
|
public static function to_utf8( $var ) {
|
|
if ( is_object( $var ) || is_array( $var ) ) {
|
|
foreach ( $var as $key => $val ) {
|
|
$var[ $key ] = self::to_utf8( $val );
|
|
}
|
|
} else if ( is_string( $var ) ) {
|
|
$var = mb_convert_encoding( $var, 'UTF-8' );
|
|
}
|
|
|
|
return $var;
|
|
}
|
|
|
|
/**
|
|
* Update post for polling (without creating a revision)
|
|
* Useful for sticky state change, resolved state change, etc.
|
|
*/
|
|
public static function bump_post( $post_id, $post_content = '' ) {
|
|
// We need to pass post_content ourselves to wp_update_post
|
|
// to avoid it re-encoding already encoded content (e.g. < -> &lt;)
|
|
// when "bumping" a post
|
|
if ( empty( $post_content ) ) {
|
|
$post = get_post( $post_id );
|
|
$post_content = $post->post_content;
|
|
}
|
|
$post_content = htmlspecialchars_decode( $post_content, ENT_QUOTES );
|
|
$post_content = apply_filters( 'o2_bump_post_content', $post_content, $post_id );
|
|
|
|
// We don't want to create a revision, so remove the wp_save_post_revision post_updated action
|
|
// before updating the post
|
|
$action_tag = 'post_updated';
|
|
$action_func = 'wp_save_post_revision';
|
|
$save_revision_hook_priority = has_action( $action_tag, $action_func );
|
|
if ( $save_revision_hook_priority ) {
|
|
remove_action( $action_tag, $action_func, $save_revision_hook_priority );
|
|
}
|
|
wp_update_post( array( 'ID' => $post_id, 'post_content' => $post_content ) );
|
|
update_post_meta( $post_id, 'client-modified', time() );
|
|
if ( $save_revision_hook_priority ) {
|
|
add_action( $action_tag, $action_func, $save_revision_hook_priority );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Will update the comment modified time to reflect the current time for polling.
|
|
*
|
|
* @param $comment_ID
|
|
*/
|
|
public static function bump_comment_modified_time( $comment_ID ) {
|
|
// We want to leave the comment_date alone and update/create a comment
|
|
// meta entry instead here - o2_comment_gmt_modified (numeric, timestamp, UTC)
|
|
update_comment_meta( $comment_ID, 'o2_comment_gmt_modified', current_time( 'timestamp', true ) );
|
|
}
|
|
|
|
/*
|
|
* Filter the home_url() for permalinks generated during AJAX calls.
|
|
* When is_admin() is true, permalinks will not reflect the protocol
|
|
* of the ajax_url.
|
|
*/
|
|
public static function home_url( $url, $path, $orig_scheme, $blog_id ) {
|
|
if ( is_ssl() && 0 === stripos( $url, 'http://' ) ) {
|
|
$url = 'https://' . substr( $url, 7 );
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
public static function o2_make_clickable( $content ) {
|
|
// make urls clickable, but exclude text within square brackets - we don't want make_clickable
|
|
// processing between brackets because it messes up not-yet-filtered shortcodes that have url
|
|
// attributes like googlemaps
|
|
|
|
$regex = '/(\[[^\]]*])/i';
|
|
|
|
$placeholders = $matches = array();
|
|
|
|
while ( preg_match( $regex, $content, $matches ) ) {
|
|
do {
|
|
$hash = wp_generate_password( 36, false );
|
|
} while( array_key_exists( $hash, $placeholders ) );
|
|
$placeholder = ":: o2-make-clickable-placeholder $hash ::";
|
|
$placeholders[ $hash ] = $matches[0];
|
|
$content = preg_replace( $regex, $placeholder, $content, 1 );
|
|
}
|
|
|
|
$content = make_clickable( $content );
|
|
|
|
foreach ( $placeholders as $hash => $original ) {
|
|
$placeholder = ":: o2-make-clickable-placeholder $hash ::";
|
|
$content = str_replace( $placeholder, $original, $content );
|
|
}
|
|
|
|
return wp_kses_post( $content );
|
|
}
|
|
}
|