Advertisement
Guest User

Untitled

a guest
May 26th, 2018
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 56.19 KB | None | 0 0
  1. <?php
  2. /*
  3. Plugin Name: o2
  4. Plugin URI: http://geto2.com
  5. Description: Front-page editing and live-updating posts/comments for your site.
  6. Version: 0.2
  7. Author: Automattic
  8. Author URI: http://wordpress.com/
  9. License: GNU General Public License v2 or later
  10. */
  11.  
  12. /*  Copyright 2013 Automattic
  13.  
  14.     This program is free software; you can redistribute it and/or modify
  15.     it under the terms of the GNU General Public License, version 2, as
  16.     published by the Free Software Foundation.
  17.  
  18.     This program is distributed in the hope that it will be useful,
  19.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.     GNU General Public License for more details.
  22.  
  23.     You should have received a copy of the GNU General Public License
  24.     along with this program; if not, write to the Free Software
  25.     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  26.  */
  27.  
  28. define( 'O2__PLUGIN_LOADED', true );
  29. define( 'O2__FILE__', __FILE__ );
  30. define( 'O2__DIR__', dirname( O2__FILE__ ) );
  31.  
  32. class o2 {
  33.     private $editor;
  34.     /**
  35.      * Initializes the plugin by setting localization, filters, and administration functions.
  36.      */
  37.     function __construct() {
  38.         require O2__DIR__ . '/inc/fragment.php';
  39.         require O2__DIR__ . '/inc/api-base.php';
  40.         require O2__DIR__ . '/inc/read-api.php';
  41.         require O2__DIR__ . '/inc/write-api.php';
  42.         require O2__DIR__ . '/inc/nopriv-write-api.php';
  43.         require O2__DIR__ . '/inc/templates.php';
  44.         require O2__DIR__ . '/inc/template-tags.php';
  45.         require O2__DIR__ . '/inc/editor.php';
  46.         require O2__DIR__ . '/inc/keyboard.php';
  47.         require O2__DIR__ . '/inc/text-helpers.php';
  48.         require O2__DIR__ . '/inc/search.php';
  49.         require O2__DIR__ . '/inc/widget-helper.php';
  50.  
  51.         // Terms in Comments powers the next group of files (must be loaded first)
  52.         // @todo: Remove mention here once fully refactored. Wrapping in conditionals
  53.         // in case these were already loaded in mu-plugins/inline-terms.php
  54.         if ( ! class_exists( 'o2_Terms_In_Comments' ) ) {
  55.             require O2__DIR__ . '/inc/terms-in-comments.php';
  56.         }
  57.         if ( ! class_exists( 'o2_Xposts' ) ) {
  58.             require O2__DIR__ . '/inc/xposts.php';
  59.             $this->xposts = new o2_Xposts();
  60.         }
  61.         if ( ! class_exists( 'o2_Tags' ) ) {
  62.             require O2__DIR__ . '/inc/tags.php';
  63.             $this->tags = new o2_Tags();
  64.         }
  65.  
  66.         // Conditionaly load WPCOM-specifics
  67.         if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
  68.             require O2__DIR__ . '/inc/wpcom.php';
  69.         }
  70.  
  71.  
  72.         // Autoload o2 modules -- must have a load.php file to be loaded
  73.         foreach ( glob( O2__DIR__ . '/modules/*/load.php' ) as $module ) {
  74.             require $module;
  75.         }
  76.  
  77.         // Load plugin text domain
  78.         // add_action( 'init', array( $this, 'plugin_textdomain' ) );
  79.  
  80.         $this->editor               = new o2_Editor();
  81.         $this->keyboard             = new o2_Keyboard();
  82.         $this->templates            = new o2_Templates();
  83.         $this->search               = new o2_Search();
  84.         $this->post_list_creator    = new o2_Post_List_Creator;
  85.         $this->comment_list_creator = new o2_Comment_List_Creator;
  86.  
  87.         // We handle comments ourselves, so Highlander shouldn't be involved
  88.         $this->disable_highlander();
  89.  
  90.         // Post flair requires some special juggling
  91.         add_action( 'init', array( $this, 'post_flair_mute' ), 11 );
  92.  
  93.         // Remove oembed handlers or cached results that are incompatible with o2
  94.         add_action( 'init', array( $this, 'remove_oembed_handlers' ) );
  95.         add_filter( 'embed_oembed_html', array( $this, 'remove_cached_incompatible_oembed' ), 10, 3 );
  96.  
  97.         // Add our read-only AJAX endpoint, for everyone
  98.         add_action( 'wp_ajax_o2_read', array( 'o2_Read_API', 'init' ) );
  99.         add_action( 'wp_ajax_nopriv_o2_read', array( 'o2_Read_API', 'init' ) );
  100.  
  101.         // And our AJAX endpoint for write operations, separate ones for authed and not-authed users
  102.         add_action( 'wp_ajax_o2_write', array( 'o2_Write_API', 'init' ) );
  103.         add_action( 'wp_ajax_nopriv_o2_write', array( 'o2_NoPriv_Write_API', 'init' ) );
  104.  
  105.         // And our AJAX handler for userdata
  106.         add_action( 'wp_ajax_o2_userdata', array( $this, 'ajax_get_o2_userdata' ) );
  107.         add_action( 'wp_ajax_nopriv_o2_userdata', array( $this, 'ajax_get_o2_userdata' ) );
  108.  
  109.         // Query var for login popup
  110.         add_action( 'init', array( $this, 'add_query_vars' ) );
  111.  
  112.         // Handle Infinite Scroll requests
  113.         add_action( 'infinite_scroll_render', array( $this, 'infinite_scroll_render' ) );
  114.  
  115.         // Register site styles and scripts
  116.         add_action( 'wp_enqueue_scripts', array( $this, 'register_plugin_styles' ) );
  117.         add_action( 'wp_enqueue_scripts', array( $this, 'register_plugin_scripts' ) );
  118.  
  119.         // Add body and post CSS classes
  120.         add_filter( 'body_class', array( $this, 'body_class' ) );
  121.         add_filter( 'post_class', array( $this, 'post_class' ), 10, 3 );
  122.  
  123.         // On activation of an o2 compatible theme, set some defaults
  124.         add_action( 'admin_init', array( $this, 'on_activating_o2_compatible_theme' ) );
  125.  
  126.         // Handle attachments differently
  127.         add_action( 'template_redirect', array( $this, 'attachment_redirect' ) );
  128.  
  129.         // Register our custom functionality
  130.         add_action( 'wp_head',     array( $this, 'wp_head' ), 100 );
  131.         add_action( 'wp_footer',   array( $this, 'wp_footer' ) );
  132.         add_action( 'wp_footer',   array( $this, 'scripts_and_styles' ) );
  133.         add_filter( 'the_excerpt', array( 'o2', 'add_json_data' ), 999999 );
  134.         add_filter( 'the_content', array( 'o2', 'add_json_data' ), 999999 );
  135.  
  136.         // Admin Options
  137.         add_action( 'customize_register', array( $this, 'customize_register' ) );
  138.         add_action( 'admin_init', array( $this, 'update_discussion_settings' ) );
  139.         add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
  140.  
  141.         add_filter( 'o2_options', array( $this, 'required_comment_fields' ) );
  142.  
  143.         // Trashed comments functionality.
  144.         add_action( 'delete_comment',    array( $this, 'delete_comment_override' ) );
  145.         add_action( 'wp_insert_comment', array( $this, 'insert_comment_actions' ), 10, 2 );
  146.         add_action( 'untrashed_comment', array( 'o2_Fragment', 'bump_comment_modified_time' ) );
  147.         add_action( 'trashed_comment',   array( 'o2_Fragment', 'bump_comment_modified_time' ) );
  148.         add_action( 'trash_comment',     array( $this, 'remove_trashed_parents' ) );
  149.         add_action( 'trash_comment',     array( $this, 'maybe_set_comment_has_children' ) );
  150.         add_action( 'untrash_comment',   array( $this, 'add_trashed_parents' ) );
  151.  
  152.         // After everything else has done its init, we need to run some first-load stuff
  153.         add_action( 'init', array( $this, 'first_load' ), 100 );
  154.  
  155.         // o2 Loaded
  156.         do_action( 'o2_loaded' );
  157.     } // end constructor
  158.  
  159.     public function disable_highlander() {
  160.         remove_action( 'init', array( 'Highlander_Comments', 'init' ) );
  161.     }
  162.  
  163.     public function post_flair_mute() {
  164.         if ( function_exists( 'post_flair' ) )
  165.             remove_filter( 'the_content', array( post_flair(), 'display' ), 999 );
  166.     }
  167.  
  168.     public function register_plugin_styles() {
  169.         wp_register_style( 'o2-plugin-styles', plugins_url( 'css/style.css', O2__FILE__ ), array( 'genericons' ) );
  170.         wp_style_add_data( 'o2-plugin-styles', 'rtl', 'replace' );
  171.         wp_enqueue_style( 'o2-plugin-styles' );
  172.     }
  173.  
  174.     public function register_plugin_scripts() {
  175.         global $wp_locale, $allowedposttags, $allowedtags;
  176.  
  177.         // Utils
  178.         wp_enqueue_script( 'o2-compare-times',        plugins_url( 'js/utils/compare-times.js', O2__FILE__ ),       array( 'jquery' ) );
  179.         wp_enqueue_script( 'o2-events',               plugins_url( 'js/utils/events.js', O2__FILE__ ),              array( 'backbone', 'jquery' ) );
  180.         wp_enqueue_script( 'o2-highlight-on-inview',  plugins_url( 'js/utils/highlight-on-inview.js', O2__FILE__ ), array( 'jquery' ) );
  181.         wp_enqueue_script( 'o2-highlight',            plugins_url( 'js/utils/jquery.highlight.js', O2__FILE__ ),    array( 'jquery' ) );
  182.         wp_enqueue_script( 'o2-is-valid-email',       plugins_url( 'js/utils/is-valid-email.js', O2__FILE__ ),      array( 'jquery' ) );
  183.         wp_enqueue_script( 'o2-moment',               plugins_url( 'js/utils/moment.js', O2__FILE__ ),              array( 'jquery' ) );
  184.         wp_enqueue_script( 'o2-raw-to-filtered',      plugins_url( 'js/utils/raw-to-filtered.js', O2__FILE__ ),     array( 'jquery' ) );
  185.         wp_enqueue_script( 'o2-plugin-caret',         plugins_url( 'js/utils/caret.js', O2__FILE__ ),               array( 'jquery' ) );
  186.         wp_enqueue_script( 'o2-plugin-placeholder',   plugins_url( 'js/utils/jquery.placeholder.js', O2__FILE__ ),  array( 'jquery' ) );
  187.         wp_enqueue_script( 'o2-page-visibility',      plugins_url( 'js/utils/page-visibility.js', O2__FILE__ ),     array( 'jquery' ) );
  188.         wp_enqueue_script( 'o2-timestamp',            plugins_url( 'js/utils/timestamp.js', O2__FILE__ ),           array( 'jquery', 'o2-moment' ) );
  189.         wp_enqueue_script( 'o2-polling',              plugins_url( 'js/utils/polling.js', O2__FILE__ ),             array( 'backbone', 'jquery', 'o2-events' ) );
  190.         wp_enqueue_script( 'o2-query',                plugins_url( 'js/utils/query.js', O2__FILE__ ),               array( 'backbone', 'jquery' ) );
  191.         wp_enqueue_script( 'o2-template',             plugins_url( 'js/utils/template.js', O2__FILE__ ),            array( 'backbone', 'jquery', 'wp-util' ) );
  192.         wp_enqueue_script( 'o2-enquire',              plugins_url( 'js/utils/enquire.js', O2__FILE__ ) );
  193.  
  194.         // Models
  195.         wp_enqueue_script( 'o2-models-base',          plugins_url( 'js/models/base.js', O2__FILE__ ),        array( 'backbone', 'jquery', 'o2-highlight', 'o2-events' ) );
  196.         wp_enqueue_script( 'o2-models-post',          plugins_url( 'js/models/post.js', O2__FILE__ ),        array( 'o2-models-base', 'backbone', 'jquery' ) );
  197.         wp_enqueue_script( 'o2-models-comment',       plugins_url( 'js/models/comment.js', O2__FILE__ ),     array( 'o2-models-base', 'backbone', 'jquery' ) );
  198.         wp_enqueue_script( 'o2-models-page-meta',     plugins_url( 'js/models/page-meta.js', O2__FILE__ ),   array( 'backbone', 'jquery' ) );
  199.         wp_enqueue_script( 'o2-models-user',          plugins_url( 'js/models/user.js', O2__FILE__ ),        array( 'backbone', 'jquery' ) );
  200.         wp_enqueue_script( 'o2-models-search-meta',   plugins_url( 'js/models/search-meta.js', O2__FILE__ ), array( 'backbone', 'jquery' ) );
  201.  
  202.         // Collections
  203.         wp_enqueue_script( 'o2-collections-comments', plugins_url( 'js/collections/comments.js', O2__FILE__ ), array( 'o2-models-comment', 'o2-compare-times' ) );
  204.         wp_enqueue_script( 'o2-collections-posts',    plugins_url( 'js/collections/posts.js', O2__FILE__ ),    array( 'o2-models-post', 'o2-compare-times' ) );
  205.         wp_enqueue_script( 'o2-collections-users',    plugins_url( 'js/collections/users.js', O2__FILE__ ),    array( 'o2-models-user', 'underscore' ) );
  206.  
  207.         // Views
  208.         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' ) );
  209.         wp_enqueue_script( 'o2-views-comment',        plugins_url( 'js/views/comment.js', O2__FILE__ ),       array( 'o2-models-comment', 'o2-editor', 'wp-backbone' ) );
  210.         wp_enqueue_script( 'o2-views-new-post',       plugins_url( 'js/views/new-post.js', O2__FILE__ ),      array( 'o2-models-post', 'o2-editor', 'wp-backbone' ) );
  211.         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' ) );
  212.         wp_enqueue_script( 'o2-views-no-posts-post',  plugins_url( 'js/views/no-posts-post.js', O2__FILE__ ), array( 'backbone', 'jquery', 'wp-backbone' ) );
  213.         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' ) );
  214.         wp_enqueue_script( 'o2-views-app-footer',     plugins_url( 'js/views/app-footer.js', O2__FILE__ ),    array( 'o2-models-page-meta', 'wp-backbone' ) );
  215.         wp_enqueue_script( 'o2-views-search-form',    plugins_url( 'js/views/search-form.js', O2__FILE__ ),   array( 'o2-models-search-meta', 'wp-backbone' ) );
  216.  
  217.         // Core application
  218.         wp_enqueue_script(
  219.             'o2-app',
  220.             plugins_url( 'js/app/main.js', O2__FILE__ ),
  221.             array(
  222.                 'o2-collections-users',
  223.                 'o2-events',
  224.                 'o2-keyboard', // @todo Re-write this as a module and load later
  225.                 'o2-models-page-meta',
  226.                 'o2-moment',
  227.                 'o2-views-app-footer',
  228.                 'o2-views-app-header',
  229.                 'o2-views-comment',
  230.                 'o2-views-new-post',
  231.                 'o2-views-post',
  232.                 'o2-views-posts',
  233.                 'utils',
  234.             )
  235.         );
  236.  
  237.         // Extend o2 by writing modules. Use o2-cocktail to load everything else first.
  238.         wp_enqueue_script(
  239.             'o2-cocktail',
  240.             plugins_url( 'js/utils/cocktail.js', O2__FILE__ ),
  241.             array(
  242.                 'o2-app',
  243.                 'o2-collections-comments',
  244.                 'o2-collections-posts',
  245.                 'o2-collections-users',
  246.                 'o2-compare-times',
  247.                 'o2-events',
  248.                 'o2-highlight',
  249.                 'o2-highlight-on-inview',
  250.                 'o2-is-valid-email',
  251.                 'o2-models-base',
  252.                 'o2-models-comment',
  253.                 'o2-models-page-meta',
  254.                 'o2-models-post',
  255.                 'o2-models-search-meta',
  256.                 'o2-models-user',
  257.                 'o2-moment',
  258.                 'o2-page-visibility',
  259.                 'o2-plugin-caret',
  260.                 'o2-plugin-placeholder',
  261.                 'o2-polling',
  262.                 'o2-query',
  263.                 'o2-raw-to-filtered',
  264.                 'o2-template',
  265.                 'o2-views-app-footer',
  266.                 'o2-views-app-header',
  267.                 'o2-views-comment',
  268.                 'o2-views-new-post',
  269.                 'o2-views-post',
  270.                 'o2-views-posts',
  271.                 'o2-views-search-form',
  272.             )
  273.         );
  274.  
  275.         // previous_posts and next_posts can return nonsense for pages, 404 and failed search pages.  This fixes that.
  276.         $prev_page_url = previous_posts( false );
  277.         $next_page_url = next_posts( null, false );
  278.         $have_posts = have_posts();
  279.         $view_type = $this->get_view_type();
  280.         if ( 'page' == $view_type || '404' == $view_type || ( 'search' == $view_type && ! $have_posts ) ) {
  281.             $prev_page_url = NULL;
  282.             $next_page_url = NULL;
  283.         }
  284.  
  285.         // Comment threading depth
  286.         $thread_comments_depth = 1;
  287.         if ( 1 == get_option( 'thread_comments' ) ) {
  288.             $thread_comments_depth = get_option( 'thread_comments_depth' );
  289.         }
  290.         if ( 1 > $thread_comments_depth ) {
  291.             $thread_comments_depth = 1;
  292.         } elseif ( 10 < $thread_comments_depth ) {
  293.             $thread_comments_depth = 10;
  294.         }
  295.  
  296.         // Keep the query vars from this page so we can use them for polling, etc.
  297.         global $wp_query;
  298.         $query_vars = $wp_query->query_vars;
  299.         $sanitized_query_vars = array();
  300.         $allowed_query_vars = apply_filters( 'o2_query_vars', array(
  301.             'author', 'author_name',                                           // Author
  302.             'cat', 'category_name', 'tag', 'tag_id', 'tax_query',              // Taxonomy
  303.             'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'm', 'w',   // Time
  304.             'p', 'name', 'page_id', 'pagename', 'page', 'paged',               // Post
  305.             's',                                                               // Search
  306.         ) );
  307.         foreach ( $query_vars as $query_var => $value ) {
  308.             if ( in_array( $query_var, $allowed_query_vars ) && ! empty( $value ) )
  309.                 $sanitized_query_vars[ $query_var ] = $value;
  310.         }
  311.         $query_vars = apply_filters( 'o2_sanitized_query_vars', $sanitized_query_vars );
  312.  
  313.         $default_polling_interval = ( $this->is_mobile() || $this->is_tablet() ) ? 60000 : 10000;
  314.  
  315.         $show_front_side_post_box = false;
  316.  
  317.         if ( is_user_logged_in() && current_user_can( 'publish_posts' ) ) {
  318.             $show_front_side_post_box = ( is_home() || is_search() || is_tag() );
  319.         }
  320.  
  321.         $order = strtoupper( get_query_var( 'order' ) );
  322.         if ( ! in_array( $order, array( 'DESC', 'ASC' ) ) ) {
  323.             $order = 'DESC';
  324.         }
  325.  
  326.         // Theme options
  327.         // @todo init default options
  328.         $defaults   = self::get_settings();
  329.         $options    = get_option( 'o2_options', $defaults );
  330.         $o2_options = array(
  331.             'options' => array(
  332.                 'nonce'                                => wp_create_nonce( 'o2_nonce' ),
  333.                 'loadTime'                             => time() * 1000, // javascript time is millisecond resolution
  334.                 'readURL'                              => admin_url( 'admin-ajax.php?action=o2_read' ),
  335.                 'writeURL'                             => admin_url( 'admin-ajax.php?action=o2_write' ),
  336.                 'userDataURL'                          => admin_url( 'admin-ajax.php?action=o2_userdata' ),
  337.                 'loginURL'                             => wp_login_url(),
  338.                 'loginWithRedirectURL'                 => wp_login_url( home_url() . "?o2_login_complete=true" ),
  339.                 'pollingInterval'                      => (int) apply_filters( 'o2_polling_interval', $default_polling_interval ),
  340.                 'timestampFormat'                      => strip_tags( wp_kses_no_null( trim( __( '%1$s on %2$s', 'o2' ) ) ) ),
  341.                 'dateFormat'                           => apply_filters( 'o2_date_format', get_option( 'date_format' ) ),
  342.                 'timeFormat'                           => apply_filters( 'o2_time_format', get_option( 'time_format' ) ),
  343.                 'todayFormat'                          => strip_tags( wp_kses_no_null( trim( _x( '%s', 'time ago today', 'o2' ) ) ) ),
  344.                 'yesterdayFormat'                      => strip_tags( wp_kses_no_null( trim( __( 'Yesterday at %s', 'o2' ) ) ) ),
  345.                 'compactFormat'                        => array(
  346.                     'seconds'  => strip_tags( wp_kses_no_null( trim( __( 'Now', 'o2' ) ) ) ),
  347.                     'minutes'  => strip_tags( wp_kses_no_null( trim( _x( '%sm', 'time in minutes abbreviation', 'o2' ) ) ) ),
  348.                     'hours'    => strip_tags( wp_kses_no_null( trim( _x( '%sh', 'time in hours abbreviation', 'o2' ) ) ) ),
  349.                     'days'     => strip_tags( wp_kses_no_null( trim( _x( '%sd', 'time in days abbreviation', 'o2' ) ) ) ),
  350.                     'weeks'    => strip_tags( wp_kses_no_null( trim( _x( '%sw', 'time in weeks abbreviation', 'o2' ) ) ) ),
  351.                     'months'   => strip_tags( wp_kses_no_null( trim( _x( '%smon', 'time in months abbreviation', 'o2' ) ) ) ),
  352.                     'years'    => strip_tags( wp_kses_no_null( trim( _x( '%sy', 'time in years abbreviation', 'o2' ) ) ) ),
  353.                 ),
  354.                 'i18nMoment'                           => $this->get_i18n_moment( $wp_locale, false ),
  355.                 'i18nLanguage'                         => apply_filters( 'o2_moment_language', 'en-o2' ),
  356.                 'infiniteScroll'                       => get_option( 'infinite_scroll' ) ? true : false,
  357.                 'prevPageURL'                          => $prev_page_url,
  358.                 'nextPageURL'                          => $next_page_url,
  359.                 'pageTitle'                            => $this->get_current_page_title(),
  360.                 'appContainer'                         => $this->get_application_container(),
  361.                 'threadContainer'                      => apply_filters( 'o2_thread_container', 'article' ),
  362.                 'showAvatars'                          => apply_filters( 'o2_show_avatars', get_option( 'show_avatars' ) ),
  363.                 'frontSidePostPrompt'                  => $options['front_side_post_prompt'] ? $options['front_side_post_prompt'] : '',
  364.                 'showFrontSidePostBox'                 => $show_front_side_post_box,
  365.                 'showCommentsInitially'                => $this->show_comments_initially(),
  366.                 'userMustBeLoggedInToComment'          => ( "1" == get_option( 'comment_registration' ) ),
  367.                 'requireUserNameAndEmailIfNotLoggedIn' => ( "1" == get_option( 'require_name_email' ) ),
  368.                 'viewType'                             => $view_type,
  369.                 'isPreview'                            => is_preview(),
  370.                 'showExtended'                         => strip_tags( wp_kses_no_null( trim( __( 'Show full post', 'o2' ) ) ) ),
  371.                 'hideExtended'                         => strip_tags( wp_kses_no_null( trim( __( 'Hide extended post', 'o2' ) ) ) ),
  372.                 'searchQuery'                          => get_search_query(),
  373.                 'havePosts'                            => $have_posts,
  374.                 'queryVars'                            => $query_vars,
  375.                 'order'                                => $order, /* surprisingly not in $query_vars */
  376.                 'threadCommentsDepth'                  => $thread_comments_depth,
  377.                 'isMobileOrTablet'                     => ( $this->is_mobile() || $this->is_tablet() ),
  378.                 'defaultAvatar'                        => get_option( 'avatar_default', 'identicon' ),
  379.                 'searchURL'                            => home_url( '/' ),
  380.                 'homeURL'                              => home_url( '/' ),
  381.                 'postId'                               => is_singular( array( 'post', 'page' ) ) ? get_the_ID() : 0,
  382.                 'mimeTypes'                            => get_allowed_mime_types(),
  383.                 'currentBlogId'                        => get_current_blog_id(),
  384.             ),
  385.             'currentUser'       => o2_Fragment::get_current_user_properties(),
  386.             'appControls'       => self::get_app_controls(),
  387.             'postFormBefore'    => apply_filters( 'o2_post_form_before', '' ),
  388.             'postFormExtras'    => apply_filters( 'o2_post_form_extras', '' ),
  389.             'commentFormBefore' => apply_filters( 'o2_comment_form_before', '' ),
  390.             'commentFormExtras' => apply_filters( 'o2_comment_form_extras', '' ),
  391.             'allowedTags'       => array(
  392.                 'post'                                 => $allowedposttags,
  393.                 'comment'                              => $allowedtags,
  394.             ),
  395.             'strings' => array(
  396.                 'unsavedChanges'                       => __( 'You have unsaved changes.', 'o2' ),
  397.                 'saveInProgress'                       => __( 'Not all changes have been saved to the server yet. Please stay on this page until they are saved.', 'o2' ),
  398.                 'reloginPrompt'                        => __( 'Your session has expired. Click here to log in again. Your changes will not be lost.', 'o2' ),
  399.                 'reloginSuccessful'                    => __( 'You have successfully logged back in.', 'o2' ),
  400.                 'newCommentBy'                         => __( 'New comment by %s', 'o2' ),
  401.                 'newAnonymousComment'                  => __( 'New comment by someone', 'o2' ),
  402.                 'newPostBy'                            => __( 'New post by %s', 'o2' ),
  403.                 'newMentionBy'                         => __( '%1$s mentioned you: "%2$s"', 'o2' ),
  404.                 'filenameNotUploadedWithType'          => __( '%1$s was not uploaded (%2$s files are not allowed).', 'o2' ),
  405.                 'filenameNotUploadedNoType'            => __( '%1$s was not uploaded (unrecognized file type).', 'o2' ),
  406.                 'fileTypeNotSupported'                 => __( 'Sorry, %1$s files are not allowed.', 'o2' ),
  407.                 'unrecognizedFileType'                 => __( 'Sorry, file not uploaded (unrecognized file type).', 'o2' ),
  408.                 'pageNotFound'                         => __( 'Apologies, but the page you requested could not be found. Perhaps searching will help.', 'o2' ),
  409.                 'searchFailed'                         => __( 'Apologies, but I could not find any results for that search term. Please try again.', 'o2' ),
  410.                 'defaultError'                         => __( 'An unexpected error occurred. Please refresh the page and try again.', 'o2' ),
  411.                 'previewPlaceholder'                   => __( 'Generating preview...', 'o2' ),
  412.                 'bold'                                 => __( 'Bold (ctrl/⌘-b)', 'o2' ),
  413.                 'italics'                              => __( 'Italics (ctrl/⌘-i)', 'o2' ),
  414.                 'link'                                 => __( 'Link (⌘-shift-a)', 'o2' ),
  415.                 'image'                                => __( 'Image', 'o2' ),
  416.                 'blockquote'                           => __( 'Blockquote', 'o2' ),
  417.                 'code'                                 => __( 'Code', 'o2' ),
  418.                 'addPostTitle'                         => __( 'Add a post title', 'o2' ),
  419.                 'enterTitleHere'                       => __( 'Enter title here', 'o2' ),
  420.                 'noPosts'                              => __( 'Ready to publish your first post? Simply use the form above.', 'o2' ),
  421.                 'noPostsMobile'                        => __( 'Tap the new post control below to begin writing your first post.', 'o2' ),
  422.                 'awaitingApproval'                     => __( 'This comment is awaiting approval.', 'o2' ),
  423.                 'isTrashed'                            => __( 'This comment was trashed.', 'o2' ),
  424.                 'prevDeleted'                          => __( 'This comment was deleted.', 'o2' ),
  425.                 'cancel'                               => __( 'Cancel', 'o2' ),
  426.                 'edit'                                 => __( 'Edit', 'o2' ),
  427.                 'email'                                => __( 'Email', 'o2' ),
  428.                 'name'                                 => __( 'Name', 'o2' ),
  429.                 'permalink'                            => __( 'Permalink', 'o2' ),
  430.                 'post'                                 => _x( 'Post', 'Verb, to post', 'o2' ),
  431.                 'reply'                                => __( 'Reply', 'o2' ),
  432.                 'save'                                 => __( 'Save', 'o2' ),
  433.                 'saving'                               => __( 'Saving', 'o2' ),
  434.                 'website'                              => __( 'Website', 'o2' ),
  435.                 'search'                               => __( 'Search', 'o2' ),
  436.                 'anonymous'                            => __( 'Someone', 'o2' ),
  437.                 'preview'                              => __( 'Preview', 'o2' ),
  438.                 'olderPosts'                           => __( 'Older posts', 'o2' ),
  439.                 'newerPosts'                           => __( 'Newer posts', 'o2' ),
  440.                 'loginToComment'                       => __( 'Login to leave a comment.', 'o2' ),
  441.                 'fillDetailsBelow'                     => __( 'Fill in your details below.', 'o2' ),
  442.                 'editingOthersComment'                 => __( "Careful! You are editing someone else's comment.", 'o2' ),
  443.                 'commentURL'                           => __( 'Website', 'o2' ),
  444.                 'showComments'                         => __( 'Show Comments', 'o2' ),
  445.                 'hideComments'                         => __( 'Hide Comments', 'o2' ),
  446.                 'redirectedHomePostTrashed'            => __( 'This post was trashed. You will be redirected home now.', 'o2' ),
  447.                 'redirectedHomePageTrashed'            => __( 'This page was trashed. You will be redirected home now.', 'o2' ),
  448.                 'postBeingTrashed'                     => __( 'This post is being trashed.', 'o2' ),
  449.                 'pageBeingTrashed'                     => __( 'This page is being trashed.', 'o2' ),
  450.                 'postTrashedFailed'                    => __( 'There was an error trashing that post. Please try again in a moment.', 'o2' ),
  451.                 'pageTrashedFailed'                    => __( 'There was an error trashing that page. Please try again in a moment.', 'o2' ),
  452.             ),
  453.         );
  454.  
  455.         $o2_options = apply_filters( 'o2_options', $o2_options );
  456.         // We cannot "localize" directly into the o2 object here, since that would cause
  457.         // our early loaded models, views, etc to be blown away
  458.         // So.... we "localize" into a o2Config object that will be "extended" into o2 itself
  459.         // on o2.App.start
  460.         wp_localize_script( 'o2-app', 'o2Config', $o2_options );
  461.     }
  462.  
  463.     /**
  464.      * Get the list of scripts and styles already on the page
  465.      */
  466.     public static function scripts_and_styles() {
  467.         global $wp_scripts, $wp_styles;
  468.  
  469.         $scripts = is_a( $wp_scripts, 'WP_Scripts' ) ? $wp_scripts->done : array();
  470.         $scripts = apply_filters( 'infinite_scroll_existing_scripts', $scripts );
  471.  
  472.         $styles = is_a( $wp_styles, 'WP_Styles' ) ? $wp_styles->done : array();
  473.         $styles = apply_filters( 'infinite_scroll_existing_stylesheets', $styles );
  474.  
  475.         ?><script type="text/javascript">
  476.             o2Config.options.scripts = <?php echo json_encode( $scripts ); ?>;
  477.             o2Config.options.styles = <?php echo json_encode( $styles ); ?>;
  478.         </script><?php
  479.     }
  480.  
  481.     function get_app_controls() {
  482.         $app_controls = array(
  483.             '<a href="#" class="o2-toggle-comments" data-alternate-text="' . esc_html__( 'Show comment threads', 'o2' ) . '">' . esc_html__( 'Hide comment threads', 'o2' ) . '</a>',
  484.         );
  485.         return apply_filters( 'o2_app_controls', $app_controls );
  486.     }
  487.  
  488.     /**
  489.      * Return only o2 containers with Infinite Scroll queries
  490.      */
  491.     function infinite_scroll_render() {
  492.         global $post;
  493.         while ( have_posts() ) {
  494.             the_post();
  495.  
  496.             $fragment = o2_Fragment::get_fragment( $post );
  497.             ?><script class='o2-data' type='application/json' style='display:none;'><?php echo json_encode( $fragment ); ?></script><?php
  498.         }
  499.     }
  500.  
  501.     function get_application_container() {
  502.         return apply_filters( 'o2_application_container', '#content' );
  503.     }
  504.  
  505.     function show_comments_initially() {
  506.         $show_comments = true;
  507.  
  508.         if ( isset( $_COOKIE['showComments'] ) && 'false' === $_COOKIE['showComments'] )
  509.             $show_comments = false;
  510.  
  511.         if ( ( $this->is_mobile() || $this->is_tablet() ) && ( ! is_single() || ! is_page() ) )
  512.             $show_comments = false;
  513.  
  514.         return apply_filters( 'o2_show_comments_initially', $show_comments );
  515.     }
  516.  
  517.     /**
  518.      * Adds an 'o2' class to the body.
  519.      */
  520.     function body_class( $classes ) {
  521.         $classes[] = 'o2';
  522.         return $classes;
  523.     }
  524.  
  525.     /**
  526.      * Add an author class to posts.
  527.      */
  528.     function post_class( $classes, $class, $post_id ) {
  529.         $post = get_post( $post_id );
  530.         if ( empty( $post ) )
  531.             return $classes;
  532.  
  533.         $user = get_user_by( 'id', $post->post_author );
  534.         if ( empty( $user ) )
  535.             return $classes;
  536.  
  537.         $classes[] = 'author-' . sanitize_html_class( $user->user_nicename, $user->ID );
  538.         return $classes;
  539.     }
  540.  
  541.     public static function get_defaults() {
  542.         return array(
  543.             'o2_enabled'             => true, //enable o2 functionality by default
  544.             'front_side_post_prompt' => __( 'Hi, {name}! What\'s happening?', 'o2' ),
  545.             'enable_resolved_posts'  => false,
  546.             'mark_posts_unresolved'  => false
  547.         );
  548.     }
  549.  
  550.     /**
  551.      * Set o2_option defaults, merge with get_theme_support, merge with pre-existing o2_options
  552.      */
  553.     public function get_settings() {
  554.         // Set o2 defaults
  555.         $settings = $defaults = self::get_defaults();
  556.  
  557.         // Get theme support options, merge with $settings
  558.         if ( is_array( get_theme_support( 'o2' ) ) ) {
  559.             $_settings = current( get_theme_support( 'o2' ) );
  560.             $settings  = self::settings_merge( $defaults, $settings, $_settings );
  561.         }
  562.  
  563.         // Get pre-existing o2_options, merge with $settings
  564.         $_settings = get_option( 'o2_options', $settings );
  565.         $settings  = self::settings_merge( $settings, $settings, $_settings );
  566.  
  567.         $settings = apply_filters( 'o2_get_settings', $settings );
  568.  
  569.         update_option( 'o2_options', $settings );
  570.  
  571.         return $settings;
  572.     }
  573.  
  574.     /**
  575.      * Add the required strings for logged out commenters
  576.      */
  577.     public static function required_comment_fields( $options ) {
  578.         if ( 1 == get_option( 'require_name_email' ) ) {
  579.             $options['strings']['commentName']  = __( 'Name (required)', 'o2' );
  580.             $options['strings']['commentEmail'] = __( 'Email (required)', 'o2' );
  581.         } else {
  582.             $options['strings']['commentName']  = __( 'Name', 'o2' );
  583.             $options['strings']['commentEmail'] = __( 'Email', 'o2' );
  584.         }
  585.         return $options;
  586.     }
  587.  
  588.     /**
  589.      * Logic to merge option/default arrays
  590.      */
  591.     public function settings_merge( $defaults, $settings, $_settings ) {
  592.         if ( isset( $_settings ) && is_array( $_settings ) ) {
  593.             foreach ( $_settings as $key => $value ) {
  594.                 switch ( $key ) {
  595.                     case 'o2_enabled' :
  596.                         if ( isset( $value ) )
  597.                             $settings[ $key ] = (bool) $value;
  598.  
  599.                         break;
  600.  
  601.                     case 'front_side_post_prompt' :
  602.                         if ( isset( $value ) )
  603.                             $settings[ $key ] = esc_attr( $value );
  604.  
  605.                         break;
  606.  
  607.                     case 'enable_resolved_posts' :
  608.                         if ( isset( $value ) )
  609.                             $settings[ $key ] = (bool) $value;
  610.  
  611.                         break;
  612.  
  613.                     case 'mark_posts_unresolved' :
  614.                         if ( isset( $value ) )
  615.                             $settings[ $key ] = (bool) $value;
  616.  
  617.                         break;
  618.  
  619.                     default:
  620.                         continue;
  621.  
  622.                         break;
  623.                 }
  624.             }
  625.         }
  626.  
  627.         $settings = wp_parse_args( $settings, $defaults );
  628.  
  629.         return $settings;
  630.     }
  631.  
  632.     /**
  633.      * Set up options for Customizer
  634.      */
  635.     public function customize_register( $wp_customize ) {
  636.         $defaults = self::get_defaults();
  637.  
  638.         $wp_customize->add_section( 'o2_options', array(
  639.             'title'         => __( 'Theme Options', 'o2' ),
  640.             'priority'      => 35,
  641.         ) );
  642.  
  643.         $wp_customize->add_setting( 'o2_options[front_side_post_prompt]', array(
  644.             'default'       => esc_attr( $defaults['front_side_post_prompt'] ),
  645.             'type'          => 'option',
  646.         ) );
  647.  
  648.         $wp_customize->add_control( 'o2_options[front_side_post_prompt]', array(
  649.             'label'         => __( 'Front-end Post Prompt: Use {name} for user\'s name', 'o2' ),
  650.             'section'       => 'o2_options',
  651.             'priority'      => 1,
  652.         ) );
  653.  
  654.         $wp_customize->add_setting( 'o2_options[enable_resolved_posts]', array(
  655.             'default'       => (bool) $defaults['enable_resolved_posts'],
  656.             'type'          => 'option',
  657.         ) );
  658.  
  659.         $wp_customize->add_control( 'o2_options[enable_resolved_posts]', array(
  660.             'label'         => __( 'Enable "To Do" Module', 'o2' ),
  661.             'section'       => 'o2_options',
  662.             'type'          => 'checkbox',
  663.             'priority'      => 2,
  664.         ) );
  665.  
  666.         $wp_customize->add_setting( 'o2_options[mark_posts_unresolved]', array(
  667.             'default'       => (bool) $defaults['mark_posts_unresolved'],
  668.             'type'          => 'option',
  669.         ) );
  670.  
  671.         $wp_customize->add_control( 'o2_options[mark_posts_unresolved]', array(
  672.             'label'         => __( 'Mark New Posts "To Do"', 'o2' ),
  673.             'section'       => 'o2_options',
  674.             'type'          => 'checkbox',
  675.             'priority'      => 3,
  676.         ) );
  677.     }
  678.  
  679.     /**
  680.     * Returns what type of 'view'/'page' is being returned by WordPress (home, single, search, etc)
  681.     */
  682.     public static function get_view_type( ) {
  683.         $type = 'home';
  684.         if ( is_home() )
  685.             $type = 'home';
  686.         else if ( is_page() )
  687.             $type = 'page';
  688.         else if ( is_singular() )
  689.             $type = 'single';
  690.         else if ( is_search() )
  691.             $type = 'search';
  692.         else if ( is_archive() )
  693.             $type = 'archive';
  694.         else if ( is_404() )
  695.             $type = '404';
  696.         return apply_filters( 'o2_view_type', $type );
  697.     }
  698.  
  699.     /*
  700.      *
  701.      */
  702.     public function get_current_page_title() {
  703.         global $wp_query;
  704.         $page_title = '';
  705.         $obj = get_queried_object();
  706.  
  707.         if ( is_home() )
  708.             $page_title = get_bloginfo( 'name', 'display' );
  709.         else if ( is_page() )
  710.             $page_title = the_title( '', '', false );
  711.         elseif ( is_author() ) {
  712.             $page_title = sprintf( __( 'Posts by %s', 'o2' ), $obj->display_name );
  713.             $current_user = wp_get_current_user();
  714.             if ( $current_user instanceof WP_User ) {
  715.                 if ( $current_user->display_name === $obj->display_name ) {
  716.                     $page_title = __( 'My Posts', 'o2' );
  717.                 }
  718.             }
  719.         } elseif ( is_category() ) {
  720.             $page_title = sprintf( __( 'Posts categorized as %s', 'o2' ), single_cat_title( '', false ) );
  721.         } elseif ( is_tag() ) {
  722.             $query_slugs = explode( ',', get_query_var( 'tag' ) );
  723.             $tag_titles = array();
  724.             foreach( (array) $query_slugs as $query_slug ) {
  725.                 if ( ! $query_slug ) {
  726.                     continue;
  727.                 }
  728.                 $query_slug = strip_tags( wp_kses_no_null( trim( $query_slug ) ) );
  729.                 $found_tag = get_term_by( 'slug', $query_slug, 'post_tag' );
  730.                 if ( $found_tag ) {
  731.                     $tag_titles[] = sprintf( '#%s', $found_tag->name );
  732.                 }
  733.             }
  734.             $page_title = implode( ', ', $tag_titles );
  735.         } elseif ( is_search() ) {
  736.             $page_title = sprintf( __( "Posts containing &lsquo;%s&rsquo; (%d)", 'o2' ), get_search_query(), $wp_query->found_posts );
  737.         } elseif ( is_404() ) {
  738.             $page_title = __( 'Page Not Found', 'o2' );
  739.         } elseif ( is_archive() ) {
  740.             if ( is_day() )
  741.                 $page_title =  __( 'Daily Archives: ', 'o2' ) . get_the_date();
  742.             elseif ( is_month() )
  743.                 $page_title = __( 'Monthly Archives: ', 'o2' ) . get_the_date( 'F Y' );
  744.             elseif ( is_year() )
  745.                 $page_title = __( 'Yearly Archives: ', 'o2' ) . get_the_date( 'Y' );
  746.             else
  747.                 $page_title = __( 'Post Archives', 'o2' );
  748.         }
  749.  
  750.         $page_title = apply_filters( 'the_title', $page_title );
  751.  
  752.         // We run a separate filter for the page title because the_title filter
  753.         // will change other content on the page (like menu items) that we don't
  754.         // want to change
  755.  
  756.         $page_title = apply_filters( 'o2_page_title', $page_title );
  757.  
  758.         return $page_title;
  759.     }
  760.  
  761.     /*
  762.      * Add our special query vars (i.e. for login complete)
  763.      */
  764.     public function add_query_vars() {
  765.         global $wp;
  766.         $wp->add_query_var( 'o2_login_complete' );
  767.     }
  768.  
  769.     public function wp_head() {
  770.         // Suppress output; will show things once we're fully loaded
  771.         ?>
  772.         <style>
  773.         <?php echo esc_js( $this->get_application_container() ); ?> {
  774.             display: none;
  775.         }
  776.         </style>
  777.         <?php
  778.     }
  779.  
  780.     /**
  781.      * Start setting up o2 on the client side.
  782.      */
  783.     public function wp_footer() {
  784.         $this->run_browser_check();
  785.  
  786.         // Bootstrap the users
  787.         global $o2_userdata;
  788.         if ( is_array( $o2_userdata ) && count( $o2_userdata ) > 0 ) {
  789.             echo "<script class='o2-user-data' type='application/json' style='display:none'>";
  790.             echo json_encode( $o2_userdata );
  791.             echo "</script>\n";
  792.         }
  793.  
  794.         $login_complete_var = get_query_var( 'o2_login_complete' );
  795.         if ( ! empty( $login_complete_var ) ) {
  796.             ?>
  797.             <script>
  798.                 jQuery( document ).ready( function( $ ) {
  799.                     if ( "undefined" != typeof window.opener && null != window.opener ) {
  800.                         window.opener.o2.App.onLogInComplete();
  801.                     }
  802.                     window.close();
  803.                 });
  804.             </script>
  805.             <?php
  806.             do_action( 'o2_wp_footer_nopriv' );
  807.         } else {
  808.             ?>
  809.             <script>
  810.                 jQuery(document).ready(function($) {
  811.                     var bootstrap = { data: [] };
  812.                     o2Data = $( '.o2-data' );
  813.                     if ( o2Data.length > 0 ) {
  814.                         o2Data.each( function() {
  815.                             // Parse the JSON that's embedded in the page and add it to the bootstrap data
  816.                             var me = $( this );
  817.                             var thread;
  818.                             try {
  819.                                 thread = $.parseJSON( me.text() );
  820.                             } catch( e ) {
  821.                                 thread = false;
  822.                                 console.log( '$.parseJSON failure: ' + me.text() );
  823.                             }
  824.                             if ( false !== thread ) {
  825.                                 _.each( thread, function( frag ) {
  826.                                     bootstrap.data.push( frag );
  827.                                 } );
  828.                             }
  829.                             me.remove();
  830.                         } );
  831.                     }
  832.  
  833.                     // Merge o2Config into o2 itself
  834.                     o2 = $.extend( o2, o2Config );
  835.  
  836.                     // Some generally-useful references
  837.                     o2.$body = $( 'body' );
  838.                     o2.$appContainer = $( o2.options.appContainer );
  839.  
  840.                     // As soon as o2 loads, poll for new content to account
  841.                     // for Chrome's caching weirdness on back/tab-recovery.
  842.                     o2.$appContainer.on( 'ready.o2', function() {
  843.                         o2.Polling.poll();
  844.                     } );
  845.  
  846.                     // Bootstrap o2 with any in-page content
  847.                     o2.start( bootstrap );
  848.                 });
  849.             </script>
  850.             <?php
  851.             do_action( 'o2_wp_footer' );
  852.         }
  853.     }
  854.  
  855.     /**
  856.      * Check for IE8, IE9 and inject a little Javascript to warn of compatibility issues
  857.      */
  858.     public function run_browser_check() {
  859.  
  860.         if ( ! preg_match_all( "/.*MSIE\s*([0-9.]+).*/", $_SERVER['HTTP_USER_AGENT'], $matches ) ) {
  861.             return;
  862.         }
  863.  
  864.         if ( (int) $matches[1][0] > 9 ) {
  865.             return;
  866.         }
  867.  
  868.         $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' );
  869.  
  870.         // Use browser comments to ensure a cached version isn't accidentally shown.
  871.         ?>
  872.         <!--[if lte IE 9]>
  873.         <script type="text/javascript">
  874.             jQuery( document ).ready( function() {
  875.                 jQuery( 'body' ).prepend( '<div class="o2-browser-warning"><?php echo esc_js( $text ); ?></div>' );
  876.             } );
  877.         </script>
  878.         <![endif]-->
  879.         <?php
  880.     }
  881.  
  882.     /**
  883.      * Embed JSONified post+comment data into each thread (post) for backbone consumption
  884.      */
  885.     public static function add_json_data( $content ) {
  886.         global $post;
  887.  
  888.         // password protected post? return immediately (password protected pages are OK)
  889.         if ( ! is_page() && ! empty( $post->post_password ) ) {
  890.             return $content;
  891.         }
  892.  
  893.         $conversation = array();
  894.         if ( is_single() || is_category() || is_archive() || is_author() || is_home() || is_page() || is_search() ) {
  895.             if ( current_filter() === 'the_content' ) {
  896.                 $conversation[] = o2_Fragment::get_fragment( $post, array( 'find-adjacent' => is_single(), 'the_content' => $content ) );
  897.             } else {
  898.                 // Other content filters (particularly, the_excerpt) may try to strip tags, which causes issues
  899.                 // for the various actions that o2 adds to the end of the content.
  900.                 $conversation[] = o2_Fragment::get_fragment( $post, array( 'find-adjacent' => is_single() ) );
  901.             }
  902.  
  903.             // Append the encoded conversation to the content in a hidden script block
  904.             $content .= "<script class='o2-data' id='o2-data-{$post->ID}' data-post-id='{$post->ID}' type='application/json' style='display:none'>";
  905.             $content .= json_encode( $conversation );
  906.             $content .= "</script>\n";
  907.         }
  908.  
  909.         return $content;
  910.     }
  911.  
  912.     /**
  913.     * Remove oembed handlers that are incompatible with o2
  914.     */
  915.     public function remove_oembed_handlers() {
  916.         wp_oembed_remove_provider( '#https?://(.+\.)?polldaddy\.com/.*#i' );
  917.         wp_oembed_remove_provider( '#https?://poll\.fm/.*#i' );
  918.     }
  919.  
  920.     /**
  921.     * Remove cached incompatible oembed results that a previous theme
  922.     * may have saved
  923.     */
  924.     public function remove_cached_incompatible_oembed( $html, $url, $args ) {
  925.         if ( false !== strpos( $html, 'polldaddy.com' ) || false !== strpos( $html, 'poll.fm' ) ) {
  926.             return $url;
  927.         }
  928.         return $html;
  929.     }
  930.  
  931.     /**
  932.      * Transforms the WP_Locale translations for the Moment.js JavaScript class.
  933.      *
  934.      * @param $locale WP_Locale - A locale object
  935.      * @param $json_encode bool - Whether to encode the result. Default true.
  936.      * @return string|array     - The translations object.
  937.      */
  938.     function get_i18n_moment( $locale, $json_encode = true ) {
  939.         $moment = array(
  940.             'months'         => array_values( $locale->month ),
  941.             'monthsShort'    => array_values( $locale->month_abbrev ),
  942.             'weekdays'       => array_values( $locale->weekday ),
  943.             'weekdaysShort'  => array_values( $locale->weekday_abbrev ),
  944.             'weekdaysMin'    => array_values( $locale->weekday ),
  945.             'relativeTime'   => array(
  946.                 'future' => strip_tags( wp_kses_no_null( trim( _x( 'in %s', 'time from now', 'o2' ) ) ) ),
  947.                 'past'   => strip_tags( wp_kses_no_null( trim( _x( '%s ago', 'time ago', 'o2' ) ) ) ),
  948.                 's'      => strip_tags( wp_kses_no_null( trim( _x( 'a few seconds', 'unit of time', 'o2' ) ) ) ),
  949.                 'm'      => strip_tags( wp_kses_no_null( trim( _x( 'a min', 'abbreviation of minute', 'o2' ) ) ) ),
  950.                 'mm'     => strip_tags( wp_kses_no_null( trim( _x( '%d mins', 'abbreviation of minutes', 'o2' ) ) ) ),
  951.                 'h'      => strip_tags( wp_kses_no_null( trim( __( 'an hour', 'o2' ) ) ) ),
  952.                 'hh'     => strip_tags( wp_kses_no_null( trim( __( '%d hours', 'o2' ) ) ) ),
  953.                 'd'      => strip_tags( wp_kses_no_null( trim( __( 'a day', 'o2' ) ) ) ),
  954.                 'dd'     => strip_tags( wp_kses_no_null( trim( __( '%d days', 'o2' ) ) ) ),
  955.                 'M'      => strip_tags( wp_kses_no_null( trim( __( 'a month', 'o2' ) ) ) ),
  956.                 'MM'     => strip_tags( wp_kses_no_null( trim( __( '%d months', 'o2' ) ) ) ),
  957.                 'y'      => strip_tags( wp_kses_no_null( trim( __( 'a year', 'o2' ) ) ) ),
  958.                 'yy'     => strip_tags( wp_kses_no_null( trim( __( '%d years', 'o2' ) ) ) ),
  959.             ),
  960.         );
  961.  
  962.         if ( $json_encode )
  963.             return json_encode( $moment );
  964.         else
  965.             return $moment;
  966.     }
  967.  
  968.     /**
  969.      * Redirect to attachments. Rather than trying to render them somehow,
  970.      * if someone links to an "attachment page", just intercept that and send
  971.      * the user directly the actual attachment resource (image, PDF, whatever)
  972.      */
  973.     function attachment_redirect() {
  974.         if ( is_attachment() ) {
  975.             wp_safe_redirect( wp_get_attachment_url( get_the_ID() ) );
  976.             exit;
  977.         }
  978.     }
  979.  
  980.     /**
  981.      * Enforce selected discussion settings to fixed values
  982.      */
  983.     function update_discussion_settings() {
  984.         if ( is_admin() && current_user_can( 'manage_options' ) ) {
  985.             // for most options, update_option will not write the db if the option hasn't changed,
  986.             // so it is OK to simply call update_option without checking if the
  987.             // option needs updating first
  988.  
  989.             // page_comments - o2 doesn't page - so should always be empty = not paged
  990.             update_option( 'page_comments', '' );
  991.  
  992.             // comments_per_page, default_comments_page - we don't use these, but let's make
  993.             // sure a reasonable value is present
  994.             $comments_per_page = get_option( 'comments_per_page' );
  995.             if ( 50 != $comments_per_page ) {
  996.                 update_option( 'comments_per_page', 50 );
  997.             }
  998.             update_option( 'default_comments_page', 'newest' );
  999.  
  1000.             // comment_order - o2 always does older at top, so enforce "asc"
  1001.             update_option( 'comment_order', 'asc' );
  1002.         }
  1003.     }
  1004.  
  1005.     function admin_enqueue_scripts( $hook ) {
  1006.         // Settings > Discussion
  1007.         if ( 'options-discussion.php' == $hook ) {
  1008.             $script_url = plugins_url( 'js/admin/discussion-settings.js', O2__FILE__ );
  1009.             wp_enqueue_script( 'o2-admin-discussion', $script_url, array( 'jquery' ) );
  1010.         }
  1011.     }
  1012.  
  1013.     /**
  1014.      * If an o2 compatible theme has just been activated, set some defaults
  1015.      * If it has no widgets set up in any of its widget areas, add some
  1016.      */
  1017.     public function on_activating_o2_compatible_theme() {
  1018.         // Only on the themes page in wp admin
  1019.         if ( ! is_admin() || FALSE === strpos( $_SERVER['REQUEST_URI'], '/wp-admin/themes.php' ) ) {
  1020.             return;
  1021.         }
  1022.  
  1023.         // Only on activation
  1024.         if ( FALSE === strpos( $_SERVER['REQUEST_URI'], 'activated=' ) ) {
  1025.             return;
  1026.         }
  1027.  
  1028.         // If they have just activated an o2 capable theme,
  1029.         if ( ! current_theme_supports( 'o2' ) ) {
  1030.             return;
  1031.         }
  1032.  
  1033.         // Set some defaults
  1034.         // thread_comments - turn on initially on for o2
  1035.         update_option( 'thread_comments', '1' );
  1036.  
  1037.         // comment_whitelist - turn off initially for o2
  1038.         delete_option( 'comment_whitelist' );
  1039.  
  1040.         // thread_comments_depth - set initially to 10 for o2
  1041.         $thread_comments_depth = get_option( 'thread_comments_depth' );
  1042.         if ( 10 != $thread_comments_depth ) {
  1043.             update_option( 'thread_comments_depth', 10 );
  1044.         }
  1045.  
  1046.         // Enable Post Likes on the homepage
  1047.         // The format for storing this option is ugly, and there's no API to access it
  1048.         if ( function_exists( 'wpl_is_enabled_sitewide' ) ) {
  1049.             // Turn on 'index' display for Post Likes if any sharing options are configured
  1050.             if ( $sharing_options = get_option( 'sharing-options' ) ) {
  1051.                 if (
  1052.                     empty( $sharing_options['global']['show'] )
  1053.                 ||
  1054.                     (
  1055.                         'index' != $sharing_options['global']['show' ]
  1056.                     &&
  1057.                         !in_array( 'index', (array) $sharing_options['global']['show'] )
  1058.                     )
  1059.                 ) {
  1060.                     if ( is_array( $sharing_options['global']['show'] ) ) {
  1061.                         // Add index to the array of where it's enabled
  1062.                         $sharing_options['global']['show'][] = 'index';
  1063.                     } else if ( is_string( $sharing_options['global']['show'] ) ) {
  1064.                         // Default is 'posts' but when it's an array it's 'post', so let's
  1065.                         // just force the array and activate everywhere
  1066.                         $sharing_options['global']['show'] = array( 'index', 'post', 'page', 'attachment' );
  1067.                     }
  1068.                     update_option( 'sharing-options', $sharing_options );
  1069.                 }
  1070.             }
  1071.         }
  1072.  
  1073.         // Enable Comment Likes
  1074.         if ( function_exists( 'wpl_is_comments_enabled_sitewide' ) ) {
  1075.             update_option( 'jetpack_comment_likes_enabled', true );
  1076.         }
  1077.  
  1078.         // Use Widget Helper to add some widgets
  1079.         $widget_helper = new o2_Widget_Helper();
  1080.  
  1081.         // Has P2 been run on this blog before?  If so, let's start with
  1082.         // its widgets (except for p2_ widgets)
  1083.         $p2_mods = get_option( 'theme_mods_pub/p2' );
  1084.         if ( false === $p2_mods ) {
  1085.             $p2_mods = get_option( 'theme_mods_p2' );
  1086.         }
  1087.  
  1088.         if ( false !== $p2_mods ) {
  1089.             $p2_sidebar_widgets = array();
  1090.             if ( isset( $p2_mods['sidebars_widgets']['data']['sidebar-1'] ) ) {
  1091.                 $p2_sidebar_widgets = $p2_mods['sidebars_widgets']['data']['sidebar-1'];
  1092.             }
  1093.  
  1094.             foreach( (array) $p2_sidebar_widgets as $p2_sidebar_widget ) {
  1095.                 // filter out any p2_ widget
  1096.                 if ( 'p2_' != substr( $p2_sidebar_widget, 0, 3 ) ) {
  1097.                     $widget_helper->add_existing_widget( $p2_sidebar_widget );
  1098.                 }
  1099.             }
  1100.         }
  1101.  
  1102.         // Determine which o2 widgets need to be added
  1103.         $search_added     = $widget_helper->has_widget( 'search', 'sidebar-1' );
  1104.         $filter_added     = $widget_helper->has_widget( 'o2-filter-widget', 'sidebar-1' );
  1105.         $activity_added   = $widget_helper->has_widget( 'o2-live-comments', 'sidebar-1' );
  1106.  
  1107.         // Add the Search widget
  1108.         if ( ! $search_added ) {
  1109.             $widget_helper->add_new_widget(
  1110.                 'search' /* widget base ID from widget class def */,
  1111.                 true, /* true: multiwidget, false: not */
  1112.                 array(
  1113.                     'title' => __( 'Search', 'o2' ) /* widget settings */
  1114.                 ),
  1115.                 'sidebar-1' /* widget area to add to, empty for default */
  1116.             );
  1117.         }
  1118.  
  1119.         // Add the o2 Filter Widget
  1120.         if ( ! $filter_added ) {
  1121.             $widget_helper->add_new_widget(
  1122.                 'o2-filter-widget', /* widget base ID from widget class def */
  1123.                 true,   /* true: multiwidget, false: not */
  1124.                 array(
  1125.                     'title' => '' /* widget settings */
  1126.                 ),
  1127.                 'sidebar-1'
  1128.             );
  1129.         }
  1130.  
  1131.         // Add the o2 Recent Activity Widget
  1132.         if ( ! $activity_added ) {
  1133.             $widget_helper->add_new_widget(
  1134.                 'o2-live-comments-widget' /* widget base ID from widget class def */,
  1135.                 true, /* true: multiwidget, false: not */
  1136.                 array(
  1137.                     'title' => __( 'Recent Activity', 'o2' ), /* widget settings */
  1138.                     'kind' => 'both',
  1139.                     'number' => 10
  1140.                 ),
  1141.                 'sidebar-1' /* widget area to add to, empty for default */
  1142.             );
  1143.         }
  1144.  
  1145.         // Remove Recent Posts and Recent Comments widgets
  1146.         if ( $widget_helper->has_widget( 'recent-comments' ) ) {
  1147.             $widget_helper->remove_widget_instances( 'recent-comments' );
  1148.         }
  1149.         if ( $widget_helper->has_widget( 'recent-posts' ) ) {
  1150.             $widget_helper->remove_widget_instances( 'recent-posts' );
  1151.         }
  1152.     }
  1153.  
  1154.     /*
  1155.      * Returns true if the user agent is recognized as a mobile device (or a tablet)
  1156.      */
  1157.     public static function is_mobile() {
  1158.         $is_mobile = ( function_exists( 'jetpack_is_mobile' ) ) ? jetpack_is_mobile() : wp_is_mobile();
  1159.         return $is_mobile;
  1160.     }
  1161.  
  1162.     /*
  1163.      * Returns true if the user agent is recognized as a tablet
  1164.      */
  1165.     public static function is_tablet() {
  1166.         $is_tablet = ( class_exists( 'Jetpack_User_Agent_Info' ) ) ? Jetpack_User_Agent_Info::is_tablet() : false;
  1167.         return $is_tablet;
  1168.     }
  1169.  
  1170.     /**
  1171.      * Create a new comment with message "This comment has been permanently deleted."
  1172.      * and put it in place of deleted comment.
  1173.      *
  1174.      * @param $comment_ID
  1175.      */
  1176.     public function delete_comment_override( $comment_ID ) {
  1177.         $children = get_comments( array( 'status' => 'approve', 'parent' => $comment_ID ) );
  1178.         if ( ! empty( $children ) ) {
  1179.             $old_comment    = get_comment( $comment_ID );
  1180.             $comment_to_add = $old_comment;
  1181.  
  1182.             unset( $comment_to_add->comment_ID );
  1183.             $comment_to_add->comment_approved = 1;
  1184.             $comment_to_add->comment_content = __( 'This comment was deleted.', 'o2' );
  1185.  
  1186.             $new_comment_id = wp_insert_comment( (array) $comment_to_add );
  1187.             $comment_created = get_comment_meta( $comment_ID, 'o2_comment_created', true );
  1188.             if ( empty ( $comment_created ) ) {
  1189.                 $comment = get_comment( $comment_ID );
  1190.                 $comment_created = strtotime( $comment->comment_date_gmt );
  1191.             }
  1192.  
  1193.             update_comment_meta( $new_comment_id, 'o2_comment_created', $comment_created );
  1194.  
  1195.             o2_Fragment::bump_comment_modified_time( $new_comment_id );
  1196.             update_comment_meta( $new_comment_id, 'o2_comment_prev_deleted', $comment_ID );
  1197.  
  1198.             $clean_comment_ids = array();
  1199.  
  1200.             foreach ( $children as $child ) {
  1201.                 $clean_comment_ids[] = $child->comment_ID;
  1202.  
  1203.                 $child->comment_parent = $new_comment_id;
  1204.                 wp_update_comment( (array) $child );
  1205.             }
  1206.  
  1207.             $clean_comment_ids[] = $new_comment_id;
  1208.             clean_comment_cache( $clean_comment_ids );
  1209.         }
  1210.     }
  1211.  
  1212.     /**
  1213.      * Add a timestamp named o2_comment_created so we can maintain
  1214.      * when the comment was created even if comment is later modified.
  1215.      *
  1216.      * @param $comment_ID
  1217.      * @param $comment
  1218.      */
  1219.     public function insert_comment_actions( $comment_ID, $comment ) {
  1220.         update_comment_meta( $comment_ID, 'o2_comment_created', current_time( 'timestamp', true ) );
  1221.     }
  1222.  
  1223.     /**
  1224.      * Returns true if comment has approved child.
  1225.      *
  1226.      * @param $comment_ID
  1227.      *
  1228.      * @return bool
  1229.      */
  1230.     public function has_approved_child( $comment_ID ) {
  1231.         $children = get_comments( array( 'status' => 'approve', 'parent' => $comment_ID ) );
  1232.  
  1233.         return ! empty( $children );
  1234.     }
  1235.  
  1236.     /**
  1237.      * When un-trashing a comment, traverse through this comment's parents and
  1238.      * add o2_comment_has_children flag where needed.
  1239.      *
  1240.      * @param $comment_ID
  1241.      * @param bool $comment
  1242.      */
  1243.     public function add_trashed_parents( $comment_ID, $comment = false ) {
  1244.         $comment = ! empty( $comment ) ? $comment : get_comment( $comment_ID );
  1245.         if (  0 < $comment->comment_parent ) {
  1246.             $parent = get_comment( $comment->comment_parent );
  1247.             if ( 'trash' == $parent->comment_approved ) {
  1248.                 $this->add_trashed_parents( $parent->comment_ID, $parent );
  1249.                 update_comment_meta( $parent->comment_ID, 'o2_comment_has_children', true );
  1250.                 o2_Fragment::bump_comment_modified_time( $parent->comment_ID );
  1251.             }
  1252.         }
  1253.     }
  1254.  
  1255.     /**
  1256.      * If this comment has no approved siblings, then go ahead and delete its parent as well.
  1257.      *
  1258.      * @param $comment_ID
  1259.      * @param bool|object $comment
  1260.      */
  1261.     public function remove_trashed_parents( $comment_ID, $comment = false ) {
  1262.         $child_count = 0;
  1263.         $has_approved_children = false;
  1264.  
  1265.         if ( empty( $comment ) ) {
  1266.  
  1267.             // If $comment isn't set, then assume we haven't recursed and setup vars.
  1268.             $child_count = 1;
  1269.             $comment = get_comment( $comment_ID );
  1270.             $has_approved_children = $this->has_approved_child( $comment_ID );
  1271.         }
  1272.  
  1273.         if ( ! $has_approved_children && 0 < $comment->comment_parent ) {
  1274.             $parent = get_comment( $comment->comment_parent );
  1275.             if ( 'trash' == $parent->comment_approved ) {
  1276.                 $children = get_comments(
  1277.                     array(
  1278.                         'count' => true,
  1279.                         'parent' => $parent->comment_ID
  1280.                     )
  1281.                 );
  1282.                 if ( $child_count == $children ) {
  1283.                     delete_comment_meta( $parent->comment_ID, 'o2_comment_has_children', true );
  1284.                     o2_Fragment::bump_comment_modified_time( $parent->comment_ID );
  1285.                     $this->remove_trashed_parents( $parent->comment_ID, $parent );
  1286.                 }
  1287.             }
  1288.         }
  1289.     }
  1290.  
  1291.     /**
  1292.      * Add has_children flag to deleted comments so that we can query for only trashed comments
  1293.      * that have children in o2_Fragent:get_fragment_from_post() for bootstrapping.
  1294.      *
  1295.      * @param $comment_ID
  1296.      */
  1297.     public function maybe_set_comment_has_children( $comment_ID ) {
  1298.         $children = get_comments( array( 'parent' => $comment_ID ) );
  1299.  
  1300.         if ( ! empty( $children ) ) {
  1301.             update_comment_meta( $comment_ID, 'o2_comment_has_children', true );
  1302.         }
  1303.     }
  1304.  
  1305.     public function ajax_get_o2_userdata() {
  1306.         // returns the o2 userdata for the given userLogin, or an error if a bad userLogin was given
  1307.         // note:  both priv and nopriv hit this, since o2s can be read by nopriv users
  1308.  
  1309.         $ok_to_serve_data = apply_filters( 'o2_read_api_ok_to_serve_data', true );
  1310.         if ( ! $ok_to_serve_data ) {
  1311.             wp_send_json_error( array( 'errorText' => 'Unauthorized' ) );
  1312.         }
  1313.  
  1314.         if ( isset( $_GET['userlogin'] ) ) {
  1315.             // V1 userlogin (singular)
  1316.             // it will be OK to remove this case after the new V2 has been deployed
  1317.             // for a day or two, so as to not disrupt V1 clients that are still active
  1318.             $user_login = $_GET['userlogin'];
  1319.             $user = get_user_by( 'login', $user_login );
  1320.  
  1321.             if ( false === $user ) {
  1322.                 wp_send_json_error( array( 'errorText' => 'Invalid request' ) );
  1323.             }
  1324.  
  1325.             // Otherwise, create and send the model
  1326.             $user_data = get_userdata( $user->ID );
  1327.             $user_model = o2_Fragment::get_model_from_userdata( $user_data );
  1328.             wp_send_json_success( $user_model );
  1329.         } else {
  1330.             // V2 userlogins (array of 1 or more)
  1331.             $user_logins = $_GET['userlogins'];
  1332.             $user_models = array();
  1333.  
  1334.             foreach( (array) $user_logins as $user_login ) {
  1335.                 $user = get_user_by( 'login', $user_login );
  1336.                 if ( false != $user ) {
  1337.                     $user_data = get_userdata( $user->ID );
  1338.                     $user_models[] = o2_Fragment::get_model_from_userdata( $user_data );
  1339.                 }
  1340.             }
  1341.  
  1342.             if ( 0 == count( $user_models ) ) {
  1343.                 wp_send_json_error( array( 'errorText' => 'Invalid request' ) );
  1344.             }
  1345.  
  1346.             wp_send_json_success( $user_models );
  1347.         }
  1348.     }
  1349.  
  1350.     /**
  1351.      * Check if o2 is active on the current site.
  1352.      */
  1353.     function is_enabled() {
  1354.         $o2_is_enabled = false;
  1355.  
  1356.         $o2_options = get_option( 'o2_options' );
  1357.         if ( is_array( $o2_options ) ) {
  1358.             if ( isset( $o2_options['o2_enabled'] ) ) {
  1359.                 $o2_is_enabled = $o2_options['o2_enabled'];
  1360.             }
  1361.         }
  1362.  
  1363.         return $o2_is_enabled;
  1364.     }
  1365.  
  1366.     /**
  1367.      * This is really only needed for the first load of o2 after activation.
  1368.      * Useful for flushing rewrite rules, and that kind of thing.
  1369.      */
  1370.     public function first_load() {
  1371.         if ( ! get_option( 'o2-just-activated', false ) ) {
  1372.             return;
  1373.         }
  1374.  
  1375.         delete_option( 'o2-just-activated' );
  1376.  
  1377.         flush_rewrite_rules( false );
  1378.     }
  1379. }
  1380.  
  1381. /*
  1382.  * Breathe
  1383.  */
  1384. global $o2;
  1385. $o2 = new o2();
  1386.  
  1387. /**
  1388.  * Plugin activation
  1389.  */
  1390. function o2_plugin_activation_hook() {
  1391.  
  1392.     // We need to do some work after o2 is activated.
  1393.     // See o2::first_load()
  1394.     update_option( 'o2-just-activated', true );
  1395.  
  1396.     // Set the default post format to 'aside' on plugin init
  1397.     $default_post_format = apply_filters( 'o2_default_post_format', 'aside' );
  1398.     $current_post_format = get_option( 'default_post_format' );
  1399.  
  1400.     // Only update the post format if a default is not set
  1401.     if ( empty( $current_post_format ) ) {
  1402.         $post_formats = get_theme_support( 'post-formats' );
  1403.         if ( in_array( $default_post_format, $post_formats[0] ) ) {
  1404.             update_option( 'default_post_format', $default_post_format );
  1405.         }
  1406.     }
  1407.  
  1408.     // Disable the Jetpack option for using a mobile theme if Jetpack loaded
  1409.     if ( defined( 'JETPACK__VERSION' ) ) {
  1410.         update_option( 'wp_mobile_disable', true );
  1411.     }
  1412. }
  1413. register_activation_hook( O2__FILE__, 'o2_plugin_activation_hook' );
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement