1401 lines
55 KiB
PHP
1401 lines
55 KiB
PHP
<?php
|
|
/*
|
|
Plugin Name: o2
|
|
Plugin URI: http://geto2.com
|
|
Description: Front-page editing and live-updating posts/comments for your site.
|
|
Version: 0.2
|
|
Author: Automattic
|
|
Author URI: http://wordpress.com/
|
|
License: GNU General Public License v2 or later
|
|
*/
|
|
|
|
use \Automattic\Jetpack\Device_Detection\User_Agent_Info;
|
|
|
|
/* Copyright 2013 Automattic
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2, as
|
|
published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
define( 'O2__PLUGIN_LOADED', true );
|
|
define( 'O2__FILE__', __FILE__ );
|
|
define( 'O2__DIR__', dirname( O2__FILE__ ) );
|
|
|
|
class o2 {
|
|
private $editor;
|
|
/**
|
|
* Initializes the plugin by setting localization, filters, and administration functions.
|
|
*/
|
|
function __construct() {
|
|
require O2__DIR__ . '/inc/fragment.php';
|
|
require O2__DIR__ . '/inc/api-base.php';
|
|
require O2__DIR__ . '/inc/read-api.php';
|
|
require O2__DIR__ . '/inc/write-api.php';
|
|
require O2__DIR__ . '/inc/nopriv-write-api.php';
|
|
require O2__DIR__ . '/inc/templates.php';
|
|
require O2__DIR__ . '/inc/template-tags.php';
|
|
require O2__DIR__ . '/inc/editor.php';
|
|
require O2__DIR__ . '/inc/keyboard.php';
|
|
require O2__DIR__ . '/inc/text-helpers.php';
|
|
require O2__DIR__ . '/inc/search.php';
|
|
require O2__DIR__ . '/inc/widget-helper.php';
|
|
|
|
// Terms in Comments powers the next group of files (must be loaded first)
|
|
// @todo: Remove mention here once fully refactored. Wrapping in conditionals
|
|
// in case these were already loaded in mu-plugins/inline-terms.php
|
|
if ( ! class_exists( 'o2_Terms_In_Comments' ) ) {
|
|
require O2__DIR__ . '/inc/terms-in-comments.php';
|
|
}
|
|
if ( ! class_exists( 'o2_Xposts' ) ) {
|
|
require O2__DIR__ . '/inc/xposts.php';
|
|
$this->xposts = new o2_Xposts();
|
|
}
|
|
if ( ! class_exists( 'o2_Tags' ) ) {
|
|
require O2__DIR__ . '/inc/tags.php';
|
|
$this->tags = new o2_Tags();
|
|
}
|
|
|
|
// Conditionaly load WPCOM-specifics
|
|
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
|
|
require O2__DIR__ . '/inc/wpcom.php';
|
|
}
|
|
|
|
// Autoload o2 modules -- must have a load.php file to be loaded
|
|
foreach ( glob( O2__DIR__ . '/modules/*/load.php' ) as $module ) {
|
|
require $module;
|
|
}
|
|
|
|
// Load plugin text domain
|
|
// add_action( 'init', array( $this, 'plugin_textdomain' ) );
|
|
|
|
$this->editor = new o2_Editor();
|
|
$this->keyboard = new o2_Keyboard();
|
|
$this->templates = new o2_Templates();
|
|
$this->search = new o2_Search();
|
|
$this->post_list_creator = new o2_Post_List_Creator;
|
|
$this->comment_list_creator = new o2_Comment_List_Creator;
|
|
|
|
// We handle comments ourselves, so Highlander shouldn't be involved
|
|
$this->disable_highlander();
|
|
|
|
// Post flair requires some special juggling
|
|
add_action( 'init', array( $this, 'post_flair_mute' ), 11 );
|
|
|
|
// Remove oembed handlers or cached results that are incompatible with o2
|
|
add_action( 'init', array( $this, 'remove_oembed_handlers' ) );
|
|
add_filter( 'embed_oembed_html', array( $this, 'remove_cached_incompatible_oembed' ), 10, 3 );
|
|
|
|
// Add our read-only AJAX endpoint, for everyone
|
|
add_action( 'wp_ajax_o2_read', array( 'o2_Read_API', 'init' ) );
|
|
add_action( 'wp_ajax_nopriv_o2_read', array( 'o2_Read_API', 'init' ) );
|
|
|
|
// And our AJAX endpoint for write operations, separate ones for authed and not-authed users
|
|
add_action( 'wp_ajax_o2_write', array( 'o2_Write_API', 'init' ) );
|
|
add_action( 'wp_ajax_nopriv_o2_write', array( 'o2_NoPriv_Write_API', 'init' ) );
|
|
|
|
// And our AJAX handler for userdata
|
|
add_action( 'wp_ajax_o2_userdata', array( $this, 'ajax_get_o2_userdata' ) );
|
|
add_action( 'wp_ajax_nopriv_o2_userdata', array( $this, 'ajax_get_o2_userdata' ) );
|
|
|
|
// Query var for login popup
|
|
add_action( 'init', array( $this, 'add_query_vars' ) );
|
|
|
|
// Handle Infinite Scroll requests
|
|
add_action( 'infinite_scroll_render', array( $this, 'infinite_scroll_render' ) );
|
|
|
|
// Register site styles and scripts
|
|
add_action( 'wp_enqueue_scripts', array( $this, 'register_plugin_styles' ) );
|
|
add_action( 'wp_enqueue_scripts', array( $this, 'register_plugin_scripts' ) );
|
|
|
|
// Add body and post CSS classes
|
|
add_filter( 'body_class', array( $this, 'body_class' ) );
|
|
add_filter( 'post_class', array( $this, 'post_class' ), 10, 3 );
|
|
|
|
// On activation of an o2 compatible theme, set some defaults
|
|
add_action( 'admin_init', array( $this, 'on_activating_o2_compatible_theme' ) );
|
|
|
|
// Handle attachments differently
|
|
add_action( 'template_redirect', array( $this, 'attachment_redirect' ) );
|
|
|
|
// Register our custom functionality
|
|
add_action( 'wp_head', array( $this, 'wp_head' ), 100 );
|
|
add_action( 'wp_footer', array( $this, 'wp_footer' ) );
|
|
add_action( 'wp_footer', array( $this, 'scripts_and_styles' ) );
|
|
add_filter( 'the_excerpt', array( 'o2', 'add_json_data' ), 999999 );
|
|
add_filter( 'the_content', array( 'o2', 'add_json_data' ), 999999 );
|
|
|
|
// Admin Options
|
|
add_action( 'customize_register', array( $this, 'customize_register' ) );
|
|
add_action( 'admin_init', array( $this, 'update_discussion_settings' ) );
|
|
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
|
|
|
|
add_filter( 'o2_options', array( $this, 'required_comment_fields' ) );
|
|
|
|
// Trashed comments functionality.
|
|
add_action( 'delete_comment', array( $this, 'delete_comment_override' ) );
|
|
add_action( 'wp_insert_comment', array( $this, 'insert_comment_actions' ), 10, 2 );
|
|
add_action( 'untrashed_comment', array( 'o2_Fragment', 'bump_comment_modified_time' ) );
|
|
add_action( 'trashed_comment', array( 'o2_Fragment', 'bump_comment_modified_time' ) );
|
|
add_action( 'trash_comment', array( $this, 'remove_trashed_parents' ) );
|
|
add_action( 'trash_comment', array( $this, 'maybe_set_comment_has_children' ) );
|
|
add_action( 'untrash_comment', array( $this, 'add_trashed_parents' ) );
|
|
|
|
// After everything else has done its init, we need to run some first-load stuff
|
|
add_action( 'init', array( $this, 'first_load' ), 100 );
|
|
|
|
// o2 Loaded
|
|
do_action( 'o2_loaded' );
|
|
} // end constructor
|
|
|
|
public function disable_highlander() {
|
|
remove_action( 'init', array( 'Highlander_Comments', 'init' ) );
|
|
}
|
|
|
|
public function post_flair_mute() {
|
|
if ( function_exists( 'post_flair' ) )
|
|
remove_filter( 'the_content', array( post_flair(), 'display' ), 999 );
|
|
}
|
|
|
|
public function register_plugin_styles() {
|
|
wp_register_style( 'o2-plugin-styles', plugins_url( 'css/style.css', O2__FILE__ ), array( 'genericons' ) );
|
|
wp_style_add_data( 'o2-plugin-styles', 'rtl', 'replace' );
|
|
wp_enqueue_style( 'o2-plugin-styles' );
|
|
}
|
|
|
|
public function register_plugin_scripts() {
|
|
global $wp_locale, $allowedposttags, $allowedtags;
|
|
|
|
// Utils
|
|
wp_enqueue_script( 'o2-compare-times', plugins_url( 'js/utils/compare-times.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-events', plugins_url( 'js/utils/events.js', O2__FILE__ ), array( 'backbone', 'jquery' ) );
|
|
wp_enqueue_script( 'o2-highlight-on-inview', plugins_url( 'js/utils/highlight-on-inview.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-highlight', plugins_url( 'js/utils/jquery.highlight.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-is-valid-email', plugins_url( 'js/utils/is-valid-email.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-moment', plugins_url( 'js/utils/moment.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-raw-to-filtered', plugins_url( 'js/utils/raw-to-filtered.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-plugin-caret', plugins_url( 'js/utils/caret.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-plugin-placeholder', plugins_url( 'js/utils/jquery.placeholder.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-page-visibility', plugins_url( 'js/utils/page-visibility.js', O2__FILE__ ), array( 'jquery' ) );
|
|
wp_enqueue_script( 'o2-timestamp', plugins_url( 'js/utils/timestamp.js', O2__FILE__ ), array( 'jquery', 'o2-moment' ) );
|
|
wp_enqueue_script( 'o2-polling', plugins_url( 'js/utils/polling.js', O2__FILE__ ), array( 'backbone', 'jquery', 'o2-events' ) );
|
|
wp_enqueue_script( 'o2-query', plugins_url( 'js/utils/query.js', O2__FILE__ ), array( 'backbone', 'jquery' ) );
|
|
wp_enqueue_script( 'o2-template', plugins_url( 'js/utils/template.js', O2__FILE__ ), array( 'backbone', 'jquery', 'wp-util' ) );
|
|
wp_enqueue_script( 'o2-enquire', plugins_url( 'js/utils/enquire.js', O2__FILE__ ) );
|
|
wp_enqueue_script( 'o2-uuid4', plugins_url( 'js/utils/uuid4.js', O2__FILE__ ) );
|
|
|
|
// Models
|
|
wp_enqueue_script( 'o2-models-base', plugins_url( 'js/models/base.js', O2__FILE__ ), array( 'backbone', 'jquery', 'o2-highlight', 'o2-events' ) );
|
|
wp_enqueue_script( 'o2-models-post', plugins_url( 'js/models/post.js', O2__FILE__ ), array( 'o2-models-base', 'backbone', 'jquery' ) );
|
|
wp_enqueue_script( 'o2-models-comment', plugins_url( 'js/models/comment.js', O2__FILE__ ), array( 'o2-models-base', 'backbone', 'jquery' ) );
|
|
wp_enqueue_script( 'o2-models-page-meta', plugins_url( 'js/models/page-meta.js', O2__FILE__ ), array( 'backbone', 'jquery' ) );
|
|
wp_enqueue_script( 'o2-models-user', plugins_url( 'js/models/user.js', O2__FILE__ ), array( 'backbone', 'jquery' ) );
|
|
wp_enqueue_script( 'o2-models-search-meta', plugins_url( 'js/models/search-meta.js', O2__FILE__ ), array( 'backbone', 'jquery' ) );
|
|
|
|
// Collections
|
|
wp_enqueue_script( 'o2-collections-comments', plugins_url( 'js/collections/comments.js', O2__FILE__ ), array( 'o2-models-comment', 'o2-compare-times' ) );
|
|
wp_enqueue_script( 'o2-collections-posts', plugins_url( 'js/collections/posts.js', O2__FILE__ ), array( 'o2-models-post', 'o2-compare-times' ) );
|
|
wp_enqueue_script( 'o2-collections-users', plugins_url( 'js/collections/users.js', O2__FILE__ ), array( 'o2-models-user', 'underscore' ) );
|
|
|
|
// Views
|
|
wp_enqueue_script( 'o2-views-app-header', plugins_url( 'js/views/app-header.js', O2__FILE__ ), array( 'o2-models-page-meta', 'o2-events', 'utils', 'wp-backbone' ) );
|
|
wp_enqueue_script( 'o2-views-comment', plugins_url( 'js/views/comment.js', O2__FILE__ ), array( 'o2-models-comment', 'o2-editor', 'wp-backbone' ) );
|
|
wp_enqueue_script( 'o2-views-new-post', plugins_url( 'js/views/new-post.js', O2__FILE__ ), array( 'o2-models-post', 'o2-editor', 'wp-backbone' ) );
|
|
wp_enqueue_script( 'o2-views-post', plugins_url( 'js/views/post.js', O2__FILE__ ), array( 'o2-models-post', 'o2-collections-comments', 'o2-editor', 'wp-backbone' ) );
|
|
wp_enqueue_script( 'o2-views-no-posts-post', plugins_url( 'js/views/no-posts-post.js', O2__FILE__ ), array( 'backbone', 'jquery', 'wp-backbone' ) );
|
|
wp_enqueue_script( 'o2-views-posts', plugins_url( 'js/views/posts.js', O2__FILE__ ), array( 'o2-collections-posts', 'jquery-color', 'o2-notifications', 'o2-views-no-posts-post', 'wp-backbone' ) );
|
|
wp_enqueue_script( 'o2-views-app-footer', plugins_url( 'js/views/app-footer.js', O2__FILE__ ), array( 'o2-models-page-meta', 'wp-backbone' ) );
|
|
wp_enqueue_script( 'o2-views-search-form', plugins_url( 'js/views/search-form.js', O2__FILE__ ), array( 'o2-models-search-meta', 'wp-backbone' ) );
|
|
|
|
// Core application
|
|
wp_enqueue_script(
|
|
'o2-app',
|
|
plugins_url( 'js/app/main.js', O2__FILE__ ),
|
|
array(
|
|
'o2-collections-users',
|
|
'o2-events',
|
|
'o2-keyboard', // @todo Re-write this as a module and load later
|
|
'o2-models-page-meta',
|
|
'o2-moment',
|
|
'o2-views-app-footer',
|
|
'o2-views-app-header',
|
|
'o2-views-comment',
|
|
'o2-views-new-post',
|
|
'o2-views-post',
|
|
'o2-views-posts',
|
|
'utils',
|
|
)
|
|
);
|
|
|
|
// Extend o2 by writing modules. Use o2-cocktail to load everything else first.
|
|
wp_enqueue_script(
|
|
'o2-cocktail',
|
|
plugins_url( 'js/utils/cocktail.js', O2__FILE__ ),
|
|
array(
|
|
'o2-app',
|
|
'o2-collections-comments',
|
|
'o2-collections-posts',
|
|
'o2-collections-users',
|
|
'o2-compare-times',
|
|
'o2-events',
|
|
'o2-highlight',
|
|
'o2-highlight-on-inview',
|
|
'o2-is-valid-email',
|
|
'o2-models-base',
|
|
'o2-models-comment',
|
|
'o2-models-page-meta',
|
|
'o2-models-post',
|
|
'o2-models-search-meta',
|
|
'o2-models-user',
|
|
'o2-moment',
|
|
'o2-page-visibility',
|
|
'o2-plugin-caret',
|
|
'o2-plugin-placeholder',
|
|
'o2-polling',
|
|
'o2-query',
|
|
'o2-raw-to-filtered',
|
|
'o2-template',
|
|
'o2-views-app-footer',
|
|
'o2-views-app-header',
|
|
'o2-views-comment',
|
|
'o2-views-new-post',
|
|
'o2-views-post',
|
|
'o2-views-posts',
|
|
'o2-views-search-form',
|
|
)
|
|
);
|
|
|
|
// previous_posts and next_posts can return nonsense for pages, 404 and failed search pages. This fixes that.
|
|
$prev_page_url = previous_posts( false );
|
|
$next_page_url = next_posts( null, false );
|
|
$have_posts = have_posts();
|
|
$view_type = $this->get_view_type();
|
|
if ( 'page' == $view_type || '404' == $view_type || ( 'search' == $view_type && ! $have_posts ) ) {
|
|
$prev_page_url = NULL;
|
|
$next_page_url = NULL;
|
|
}
|
|
|
|
// Comment threading depth
|
|
$thread_comments_depth = 1;
|
|
if ( 1 == get_option( 'thread_comments' ) ) {
|
|
$thread_comments_depth = get_option( 'thread_comments_depth' );
|
|
}
|
|
if ( 1 > $thread_comments_depth ) {
|
|
$thread_comments_depth = 1;
|
|
} elseif ( 10 < $thread_comments_depth ) {
|
|
$thread_comments_depth = 10;
|
|
}
|
|
|
|
// Keep the query vars from this page so we can use them for polling, etc.
|
|
global $wp_query;
|
|
$query_vars = $wp_query->query_vars;
|
|
$sanitized_query_vars = array();
|
|
$allowed_query_vars = apply_filters( 'o2_query_vars', array(
|
|
'author', 'author_name', // Author
|
|
'cat', 'category_name', 'tag', 'tag_id', 'tax_query', // Taxonomy
|
|
'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'm', 'w', // Time
|
|
'p', 'name', 'page_id', 'pagename', 'page', 'paged', // Post
|
|
's', // Search
|
|
) );
|
|
foreach ( $query_vars as $query_var => $value ) {
|
|
if ( in_array( $query_var, $allowed_query_vars ) && ! empty( $value ) )
|
|
$sanitized_query_vars[ $query_var ] = $value;
|
|
}
|
|
$query_vars = apply_filters( 'o2_sanitized_query_vars', $sanitized_query_vars );
|
|
|
|
$default_polling_interval = ( $this->is_mobile() || $this->is_tablet() ) ? 60000 : 10000;
|
|
|
|
$show_front_side_post_box = false;
|
|
|
|
if ( is_user_logged_in() && current_user_can( 'publish_posts' ) ) {
|
|
$show_front_side_post_box = ( is_home() || is_search() || is_tag() );
|
|
}
|
|
|
|
$order = strtoupper( get_query_var( 'order' ) );
|
|
if ( ! in_array( $order, array( 'DESC', 'ASC' ) ) ) {
|
|
$order = 'DESC';
|
|
}
|
|
|
|
// Theme options
|
|
// @todo init default options
|
|
$defaults = self::get_settings();
|
|
$options = get_option( 'o2_options', $defaults );
|
|
$o2_options = array(
|
|
'options' => array(
|
|
'nonce' => wp_create_nonce( 'o2_nonce' ),
|
|
'loadTime' => time() * 1000, // javascript time is millisecond resolution
|
|
'readURL' => admin_url( 'admin-ajax.php?action=o2_read' ),
|
|
'writeURL' => admin_url( 'admin-ajax.php?action=o2_write' ),
|
|
'userDataURL' => admin_url( 'admin-ajax.php?action=o2_userdata' ),
|
|
'loginURL' => wp_login_url(),
|
|
'loginWithRedirectURL' => wp_login_url( home_url() . "?o2_login_complete=true" ),
|
|
'pollingInterval' => (int) apply_filters( 'o2_polling_interval', $default_polling_interval ),
|
|
'timestampFormat' => strip_tags( wp_kses_no_null( trim( __( '%1$s on %2$s', 'o2' ) ) ) ),
|
|
'dateFormat' => apply_filters( 'o2_date_format', get_option( 'date_format' ) ),
|
|
'timeFormat' => apply_filters( 'o2_time_format', get_option( 'time_format' ) ),
|
|
'todayFormat' => strip_tags( wp_kses_no_null( trim( _x( '%s', 'time ago today', 'o2' ) ) ) ),
|
|
'yesterdayFormat' => strip_tags( wp_kses_no_null( trim( __( 'Yesterday at %s', 'o2' ) ) ) ),
|
|
'compactFormat' => array(
|
|
'seconds' => strip_tags( wp_kses_no_null( trim( __( 'Now', 'o2' ) ) ) ),
|
|
'minutes' => strip_tags( wp_kses_no_null( trim( _x( '%sm', 'time in minutes abbreviation', 'o2' ) ) ) ),
|
|
'hours' => strip_tags( wp_kses_no_null( trim( _x( '%sh', 'time in hours abbreviation', 'o2' ) ) ) ),
|
|
'days' => strip_tags( wp_kses_no_null( trim( _x( '%sd', 'time in days abbreviation', 'o2' ) ) ) ),
|
|
'weeks' => strip_tags( wp_kses_no_null( trim( _x( '%sw', 'time in weeks abbreviation', 'o2' ) ) ) ),
|
|
'months' => strip_tags( wp_kses_no_null( trim( _x( '%smon', 'time in months abbreviation', 'o2' ) ) ) ),
|
|
'years' => strip_tags( wp_kses_no_null( trim( _x( '%sy', 'time in years abbreviation', 'o2' ) ) ) ),
|
|
),
|
|
'i18nMoment' => $this->get_i18n_moment( $wp_locale, false ),
|
|
'i18nLanguage' => apply_filters( 'o2_moment_language', 'en-o2' ),
|
|
'infiniteScroll' => get_option( 'infinite_scroll' ) ? true : false,
|
|
'prevPageURL' => $prev_page_url,
|
|
'nextPageURL' => $next_page_url,
|
|
'pageTitle' => $this->get_current_page_title(),
|
|
'appContainer' => $this->get_application_container(),
|
|
'threadContainer' => apply_filters( 'o2_thread_container', 'article' ),
|
|
'showAvatars' => apply_filters( 'o2_show_avatars', get_option( 'show_avatars' ) ),
|
|
'frontSidePostPrompt' => $options['front_side_post_prompt'] ? $options['front_side_post_prompt'] : '',
|
|
'showFrontSidePostBox' => $show_front_side_post_box,
|
|
'showCommentsInitially' => $this->show_comments_initially(),
|
|
'userMustBeLoggedInToComment' => ( "1" == get_option( 'comment_registration' ) ),
|
|
'requireUserNameAndEmailIfNotLoggedIn' => ( "1" == get_option( 'require_name_email' ) ),
|
|
'viewType' => $view_type,
|
|
'isPreview' => is_preview(),
|
|
'showExtended' => strip_tags( wp_kses_no_null( trim( __( 'Show full post', 'o2' ) ) ) ),
|
|
'hideExtended' => strip_tags( wp_kses_no_null( trim( __( 'Hide extended post', 'o2' ) ) ) ),
|
|
'searchQuery' => get_search_query(),
|
|
'havePosts' => $have_posts,
|
|
'queryVars' => $query_vars,
|
|
'order' => $order, /* surprisingly not in $query_vars */
|
|
'threadCommentsDepth' => $thread_comments_depth,
|
|
'isMobileOrTablet' => ( $this->is_mobile() || $this->is_tablet() ),
|
|
'defaultAvatar' => get_option( 'avatar_default', 'identicon' ),
|
|
'searchURL' => home_url( '/' ),
|
|
'homeURL' => home_url( '/' ),
|
|
'postId' => is_singular( array( 'post', 'page' ) ) ? get_the_ID() : 0,
|
|
'mimeTypes' => get_allowed_mime_types(),
|
|
'currentBlogId' => get_current_blog_id(),
|
|
),
|
|
'currentUser' => o2_Fragment::get_current_user_properties(),
|
|
'appControls' => self::get_app_controls(),
|
|
'postFormBefore' => apply_filters( 'o2_post_form_before', '' ),
|
|
'postFormExtras' => apply_filters( 'o2_post_form_extras', '' ),
|
|
'commentFormBefore' => apply_filters( 'o2_comment_form_before', '' ),
|
|
'commentFormExtras' => apply_filters( 'o2_comment_form_extras', '' ),
|
|
'allowedTags' => array(
|
|
'post' => $allowedposttags,
|
|
'comment' => $allowedtags,
|
|
),
|
|
'strings' => array(
|
|
'unsavedChanges' => __( 'You have unsaved changes.', 'o2' ),
|
|
'saveInProgress' => __( 'Not all changes have been saved to the server yet. Please stay on this page until they are saved.', 'o2' ),
|
|
'reloginPrompt' => __( 'Your session has expired. Click here to log in again. Your changes will not be lost.', 'o2' ),
|
|
'reloginSuccessful' => __( 'You have successfully logged back in.', 'o2' ),
|
|
'newCommentBy' => __( 'New comment by %s', 'o2' ),
|
|
'newAnonymousComment' => __( 'New comment by someone', 'o2' ),
|
|
'newPostBy' => __( 'New post by %s', 'o2' ),
|
|
'newMentionBy' => __( '%1$s mentioned you: "%2$s"', 'o2' ),
|
|
'filenameNotUploadedWithType' => __( '%1$s was not uploaded (%2$s files are not allowed).', 'o2' ),
|
|
'filenameNotUploadedNoType' => __( '%1$s was not uploaded (unrecognized file type).', 'o2' ),
|
|
'fileTypeNotSupported' => __( 'Sorry, %1$s files are not allowed.', 'o2' ),
|
|
'unrecognizedFileType' => __( 'Sorry, file not uploaded (unrecognized file type).', 'o2' ),
|
|
'pageNotFound' => __( 'Apologies, but the page you requested could not be found. Perhaps searching will help.', 'o2' ),
|
|
'searchFailed' => __( 'Apologies, but I could not find any results for that search term. Please try again.', 'o2' ),
|
|
'defaultError' => __( 'An unexpected error occurred. Please refresh the page and try again.', 'o2' ),
|
|
'previewPlaceholder' => __( 'Generating preview...', 'o2' ),
|
|
'bold' => __( 'Bold (ctrl/⌘-b)', 'o2' ),
|
|
'italics' => __( 'Italics (ctrl/⌘-i)', 'o2' ),
|
|
'link' => __( 'Link (⌘-shift-a)', 'o2' ),
|
|
'image' => __( 'Image', 'o2' ),
|
|
'blockquote' => __( 'Blockquote', 'o2' ),
|
|
'code' => __( 'Code', 'o2' ),
|
|
'addPostTitle' => __( 'Add a post title', 'o2' ),
|
|
'enterTitleHere' => __( 'Enter title here', 'o2' ),
|
|
'noPosts' => __( 'Ready to publish your first post? Simply use the form above.', 'o2' ),
|
|
'noPostsMobile' => __( 'Tap the new post control below to begin writing your first post.', 'o2' ),
|
|
'awaitingApproval' => __( 'This comment is awaiting approval.', 'o2' ),
|
|
'isTrashed' => __( 'This comment was trashed.', 'o2' ),
|
|
'prevDeleted' => __( 'This comment was deleted.', 'o2' ),
|
|
'cancel' => __( 'Cancel', 'o2' ),
|
|
'edit' => __( 'Edit', 'o2' ),
|
|
'email' => __( 'Email', 'o2' ),
|
|
'name' => __( 'Name', 'o2' ),
|
|
'permalink' => __( 'Permalink', 'o2' ),
|
|
'post' => _x( 'Post', 'Verb, to post', 'o2' ),
|
|
'reply' => __( 'Reply', 'o2' ),
|
|
'save' => __( 'Save', 'o2' ),
|
|
'saving' => __( 'Saving', 'o2' ),
|
|
'website' => __( 'Website', 'o2' ),
|
|
'search' => __( 'Search', 'o2' ),
|
|
'anonymous' => __( 'Someone', 'o2' ),
|
|
'preview' => __( 'Preview', 'o2' ),
|
|
'olderPosts' => __( 'Older posts', 'o2' ),
|
|
'newerPosts' => __( 'Newer posts', 'o2' ),
|
|
'loginToComment' => __( 'Login to leave a comment.', 'o2' ),
|
|
'fillDetailsBelow' => __( 'Fill in your details below.', 'o2' ),
|
|
'editingOthersComment' => __( "Careful! You are editing someone else's comment.", 'o2' ),
|
|
'commentURL' => __( 'Website', 'o2' ),
|
|
'showComments' => __( 'Show Comments', 'o2' ),
|
|
'hideComments' => __( 'Hide Comments', 'o2' ),
|
|
'redirectedHomePostTrashed' => __( 'This post was trashed. You will be redirected home now.', 'o2' ),
|
|
'redirectedHomePageTrashed' => __( 'This page was trashed. You will be redirected home now.', 'o2' ),
|
|
'postBeingTrashed' => __( 'This post is being trashed.', 'o2' ),
|
|
'pageBeingTrashed' => __( 'This page is being trashed.', 'o2' ),
|
|
'postTrashedFailed' => __( 'There was an error trashing that post. Please try again in a moment.', 'o2' ),
|
|
'pageTrashedFailed' => __( 'There was an error trashing that page. Please try again in a moment.', 'o2' ),
|
|
'shortlinkCopied' => __( 'Shortlink copied to clipboard.', 'o2' ),
|
|
),
|
|
);
|
|
|
|
$o2_options = apply_filters( 'o2_options', $o2_options );
|
|
// We cannot "localize" directly into the o2 object here, since that would cause
|
|
// our early loaded models, views, etc to be blown away
|
|
// So.... we "localize" into a o2Config object that will be "extended" into o2 itself
|
|
// on o2.App.start
|
|
wp_localize_script( 'o2-app', 'o2Config', $o2_options );
|
|
}
|
|
|
|
/**
|
|
* Get the list of scripts and styles already on the page
|
|
*/
|
|
public static function scripts_and_styles() {
|
|
global $wp_scripts, $wp_styles;
|
|
|
|
$scripts = is_a( $wp_scripts, 'WP_Scripts' ) ? $wp_scripts->done : array();
|
|
$scripts = apply_filters( 'infinite_scroll_existing_scripts', $scripts );
|
|
|
|
$styles = is_a( $wp_styles, 'WP_Styles' ) ? $wp_styles->done : array();
|
|
$styles = apply_filters( 'infinite_scroll_existing_stylesheets', $styles );
|
|
|
|
?><script type="text/javascript">
|
|
o2Config.options.scripts = <?php echo json_encode( $scripts ); ?>;
|
|
o2Config.options.styles = <?php echo json_encode( $styles ); ?>;
|
|
</script><?php
|
|
}
|
|
|
|
function get_app_controls() {
|
|
$app_controls = array(
|
|
'<a href="#" class="o2-toggle-comments" data-alternate-text="' . esc_html__( 'Show comment threads', 'o2' ) . '">' . esc_html__( 'Hide comment threads', 'o2' ) . '</a>',
|
|
);
|
|
return apply_filters( 'o2_app_controls', $app_controls );
|
|
}
|
|
|
|
/**
|
|
* Return only o2 containers with Infinite Scroll queries
|
|
*/
|
|
function infinite_scroll_render() {
|
|
global $post;
|
|
while ( have_posts() ) {
|
|
the_post();
|
|
|
|
$fragment = o2_Fragment::get_fragment( $post );
|
|
?><script class='o2-data' type='application/json' style='display:none;'><?php echo json_encode( $fragment ); ?></script><?php
|
|
}
|
|
}
|
|
|
|
function get_application_container() {
|
|
return apply_filters( 'o2_application_container', '#content' );
|
|
}
|
|
|
|
function show_comments_initially() {
|
|
$show_comments = true;
|
|
|
|
if ( isset( $_COOKIE['showComments'] ) && 'false' === $_COOKIE['showComments'] )
|
|
$show_comments = false;
|
|
|
|
if ( ( $this->is_mobile() || $this->is_tablet() ) && ( ! is_single() || ! is_page() ) )
|
|
$show_comments = false;
|
|
|
|
return apply_filters( 'o2_show_comments_initially', $show_comments );
|
|
}
|
|
|
|
/**
|
|
* Adds an 'o2' class to the body.
|
|
*/
|
|
function body_class( $classes ) {
|
|
$classes[] = 'o2';
|
|
return $classes;
|
|
}
|
|
|
|
/**
|
|
* Add an author class to posts.
|
|
*/
|
|
function post_class( $classes, $class, $post_id ) {
|
|
$post = get_post( $post_id );
|
|
if ( empty( $post ) )
|
|
return $classes;
|
|
|
|
$user = get_user_by( 'id', $post->post_author );
|
|
if ( empty( $user ) )
|
|
return $classes;
|
|
|
|
$classes[] = 'author-' . sanitize_html_class( $user->user_nicename, $user->ID );
|
|
return $classes;
|
|
}
|
|
|
|
public static function get_defaults() {
|
|
return array(
|
|
'o2_enabled' => true, //enable o2 functionality by default
|
|
'front_side_post_prompt' => __( '嗨, {name}! 有什么重要的事?', 'o2' ),
|
|
'enable_resolved_posts' => false,
|
|
'mark_posts_unresolved' => false
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Set o2_option defaults, merge with get_theme_support, merge with pre-existing o2_options
|
|
*/
|
|
public function get_settings() {
|
|
// Set o2 defaults
|
|
$settings = $defaults = self::get_defaults();
|
|
|
|
// Get theme support options, merge with $settings
|
|
if ( is_array( get_theme_support( 'o2' ) ) ) {
|
|
$_settings = current( get_theme_support( 'o2' ) );
|
|
$settings = self::settings_merge( $defaults, $settings, $_settings );
|
|
}
|
|
|
|
// Get pre-existing o2_options, merge with $settings
|
|
$_settings = get_option( 'o2_options', $settings );
|
|
$settings = self::settings_merge( $settings, $settings, $_settings );
|
|
|
|
$settings = apply_filters( 'o2_get_settings', $settings );
|
|
|
|
update_option( 'o2_options', $settings );
|
|
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* Add the required strings for logged out commenters
|
|
*/
|
|
public static function required_comment_fields( $options ) {
|
|
if ( 1 == get_option( 'require_name_email' ) ) {
|
|
$options['strings']['commentName'] = __( 'Name (required)', 'o2' );
|
|
$options['strings']['commentEmail'] = __( 'Email (required)', 'o2' );
|
|
} else {
|
|
$options['strings']['commentName'] = __( 'Name', 'o2' );
|
|
$options['strings']['commentEmail'] = __( 'Email', 'o2' );
|
|
}
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* Logic to merge option/default arrays
|
|
*/
|
|
public function settings_merge( $defaults, $settings, $_settings ) {
|
|
if ( isset( $_settings ) && is_array( $_settings ) ) {
|
|
foreach ( $_settings as $key => $value ) {
|
|
switch ( $key ) {
|
|
case 'o2_enabled' :
|
|
if ( isset( $value ) )
|
|
$settings[ $key ] = (bool) $value;
|
|
|
|
break;
|
|
|
|
case 'front_side_post_prompt' :
|
|
if ( isset( $value ) )
|
|
$settings[ $key ] = esc_attr( $value );
|
|
|
|
break;
|
|
|
|
case 'enable_resolved_posts' :
|
|
if ( isset( $value ) )
|
|
$settings[ $key ] = (bool) $value;
|
|
|
|
break;
|
|
|
|
case 'mark_posts_unresolved' :
|
|
if ( isset( $value ) )
|
|
$settings[ $key ] = (bool) $value;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$settings = wp_parse_args( $settings, $defaults );
|
|
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* Set up options for Customizer
|
|
*/
|
|
public function customize_register( $wp_customize ) {
|
|
$defaults = self::get_defaults();
|
|
|
|
$wp_customize->add_section( 'o2_options', array(
|
|
'title' => __( 'Theme Options', 'o2' ),
|
|
'priority' => 35,
|
|
) );
|
|
|
|
$wp_customize->add_setting( 'o2_options[front_side_post_prompt]', array(
|
|
'default' => esc_attr( $defaults['front_side_post_prompt'] ),
|
|
'type' => 'option',
|
|
) );
|
|
|
|
$wp_customize->add_control( 'o2_options[front_side_post_prompt]', array(
|
|
'label' => __( 'Front-end Post Prompt: Use {name} for user\'s name', 'o2' ),
|
|
'section' => 'o2_options',
|
|
'priority' => 1,
|
|
) );
|
|
|
|
$wp_customize->add_setting( 'o2_options[enable_resolved_posts]', array(
|
|
'default' => (bool) $defaults['enable_resolved_posts'],
|
|
'type' => 'option',
|
|
) );
|
|
|
|
$wp_customize->add_control( 'o2_options[enable_resolved_posts]', array(
|
|
'label' => __( 'Enable "To Do" Module', 'o2' ),
|
|
'section' => 'o2_options',
|
|
'type' => 'checkbox',
|
|
'priority' => 2,
|
|
) );
|
|
|
|
$wp_customize->add_setting( 'o2_options[mark_posts_unresolved]', array(
|
|
'default' => (bool) $defaults['mark_posts_unresolved'],
|
|
'type' => 'option',
|
|
) );
|
|
|
|
$wp_customize->add_control( 'o2_options[mark_posts_unresolved]', array(
|
|
'label' => __( 'Mark New Posts "To Do"', 'o2' ),
|
|
'section' => 'o2_options',
|
|
'type' => 'checkbox',
|
|
'priority' => 3,
|
|
) );
|
|
}
|
|
|
|
/**
|
|
* Returns what type of 'view'/'page' is being returned by WordPress (home, single, search, etc)
|
|
*/
|
|
public static function get_view_type( ) {
|
|
$type = 'home';
|
|
if ( is_home() )
|
|
$type = 'home';
|
|
else if ( is_page() )
|
|
$type = 'page';
|
|
else if ( is_singular() )
|
|
$type = 'single';
|
|
else if ( is_search() )
|
|
$type = 'search';
|
|
else if ( is_archive() )
|
|
$type = 'archive';
|
|
else if ( is_404() )
|
|
$type = '404';
|
|
return apply_filters( 'o2_view_type', $type );
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
public function get_current_page_title() {
|
|
global $wp_query;
|
|
$page_title = '';
|
|
$obj = get_queried_object();
|
|
|
|
if ( is_home() )
|
|
$page_title = get_bloginfo( 'name', 'display' );
|
|
else if ( is_page() )
|
|
$page_title = the_title( '', '', false );
|
|
elseif ( is_author() ) {
|
|
$page_title = sprintf( __( 'Posts by %s', 'o2' ), $obj->display_name );
|
|
$current_user = wp_get_current_user();
|
|
if ( $current_user instanceof WP_User ) {
|
|
if ( $current_user->display_name === $obj->display_name ) {
|
|
$page_title = __( 'My Posts', 'o2' );
|
|
}
|
|
}
|
|
} elseif ( is_category() ) {
|
|
$page_title = sprintf( __( 'Posts categorized as %s', 'o2' ), single_cat_title( '', false ) );
|
|
} elseif ( is_tag() ) {
|
|
$query_slugs = explode( ',', get_query_var( 'tag' ) );
|
|
$tag_titles = array();
|
|
foreach( (array) $query_slugs as $query_slug ) {
|
|
if ( ! $query_slug ) {
|
|
continue;
|
|
}
|
|
$query_slug = strip_tags( wp_kses_no_null( trim( $query_slug ) ) );
|
|
$found_tag = get_term_by( 'slug', $query_slug, 'post_tag' );
|
|
if ( $found_tag ) {
|
|
$tag_titles[] = sprintf( '#%s', $found_tag->name );
|
|
}
|
|
}
|
|
$page_title = implode( ', ', $tag_titles );
|
|
} elseif ( is_search() ) {
|
|
$page_title = sprintf( __( "Posts containing ‘%s’ (%d)", 'o2' ), get_search_query(), $wp_query->found_posts );
|
|
} elseif ( is_404() ) {
|
|
$page_title = __( 'Page Not Found', 'o2' );
|
|
} elseif ( is_archive() ) {
|
|
if ( is_day() )
|
|
$page_title = __( 'Daily Archives: ', 'o2' ) . get_the_date();
|
|
elseif ( is_month() )
|
|
$page_title = __( 'Monthly Archives: ', 'o2' ) . get_the_date( 'F Y' );
|
|
elseif ( is_year() )
|
|
$page_title = __( 'Yearly Archives: ', 'o2' ) . get_the_date( 'Y' );
|
|
else
|
|
$page_title = __( 'Post Archives', 'o2' );
|
|
}
|
|
|
|
$page_title = apply_filters( 'the_title', $page_title );
|
|
|
|
// We run a separate filter for the page title because the_title filter
|
|
// will change other content on the page (like menu items) that we don't
|
|
// want to change
|
|
|
|
$page_title = apply_filters( 'o2_page_title', $page_title );
|
|
|
|
return $page_title;
|
|
}
|
|
|
|
/*
|
|
* Add our special query vars (i.e. for login complete)
|
|
*/
|
|
public function add_query_vars() {
|
|
global $wp;
|
|
$wp->add_query_var( 'o2_login_complete' );
|
|
}
|
|
|
|
public function wp_head() {
|
|
// Suppress output; will show things once we're fully loaded
|
|
?>
|
|
<style>
|
|
<?php echo esc_js( $this->get_application_container() ); ?> {
|
|
display: none;
|
|
}
|
|
</style>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Start setting up o2 on the client side.
|
|
*/
|
|
public function wp_footer() {
|
|
$this->run_browser_check();
|
|
|
|
// Bootstrap the users
|
|
global $o2_userdata;
|
|
if ( is_array( $o2_userdata ) && count( $o2_userdata ) > 0 ) {
|
|
echo "<script class='o2-user-data' type='application/json' style='display:none'>";
|
|
echo json_encode( $o2_userdata );
|
|
echo "</script>\n";
|
|
}
|
|
|
|
$login_complete_var = get_query_var( 'o2_login_complete' );
|
|
if ( ! empty( $login_complete_var ) ) {
|
|
?>
|
|
<script>
|
|
jQuery( document ).ready( function( $ ) {
|
|
if ( "undefined" != typeof window.opener && null != window.opener ) {
|
|
window.opener.o2.App.onLogInComplete();
|
|
}
|
|
window.close();
|
|
});
|
|
</script>
|
|
<?php
|
|
do_action( 'o2_wp_footer_nopriv' );
|
|
} else {
|
|
?>
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
var bootstrap = { data: [] };
|
|
o2Data = $( 'script.o2-data' );
|
|
if ( o2Data.length > 0 ) {
|
|
o2Data.each( function() {
|
|
// Parse the JSON that's embedded in the page and add it to the bootstrap data
|
|
var me = $( this );
|
|
var thread;
|
|
try {
|
|
thread = $.parseJSON( me.text() );
|
|
} catch( e ) {
|
|
thread = false;
|
|
console.log( '$.parseJSON failure: ' + me.text() );
|
|
}
|
|
if ( false !== thread ) {
|
|
_.each( thread, function( frag ) {
|
|
bootstrap.data.push( frag );
|
|
} );
|
|
}
|
|
me.remove();
|
|
} );
|
|
}
|
|
|
|
// Merge o2Config into o2 itself
|
|
o2 = $.extend( o2, o2Config );
|
|
|
|
// Some generally-useful references
|
|
o2.$body = $( 'body' );
|
|
o2.$appContainer = $( o2.options.appContainer );
|
|
|
|
// As soon as o2 loads, poll for new content to account
|
|
// for Chrome's caching weirdness on back/tab-recovery.
|
|
o2.$appContainer.on( 'ready.o2', function() {
|
|
o2.Polling.poll();
|
|
} );
|
|
|
|
// Bootstrap o2 with any in-page content
|
|
o2.start( bootstrap );
|
|
});
|
|
</script>
|
|
<?php
|
|
do_action( 'o2_wp_footer' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for IE8, IE9 and inject a little Javascript to warn of compatibility issues
|
|
*/
|
|
public function run_browser_check() {
|
|
|
|
if ( ! preg_match_all( "/.*MSIE\s*([0-9.]+).*/", $_SERVER['HTTP_USER_AGENT'], $matches ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( (int) $matches[1][0] > 9 ) {
|
|
return;
|
|
}
|
|
|
|
$text = esc_html__( 'Your browser is not up-to-date. As a result, some features may not work correctly. Please switch to an updated browser.', 'o2' );
|
|
|
|
// Use browser comments to ensure a cached version isn't accidentally shown.
|
|
?>
|
|
<!--[if lte IE 9]>
|
|
<script type="text/javascript">
|
|
jQuery( document ).ready( function() {
|
|
jQuery( 'body' ).prepend( '<div class="o2-browser-warning"><?php echo esc_js( $text ); ?></div>' );
|
|
} );
|
|
</script>
|
|
<![endif]-->
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Embed JSONified post+comment data into each thread (post) for backbone consumption
|
|
*/
|
|
public static function add_json_data( $content ) {
|
|
global $post;
|
|
|
|
// No post context? return immediately.
|
|
if ( ! $post ) {
|
|
return $content;
|
|
}
|
|
|
|
// 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() ) {
|
|
if ( current_filter() === 'the_content' ) {
|
|
$conversation[] = o2_Fragment::get_fragment( $post, array( 'find-adjacent' => is_single(), 'the_content' => $content ) );
|
|
} else {
|
|
// Other content filters (particularly, the_excerpt) may try to strip tags, which causes issues
|
|
// for the various actions that o2 adds to the end of the content.
|
|
$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;
|
|
}
|
|
|
|
/**
|
|
* Remove oembed handlers that are incompatible with o2
|
|
*/
|
|
public function remove_oembed_handlers() {
|
|
wp_oembed_remove_provider( '#https?://(.+\.)?polldaddy\.com/.*#i' );
|
|
wp_oembed_remove_provider( '#https?://poll\.fm/.*#i' );
|
|
}
|
|
|
|
/**
|
|
* Remove cached incompatible oembed results that a previous theme
|
|
* may have saved
|
|
*/
|
|
public function remove_cached_incompatible_oembed( $html, $url, $args ) {
|
|
if ( false !== strpos( $html, 'polldaddy.com' ) || false !== strpos( $html, 'poll.fm' ) ) {
|
|
return $url;
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Transforms the WP_Locale translations for the Moment.js JavaScript class.
|
|
*
|
|
* @param $locale WP_Locale - A locale object
|
|
* @param $json_encode bool - Whether to encode the result. Default true.
|
|
* @return string|array - The translations object.
|
|
*/
|
|
function get_i18n_moment( $locale, $json_encode = true ) {
|
|
$moment = array(
|
|
'months' => array_values( $locale->month ),
|
|
'monthsShort' => array_values( $locale->month_abbrev ),
|
|
'weekdays' => array_values( $locale->weekday ),
|
|
'weekdaysShort' => array_values( $locale->weekday_abbrev ),
|
|
'weekdaysMin' => array_values( $locale->weekday ),
|
|
'relativeTime' => array(
|
|
'future' => strip_tags( wp_kses_no_null( trim( _x( 'in %s', 'time from now', 'o2' ) ) ) ),
|
|
'past' => strip_tags( wp_kses_no_null( trim( _x( '%s ago', 'time ago', 'o2' ) ) ) ),
|
|
's' => strip_tags( wp_kses_no_null( trim( _x( 'a few seconds', 'unit of time', 'o2' ) ) ) ),
|
|
'm' => strip_tags( wp_kses_no_null( trim( _x( 'a min', 'abbreviation of minute', 'o2' ) ) ) ),
|
|
'mm' => strip_tags( wp_kses_no_null( trim( _x( '%d mins', 'abbreviation of minutes', 'o2' ) ) ) ),
|
|
'h' => strip_tags( wp_kses_no_null( trim( __( 'an hour', 'o2' ) ) ) ),
|
|
'hh' => strip_tags( wp_kses_no_null( trim( __( '%d hours', 'o2' ) ) ) ),
|
|
'd' => strip_tags( wp_kses_no_null( trim( __( 'a day', 'o2' ) ) ) ),
|
|
'dd' => strip_tags( wp_kses_no_null( trim( __( '%d days', 'o2' ) ) ) ),
|
|
'M' => strip_tags( wp_kses_no_null( trim( __( 'a month', 'o2' ) ) ) ),
|
|
'MM' => strip_tags( wp_kses_no_null( trim( __( '%d months', 'o2' ) ) ) ),
|
|
'y' => strip_tags( wp_kses_no_null( trim( __( 'a year', 'o2' ) ) ) ),
|
|
'yy' => strip_tags( wp_kses_no_null( trim( __( '%d years', 'o2' ) ) ) ),
|
|
),
|
|
);
|
|
|
|
if ( $json_encode )
|
|
return json_encode( $moment );
|
|
else
|
|
return $moment;
|
|
}
|
|
|
|
/**
|
|
* Redirect to attachments. Rather than trying to render them somehow,
|
|
* if someone links to an "attachment page", just intercept that and send
|
|
* the user directly the actual attachment resource (image, PDF, whatever)
|
|
*/
|
|
function attachment_redirect() {
|
|
if ( is_attachment() ) {
|
|
wp_safe_redirect( wp_get_attachment_url( get_the_ID() ) );
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enforce selected discussion settings to fixed values
|
|
*/
|
|
function update_discussion_settings() {
|
|
if ( is_admin() && current_user_can( 'manage_options' ) ) {
|
|
// for most options, update_option will not write the db if the option hasn't changed,
|
|
// so it is OK to simply call update_option without checking if the
|
|
// option needs updating first
|
|
|
|
// page_comments - o2 doesn't page - so should always be empty = not paged
|
|
update_option( 'page_comments', '' );
|
|
|
|
// comments_per_page, default_comments_page - we don't use these, but let's make
|
|
// sure a reasonable value is present
|
|
$comments_per_page = get_option( 'comments_per_page' );
|
|
if ( 50 != $comments_per_page ) {
|
|
update_option( 'comments_per_page', 50 );
|
|
}
|
|
update_option( 'default_comments_page', 'newest' );
|
|
|
|
// comment_order - o2 always does older at top, so enforce "asc"
|
|
update_option( 'comment_order', 'asc' );
|
|
}
|
|
}
|
|
|
|
function admin_enqueue_scripts( $hook ) {
|
|
// Settings > Discussion
|
|
if ( 'options-discussion.php' == $hook ) {
|
|
$script_url = plugins_url( 'js/admin/discussion-settings.js', O2__FILE__ );
|
|
wp_enqueue_script( 'o2-admin-discussion', $script_url, array( 'jquery' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If an o2 compatible theme has just been activated, set some defaults
|
|
* If it has no widgets set up in any of its widget areas, add some
|
|
*/
|
|
public function on_activating_o2_compatible_theme() {
|
|
// Only on the themes page in wp admin
|
|
if ( ! is_admin() || FALSE === strpos( $_SERVER['REQUEST_URI'], '/wp-admin/themes.php' ) ) {
|
|
return;
|
|
}
|
|
|
|
// Only on activation
|
|
if ( FALSE === strpos( $_SERVER['REQUEST_URI'], 'activated=' ) ) {
|
|
return;
|
|
}
|
|
|
|
// If they have just activated an o2 capable theme,
|
|
if ( ! current_theme_supports( 'o2' ) ) {
|
|
return;
|
|
}
|
|
|
|
// Set some defaults
|
|
// thread_comments - turn on initially on for o2
|
|
update_option( 'thread_comments', '1' );
|
|
|
|
// comment_whitelist - turn off initially for o2
|
|
delete_option( 'comment_whitelist' );
|
|
|
|
// thread_comments_depth - set initially to 10 for o2
|
|
$thread_comments_depth = get_option( 'thread_comments_depth' );
|
|
if ( 10 != $thread_comments_depth ) {
|
|
update_option( 'thread_comments_depth', 10 );
|
|
}
|
|
|
|
// Enable Post Likes on the homepage
|
|
// The format for storing this option is ugly, and there's no API to access it
|
|
if ( function_exists( 'wpl_is_enabled_sitewide' ) ) {
|
|
// Turn on 'index' display for Post Likes if any sharing options are configured
|
|
if ( $sharing_options = get_option( 'sharing-options' ) ) {
|
|
if (
|
|
empty( $sharing_options['global']['show'] )
|
|
||
|
|
(
|
|
'index' != $sharing_options['global']['show' ]
|
|
&&
|
|
!in_array( 'index', (array) $sharing_options['global']['show'] )
|
|
)
|
|
) {
|
|
if ( is_array( $sharing_options['global']['show'] ) ) {
|
|
// Add index to the array of where it's enabled
|
|
$sharing_options['global']['show'][] = 'index';
|
|
} else if ( is_string( $sharing_options['global']['show'] ) ) {
|
|
// Default is 'posts' but when it's an array it's 'post', so let's
|
|
// just force the array and activate everywhere
|
|
$sharing_options['global']['show'] = array( 'index', 'post', 'page', 'attachment' );
|
|
}
|
|
update_option( 'sharing-options', $sharing_options );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Enable Comment Likes
|
|
if ( function_exists( 'wpl_is_comments_enabled_sitewide' ) ) {
|
|
update_option( 'jetpack_comment_likes_enabled', true );
|
|
}
|
|
|
|
// Use Widget Helper to add some widgets
|
|
$widget_helper = new o2_Widget_Helper();
|
|
|
|
// Has P2 been run on this blog before? If so, let's start with
|
|
// its widgets (except for p2_ widgets)
|
|
$p2_mods = get_option( 'theme_mods_pub/p2' );
|
|
if ( false === $p2_mods ) {
|
|
$p2_mods = get_option( 'theme_mods_p2' );
|
|
}
|
|
|
|
if ( false !== $p2_mods ) {
|
|
$p2_sidebar_widgets = array();
|
|
if ( isset( $p2_mods['sidebars_widgets']['data']['sidebar-1'] ) ) {
|
|
$p2_sidebar_widgets = $p2_mods['sidebars_widgets']['data']['sidebar-1'];
|
|
}
|
|
|
|
foreach( (array) $p2_sidebar_widgets as $p2_sidebar_widget ) {
|
|
// filter out any p2_ widget
|
|
if ( 'p2_' != substr( $p2_sidebar_widget, 0, 3 ) ) {
|
|
$widget_helper->add_existing_widget( $p2_sidebar_widget );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine which o2 widgets need to be added
|
|
$search_added = $widget_helper->has_widget( 'search', 'sidebar-1' );
|
|
$filter_added = $widget_helper->has_widget( 'o2-filter-widget', 'sidebar-1' );
|
|
$activity_added = $widget_helper->has_widget( 'o2-live-comments', 'sidebar-1' );
|
|
|
|
// Add the Search widget
|
|
if ( ! $search_added ) {
|
|
$widget_helper->add_new_widget(
|
|
'search' /* widget base ID from widget class def */,
|
|
true, /* true: multiwidget, false: not */
|
|
array(
|
|
'title' => __( 'Search', 'o2' ) /* widget settings */
|
|
),
|
|
'sidebar-1' /* widget area to add to, empty for default */
|
|
);
|
|
}
|
|
|
|
// Add the o2 Filter Widget
|
|
if ( ! $filter_added ) {
|
|
$widget_helper->add_new_widget(
|
|
'o2-filter-widget', /* widget base ID from widget class def */
|
|
true, /* true: multiwidget, false: not */
|
|
array(
|
|
'title' => '' /* widget settings */
|
|
),
|
|
'sidebar-1'
|
|
);
|
|
}
|
|
|
|
// Add the o2 Recent Activity Widget
|
|
if ( ! $activity_added ) {
|
|
$widget_helper->add_new_widget(
|
|
'o2-live-comments-widget' /* widget base ID from widget class def */,
|
|
true, /* true: multiwidget, false: not */
|
|
array(
|
|
'title' => __( 'Recent Activity', 'o2' ), /* widget settings */
|
|
'kind' => 'both',
|
|
'number' => 10
|
|
),
|
|
'sidebar-1' /* widget area to add to, empty for default */
|
|
);
|
|
}
|
|
|
|
// Remove Recent Posts and Recent Comments widgets
|
|
if ( $widget_helper->has_widget( 'recent-comments' ) ) {
|
|
$widget_helper->remove_widget_instances( 'recent-comments' );
|
|
}
|
|
if ( $widget_helper->has_widget( 'recent-posts' ) ) {
|
|
$widget_helper->remove_widget_instances( 'recent-posts' );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns true if the user agent is recognized as a mobile device (or a tablet)
|
|
*/
|
|
public static function is_mobile() {
|
|
$is_mobile = ( function_exists( 'jetpack_is_mobile' ) ) ? jetpack_is_mobile() : wp_is_mobile();
|
|
return $is_mobile;
|
|
}
|
|
|
|
/*
|
|
* Returns true if the user agent is recognized as a tablet
|
|
*/
|
|
public static function is_tablet() {
|
|
if ( class_exists( '\Automattic\Jetpack\Device_Detection\User_Agent_Info' ) ) {
|
|
$jetpack_user_agent = new User_Agent_Info();
|
|
$is_tablet = $jetpack_user_agent->is_tablet();
|
|
} else if ( class_exists( 'Jetpack_User_Agent_Info' ) ) {
|
|
$is_tablet = Jetpack_User_Agent_Info::is_tablet();
|
|
} else {
|
|
$is_tablet = false;
|
|
}
|
|
return $is_tablet;
|
|
}
|
|
|
|
/**
|
|
* Create a new comment with message "This comment has been permanently deleted."
|
|
* and put it in place of deleted comment.
|
|
*
|
|
* @param $comment_ID
|
|
*/
|
|
public function delete_comment_override( $comment_ID ) {
|
|
$children = get_comments( array( 'status' => 'approve', 'parent' => $comment_ID ) );
|
|
if ( ! empty( $children ) ) {
|
|
$old_comment = get_comment( $comment_ID );
|
|
$comment_to_add = $old_comment;
|
|
|
|
unset( $comment_to_add->comment_ID );
|
|
$comment_to_add->comment_approved = 1;
|
|
$comment_to_add->comment_content = __( 'This comment was deleted.', 'o2' );
|
|
|
|
$new_comment_id = wp_insert_comment( (array) $comment_to_add );
|
|
$comment_created = get_comment_meta( $comment_ID, 'o2_comment_created', true );
|
|
if ( empty ( $comment_created ) ) {
|
|
$comment = get_comment( $comment_ID );
|
|
$comment_created = strtotime( $comment->comment_date_gmt );
|
|
}
|
|
|
|
update_comment_meta( $new_comment_id, 'o2_comment_created', $comment_created );
|
|
|
|
o2_Fragment::bump_comment_modified_time( $new_comment_id );
|
|
update_comment_meta( $new_comment_id, 'o2_comment_prev_deleted', $comment_ID );
|
|
|
|
$clean_comment_ids = array();
|
|
|
|
foreach ( $children as $child ) {
|
|
$clean_comment_ids[] = $child->comment_ID;
|
|
|
|
$child->comment_parent = $new_comment_id;
|
|
wp_update_comment( (array) $child );
|
|
}
|
|
|
|
$clean_comment_ids[] = $new_comment_id;
|
|
clean_comment_cache( $clean_comment_ids );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a timestamp named o2_comment_created so we can maintain
|
|
* when the comment was created even if comment is later modified.
|
|
*
|
|
* @param $comment_ID
|
|
* @param $comment
|
|
*/
|
|
public function insert_comment_actions( $comment_ID, $comment ) {
|
|
update_comment_meta( $comment_ID, 'o2_comment_created', current_time( 'timestamp', true ) );
|
|
}
|
|
|
|
/**
|
|
* Returns true if comment has approved child.
|
|
*
|
|
* @param $comment_ID
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function has_approved_child( $comment_ID ) {
|
|
$children = get_comments( array( 'status' => 'approve', 'parent' => $comment_ID ) );
|
|
|
|
return ! empty( $children );
|
|
}
|
|
|
|
/**
|
|
* When un-trashing a comment, traverse through this comment's parents and
|
|
* add o2_comment_has_children flag where needed.
|
|
*
|
|
* @param $comment_ID
|
|
* @param bool $comment
|
|
*/
|
|
public function add_trashed_parents( $comment_ID, $comment = false ) {
|
|
$comment = ! empty( $comment ) ? $comment : get_comment( $comment_ID );
|
|
if ( 0 < $comment->comment_parent ) {
|
|
$parent = get_comment( $comment->comment_parent );
|
|
if ( 'trash' == $parent->comment_approved ) {
|
|
$this->add_trashed_parents( $parent->comment_ID, $parent );
|
|
update_comment_meta( $parent->comment_ID, 'o2_comment_has_children', true );
|
|
o2_Fragment::bump_comment_modified_time( $parent->comment_ID );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If this comment has no approved siblings, then go ahead and delete its parent as well.
|
|
*
|
|
* @param $comment_ID
|
|
* @param bool|object $comment
|
|
*/
|
|
public function remove_trashed_parents( $comment_ID, $comment = false ) {
|
|
$child_count = 0;
|
|
$has_approved_children = false;
|
|
|
|
if ( empty( $comment ) ) {
|
|
|
|
// If $comment isn't set, then assume we haven't recursed and setup vars.
|
|
$child_count = 1;
|
|
$comment = get_comment( $comment_ID );
|
|
$has_approved_children = $this->has_approved_child( $comment_ID );
|
|
}
|
|
|
|
if ( ! $has_approved_children && 0 < $comment->comment_parent ) {
|
|
$parent = get_comment( $comment->comment_parent );
|
|
if ( 'trash' == $parent->comment_approved ) {
|
|
$children = get_comments(
|
|
array(
|
|
'count' => true,
|
|
'parent' => $parent->comment_ID
|
|
)
|
|
);
|
|
if ( $child_count == $children ) {
|
|
delete_comment_meta( $parent->comment_ID, 'o2_comment_has_children', true );
|
|
o2_Fragment::bump_comment_modified_time( $parent->comment_ID );
|
|
$this->remove_trashed_parents( $parent->comment_ID, $parent );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add has_children flag to deleted comments so that we can query for only trashed comments
|
|
* that have children in o2_Fragent:get_fragment_from_post() for bootstrapping.
|
|
*
|
|
* @param $comment_ID
|
|
*/
|
|
public function maybe_set_comment_has_children( $comment_ID ) {
|
|
$children = get_comments( array( 'parent' => $comment_ID ) );
|
|
|
|
if ( ! empty( $children ) ) {
|
|
update_comment_meta( $comment_ID, 'o2_comment_has_children', true );
|
|
}
|
|
}
|
|
|
|
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 );
|
|
} elseif ( isset( $_GET['userlogins'] ) ) {
|
|
// 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 );
|
|
} else {
|
|
wp_send_json_error( array( 'errorText' => 'Invalid request' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if o2 is active on the current site.
|
|
*/
|
|
function is_enabled() {
|
|
$o2_is_enabled = false;
|
|
|
|
$o2_options = get_option( 'o2_options' );
|
|
if ( is_array( $o2_options ) ) {
|
|
if ( isset( $o2_options['o2_enabled'] ) ) {
|
|
$o2_is_enabled = $o2_options['o2_enabled'];
|
|
}
|
|
}
|
|
|
|
return $o2_is_enabled;
|
|
}
|
|
|
|
/**
|
|
* This is really only needed for the first load of o2 after activation.
|
|
* Useful for flushing rewrite rules, and that kind of thing.
|
|
*/
|
|
public function first_load() {
|
|
if ( ! get_option( 'o2-just-activated', false ) ) {
|
|
return;
|
|
}
|
|
|
|
delete_option( 'o2-just-activated' );
|
|
|
|
flush_rewrite_rules( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Breathe
|
|
*/
|
|
global $o2;
|
|
$o2 = new o2();
|
|
|