Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
367 lines (286 sloc) 15.2 KB
<?php
if( basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']) )
die();
class ScoperTeaser {
// Manipulate the results set in various ways to prepare it for teaser filtering
// Determine which listed items are readabled (i.e. will not be teased). Clear private status so teased items will not be hidden completely or trigger a 404
function posts_teaser_prep_results($results, $tease_otypes, $args = '') {
$defaults = array('user' => '', 'use_object_roles' => -1, 'use_term_roles' => -1, 'request' => '', 'object_type' => '' );
$args = array_merge( $defaults, (array) $args );
extract($args);
global $wpdb, $scoper, $wp_query;
global $query_interceptor;
if ( ! $results )
return $results;
if ( did_action('wp_meta') && ! did_action('wp_head') )
return $results;
if ( empty($request) ) {
// TODO: teaser logs last_request itself
global $query_interceptor;
if ( empty ($query_interceptor->last_request['post']) ) {
// try to get it from wpdb instead
if ( ! empty($wpdb->last_query) )
$request = $wpdb->last_query;
else {
// don't risk exposing hidden content if something goes wrong with query logging
return array();
}
} else
$request = $query_interceptor->last_request['post'];
}
if ( strpos( $request, 'WHERE 1=2' ) ) {
$ids = array();
foreach( array_keys($results) as $key ) {
$ids []= $results[$key]->ID;
}
$request = "SELECT * FROM $wpdb->posts WHERE ID IN ('" . implode("','", $ids) . "')";
}
// Pagination could be broken by subsequent query for filtered ids, so buffer current paging parameters
// ( this code mimics WP_Query::get_posts() )
if ( ! empty( $wp_query->query_vars['posts_per_page'] ) ) {
$found_posts_query = apply_filters( 'found_posts_query', 'SELECT FOUND_ROWS()' );
$buffer_found_posts = $wpdb->get_var( $found_posts_query );
if ( $buffer_found_posts >= $wp_query->query_vars['posts_per_page'] ) {
$restore_pagination = true;
$buffer_found_posts = apply_filters( 'found_posts', $buffer_found_posts );
}
}
$list_private = array();
if ( awp_ver( '3.0' ) )
$private_stati = get_post_stati( array( 'private' => true ) );
else
$private_stati = array( 'private' );
if ( is_single() || is_page() ) {
$maybe_fudge_private = true;
$maybe_strip_private = false;
} else {
$maybe_strip_private = true;
$maybe_fudge_private = false;
}
if ( ! is_object($user) ) {
global $current_user;
$user = $current_user;
}
// don't risk exposing hidden content if there is a problem with query parsing
if ( ! $pos = strpos(strtoupper($request), " FROM") )
return array();
$distinct = ( stripos( $request, " DISTINCT " ) ) ? 'DISTINCT' : ''; // RS does not add any joins, but if DISTINCT clause exists in query, retain it
$request = "SELECT $distinct {$wpdb->posts}.ID " . substr($request, $pos);
if ( $limitpos = strpos($request, ' LIMIT ') )
$request = substr($request, 0, $limitpos);
$args['skip_teaser'] = true;
$filtered_request = $query_interceptor->flt_objects_request($request, 'post', $object_type, $args);
global $scoper_teaser_filtered_ids;
$scoper_teaser_filtered_ids = scoper_get_col($filtered_request);
if ( ! isset($scoper->teaser_ids) )
$scoper->teaser_ids = array();
$hide_ungranted_private = array();
foreach ( $tease_otypes as $object_type ) {
$hide_ungranted_private[$object_type] = scoper_get_otype_option('teaser_hide_private', 'post', $object_type);
}
foreach ( array_keys($results) as $key ) {
if ( is_array($results[$key]) )
$id = $results[$key]['ID'];
else
$id = $results[$key]->ID;
if ( ! $scoper_teaser_filtered_ids || ! in_array($id, $scoper_teaser_filtered_ids) ) {
if ( isset($results[$key]->post_type) )
$object_type = $results[$key]->post_type;
else
$object_type = $scoper->data_sources->get_from_db('type', 'post', $id);
if ( ! in_array($object_type, $tease_otypes) )
continue;
// Defeat a WP core secondary safeguard so we can apply the teaser message rather than 404
if ( in_array( $results[$key]->post_status, $private_stati ) ) {
// don't want the teaser message (or presence in category archive listing) if we're hiding a page from listing
$type_obj = get_post_type_object( $object_type );
if ( $type_obj && $type_obj->hierarchical ) { // TODO: review implementation of this option with custom types
if ( ! isset($list_private[$object_type]) )
$list_private[$object_type] = scoper_get_otype_option( 'private_items_listable', 'post', 'page' );
} else
$list_private[$object_type] = true;
if ( $hide_ungranted_private[$object_type] || ( $maybe_strip_private && ! $list_private[$object_type] ) ) {
$need_reindex = true;
unset ( $results[$key] );
// Actually, don't do this because the current method of removing private items from the paged result set will not move items from one result page to another
//$buffer_found_posts--; // since we're removing this item from the teased results, decrement the paging total
continue;
} elseif ( ! empty( $maybe_fudge_private ) && $list_private[$object_type] ) {
$results[$key]->post_status = 'publish';
}
}
}
}
if ( ! empty($need_reindex) ) // re-index the array so paging isn't confused
$results = array_values($results);
// pagination could be broken by the filtered ids query performed in this function, so original paging parameters were buffered
if ( ! empty($restore_pagination) ) {
// WP query will apply found_posts filter shortly after this function returns. Feed it the buffered value from original unfiltered results.
// Static flag in created function ensures it is only applied once.
$func_name = create_function( '$a', 'static $been_here; if ( ! empty($been_here) ) return $a; else {$been_here = true; ' . "return $buffer_found_posts;}" );
add_filter( 'found_posts', $func_name, 1);
}
return $results;
}
// apply teaser modifications to the recordset. Note: this is applied later than
function posts_teaser($results, $tease_otypes, $args = '') {
$defaults = array('user' => '', 'use_object_roles' => -1, 'use_term_roles' => -1, 'request' => '' );
$args = array_merge( $defaults, (array) $args );
extract($args);
global $wpdb, $scoper, $wp_query;
if ( did_action('wp_meta') && ! did_action('wp_head') )
return $results;
if ( ! is_object($user) ) {
global $current_user;
$user = $current_user;
}
$teaser_replace = array();
$teaser_prepend = array();
$teaser_append = array();
foreach ( $tease_otypes as $object_type ) {
$teaser_replace[$object_type]['post_content'] = ScoperTeaser::get_teaser_text( 'replace', 'content', 'post', $object_type, $user );
$teaser_prepend[$object_type]['post_content'] = ScoperTeaser::get_teaser_text( 'prepend', 'content', 'post', $object_type, $user );
$teaser_append[$object_type]['post_content'] = ScoperTeaser::get_teaser_text( 'append', 'content', 'post', $object_type, $user );
$teaser_replace[$object_type]['post_excerpt'] = ScoperTeaser::get_teaser_text( 'replace', 'excerpt', 'post', $object_type, $user );
$teaser_prepend[$object_type]['post_excerpt'] = ScoperTeaser::get_teaser_text( 'prepend', 'excerpt', 'post', $object_type, $user );
$teaser_append[$object_type]['post_excerpt'] = ScoperTeaser::get_teaser_text( 'append', 'excerpt', 'post', $object_type, $user );
$teaser_prepend[$object_type]['post_title'] = ScoperTeaser::get_teaser_text( 'prepend', 'name', 'post', $object_type, $user );
$teaser_append[$object_type]['post_title'] = ScoperTeaser::get_teaser_text( 'append', 'name', 'post', $object_type, $user );
}
global $scoper_teaser_filtered_ids;
if ( ! isset($scoper_teaser_filtered_ids) )
self::posts_teaser_prep_results( $results, $tease_otypes, $args );
if ( ! isset($scoper->teaser_ids) )
$scoper->teaser_ids = array();
$excerpt_teaser = array();
$more_teaser = array();
$x_chars_teaser = array();
foreach ( $tease_otypes as $object_type ) {
$teaser_type = scoper_get_otype_option( 'use_teaser', 'post', $object_type );
if ( 'excerpt' == $teaser_type )
$excerpt_teaser[$object_type] = true;
elseif ( 'more' == $teaser_type ) {
$excerpt_teaser[$object_type] = true;
$more_teaser[$object_type] = true;
} elseif ( 'x_chars' == $teaser_type ) {
$excerpt_teaser[$object_type] = true;
$more_teaser[$object_type] = true;
$x_chars_teaser[$object_type] = true;
}
}
// strip content from all $results rows not in $items
$args = array( 'teaser_prepend' => $teaser_prepend, 'teaser_append' => $teaser_append, 'teaser_replace' => $teaser_replace,
'excerpt_teaser' => $excerpt_teaser, 'more_teaser' => $more_teaser, 'x_chars_teaser' => $x_chars_teaser );
foreach ( array_keys($results) as $key ) {
if ( is_array($results[$key]) )
$id = $results[$key]['ID'];
else
$id = $results[$key]->ID;
if ( ! $scoper_teaser_filtered_ids || ! in_array($id, $scoper_teaser_filtered_ids) ) {
if ( isset($results[$key]->post_type) )
$object_type = $results[$key]->post_type;
else
$object_type = $scoper->data_sources->get_from_db('type', 'post', $id);
if ( ! in_array($object_type, $tease_otypes) )
continue;
ScoperTeaser::apply_teaser( $results[$key], 'post', $object_type, $args );
}
}
return $results;
}
function get_teaser_text( $teaser_operation, $variable, $src_name, $object_type, $user = '' ) {
if ( ! is_object($user) ) {
global $current_user;
$user = $current_user;
}
$anon = ( $user->ID == 0 ) ? '_anon' : '';
if ( $msg = scoper_get_otype_option( "teaser_{$teaser_operation}_{$variable}{$anon}", 'post', $object_type, CURRENT_ACCESS_NAME_RS) ) {
if ( defined('SCOPER_TRANSLATE_TEASER') ) {
scoper_load_textdomain(); // otherwise this is only loaded for wp-admin
$msg = translate( $msg, 'scoper');
if ( ! empty($msg) && ! is_null($msg) && is_string($msg) )
$msg = htmlspecialchars_decode( $msg );
}
return $msg;
}
}
function apply_teaser( &$object, $src_name, $object_type, $args = '' ) {
$defaults = array( 'col_excerpt' => '', 'col_content' => '', 'excerpt_teaser' => '', 'col_id' => '',
'teaser_prepend' => '', 'teaser_append' => '', 'teaser_replace' => '', 'more_teaser' => '',
'x_chars_teaser' => '' );
$args = array_merge( $defaults, (array) $args );
extract($args);
global $scoper;
if ( is_array($object) )
$id = $object['ID'];
else
$id = $object->ID;
$object->scoper_teaser = true;
$scoper->teaser_ids['post'][$id] = true;
if ( ! empty( $object->post_password ) ) {
$excerpt_teaser[$object_type] = false;
$more_teaser[$object_type] = false;
$x_chars_teaser[$object_type] = false;
}
if ( ! empty($x_chars_teaser[$object_type]) )
$num_chars = ( defined('SCOPER_TEASER_NUM_CHARS') ) ? SCOPER_TEASER_NUM_CHARS : 50;
// Content replacement mode is applied in the following preference order:
// 1. Custom excerpt, if available and if selected teaser mode is "excerpt", "excerpt or more", or "excerpt, pre-more or first x chars"
// 2. Pre-more content, if applicable and if selected teaser mode is "excerpt or more", or "excerpt, pre-more or first x chars"
// 3. First X Characters (defined by SCOPER_TEASER_NUM_CHARS), if total content is longer than that and selected teaser mode is "excerpt, pre-more or first x chars"
$teaser_set = false;
$use_excerpt_suffix = true;
// optionally, use post excerpt as the hidden content teaser instead of a fixed replacement
if ( ! empty($excerpt_teaser[$object_type]) && ! empty($object->post_excerpt) ) {
$object->post_content = $object->post_excerpt;
} elseif ( ! empty($more_teaser[$object_type]) && ( $more_pos = strpos($object->post_content, '<!--more-->') ) ) {
$object->post_content = substr( $object->post_content, 0, $more_pos + 11 );
$object->post_excerpt = $object->post_content;
if ( is_single() || is_page() )
$object->post_content .= '<p class="scoper_more_teaser">' . $teaser_replace[$object_type]['post_content'] . '</p>';
// since no custom excerpt or more tag is stored, use first X characters as teaser - but only if the total length is more than that
} elseif ( ! empty($x_chars_teaser[$object_type]) && ! empty($object->post_content) && ( strlen( strip_tags($object->post_content) ) > $num_chars ) ) {
scoper_load_textdomain(); // otherwise this is only loaded for wp-admin
// since we are stripping out img tag, also strip out image caption applied by WP
$object->post_content = preg_replace( "/\[caption.*\]/", '', $object->post_content );
$object->post_content = str_replace( "[/caption]", '', $object->post_content );
$object->post_content = sprintf(_x('%s...', 'teaser suffix', 'scoper'), substr( strip_tags($object->post_content), 0, $num_chars ) );
$object->post_excerpt = $object->post_content;
if ( is_single() || is_page() )
$object->post_content .= '<p class="scoper_x_chars_teaser">' . $teaser_replace[$object_type]['post_content'] . '</p>';
} else {
if ( isset($teaser_replace[$object_type]['post_content']) )
$object->post_content = $teaser_replace[$object_type]['post_content'];
else
$object->post_content = '';
// Replace excerpt with a user-specified fixed teaser message,
// but only if since no custom excerpt exists or teaser options aren't set to some variation of "use excerpt as teaser"
if ( ! empty($teaser_replace[$object_type]['post_excerpt']) )
$object->post_excerpt = $teaser_replace[$object_type]['post_excerpt'];
else
$object->post_excerpt = '';
// If SCOPER_FORCE_EXCERPT_SUFFIX is defined, use the "content" prefix and suffix only when fully replacing content with a fixed teaser
$use_excerpt_suffix = false;
}
// Deal with ambiguity in teaser settings. Previously, content prefix/suffix was applied even if RS substitutes the excerpt as displayed content.
// To avoid confusion with existing installations, only use excerpt prefix/suffix if a value is set or constant is defined.
if ( $use_excerpt_suffix && defined( 'SCOPER_FORCE_EXCERPT_SUFFIX' ) ) {
$teaser_prepend[$object_type]['post_content'] = $teaser_prepend[$object_type]['post_excerpt'];
$teaser_append[$object_type]['post_content'] = $teaser_append[$object_type]['post_excerpt'];
}
foreach ( $teaser_prepend[$object_type] as $col => $entry )
if ( isset($object->$col) )
$object->$col = $entry . $object->$col;
foreach ( $teaser_append[$object_type] as $col => $entry )
if ( isset($object->$col) ) {
if ( ( $col == 'post_content' ) && ! empty( $more_pos ) ) { // WP will strip off anything after the more comment
$object->$col = str_replace( '<!--more-->', "$entry<!--more-->", $object->$col );
} else
$object->$col .= $entry;
}
// no need to display password form if we're blocking content anyway
if ( ! empty( $object->post_password ) )
$object->post_password = '';
}
} // end class
?>