| @@ -39,8 +39,15 @@ | ||
| 39 | 39 | */ |
| 40 | 40 | public $author = false; |
| 41 | 41 | |
| 42 | 42 | /** |
| 43 | + * Whether an tag is being displayed | |
| 44 | + * | |
| 45 | + * @var object|false | |
| 46 | + */ | |
| 47 | + public $tag = false; | |
| 48 | + | |
| 49 | + /** | |
| 43 | 50 | * Whether a post-format is being displayed |
| 44 | 51 | * |
| 45 | 52 | * @var string|false |
| 46 | 53 | */ |
| @@ -46,15 +53,35 @@ | ||
| 46 | 53 | */ |
| 47 | 54 | public $post_format = false; |
| 48 | 55 | |
| 49 | 56 | /** |
| 50 | - * Whether a reaciton is being displayed | |
| 57 | + * Whether a specific template was selected to load. | |
| 51 | 58 | * |
| 52 | 59 | * @var string|false |
| 53 | 60 | */ |
| 61 | + public $template = false; | |
| 62 | + | |
| 63 | + /** | |
| 64 | + * Whether a reaction is being displayed. | |
| 65 | + * | |
| 66 | + * @var string|false | |
| 67 | + */ | |
| 54 | 68 | public $reaction = false; |
| 55 | 69 | |
| 56 | 70 | /** |
| 71 | + * The current theme. | |
| 72 | + * | |
| 73 | + * @var string | |
| 74 | + */ | |
| 75 | + private $theme = 'default'; | |
| 76 | + /** | |
| 77 | + * Available themes. | |
| 78 | + * | |
| 79 | + * @var array | |
| 80 | + */ | |
| 81 | + private static $themes = array(); | |
| 82 | + | |
| 83 | + /** | |
| 57 | 84 | * Constructor |
| 58 | 85 | * |
| 59 | 86 | * @param Friends $friends A reference to the Friends object. |
| 60 | 87 | */ |
| @@ -67,8 +94,9 @@ | ||
| 67 | 94 | * Register the WordPress hooks |
| 68 | 95 | */ |
| 69 | 96 | private function register_hooks() { |
| 70 | 97 | add_filter( 'pre_get_posts', array( $this, 'friend_posts_query' ), 2 ); |
| 98 | + add_filter( 'pre_get_posts', array( $this, 'exclude_compose_format_from_feed' ), 10 ); | |
| 71 | 99 | add_filter( 'post_type_link', array( $this, 'friend_post_link' ), 10, 2 ); |
| 72 | 100 | add_filter( 'friends_header_widget_title', array( $this, 'header_widget_title' ) ); |
| 73 | 101 | add_filter( 'get_edit_post_link', array( $this, 'friend_post_edit_link' ) ); |
| 74 | 102 | add_filter( 'template_include', array( $this, 'template_override' ) ); |
| @@ -75,27 +103,56 @@ | ||
| 75 | 103 | add_filter( 'wp_loaded', array( $this, 'add_rewrite_rule' ) ); |
| 76 | 104 | add_filter( 'init', array( $this, 'register_friends_sidebar' ) ); |
| 77 | 105 | add_action( 'init', array( $this, 'add_theme_supports' ) ); |
| 78 | 106 | add_action( 'wp_ajax_friends_publish', array( $this, 'ajax_frontend_publish_post' ) ); |
| 107 | + add_action( 'wp_ajax_friends-mention-autocomplete', array( $this, 'ajax_mention_autocomplete' ) ); | |
| 79 | 108 | add_action( 'wp_ajax_friends-change-post-format', array( $this, 'ajax_change_post_format' ) ); |
| 80 | 109 | add_action( 'wp_ajax_friends-load-next-page', array( $this, 'ajax_load_next_page' ) ); |
| 81 | 110 | add_action( 'wp_ajax_friends-autocomplete', array( $this, 'ajax_autocomplete' ) ); |
| 82 | - add_action( 'wp_ajax_friends-in-reply-to-preview', array( $this, 'ajax_in_reply_to_preview' ) ); | |
| 111 | + add_action( 'wp_ajax_friends-set-widget-open-state', array( $this, 'ajax_set_widget_open_state' ) ); | |
| 112 | + add_action( 'wp_ajax_friends-get-post-counts', array( $this, 'ajax_get_post_counts' ) ); | |
| 113 | + add_action( 'friends_search_autocomplete', array( $this, 'autocomplete_user_search' ), 10, 2 ); | |
| 83 | 114 | add_action( 'wp_ajax_friends-star', array( $this, 'ajax_star_friend_user' ) ); |
| 115 | + add_action( 'wp_ajax_friends-move-to-folder', array( $this, 'ajax_move_to_folder' ) ); | |
| 116 | + add_action( 'wp_ajax_friends-create-folder', array( $this, 'ajax_create_folder' ) ); | |
| 84 | 117 | add_action( 'wp_ajax_friends-load-comments', array( $this, 'ajax_load_comments' ) ); |
| 85 | 118 | add_action( 'wp_ajax_friends-reblog', array( $this, 'wp_ajax_reblog' ) ); |
| 86 | 119 | add_action( 'friends_post_footer_first', array( $this, 'reblog_button' ) ); |
| 87 | - add_filter( 'friends_reblog', array( get_called_class(), 'reblog' ), 10, 2 ); | |
| 120 | + add_filter( 'friends_reblog', array( get_called_class(), 'reblog' ), 10, 3 ); | |
| 88 | 121 | add_filter( 'friends_unreblog', array( get_called_class(), 'unreblog' ), 10, 2 ); |
| 89 | - add_action( 'wp_untrash_post_status', array( $this, 'untrash_post_status' ), 10, 3 ); | |
| 122 | + add_action( 'wp_untrash_post_status', array( $this, 'untrash_post_status' ), 10, 2 ); | |
| 90 | 123 | add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); |
| 124 | + add_action( 'template_redirect', array( $this, 'load_theme' ) ); | |
| 125 | + add_action( 'admin_bar_menu', array( $this, 'admin_bar_theme_switcher' ), 100 ); | |
| 126 | + add_action( 'customize_loaded_components', array( $this, 'ensure_widget_editing' ) ); | |
| 127 | + add_action( 'friends_load_theme_default', array( $this, 'default_theme' ) ); | |
| 128 | + add_action( 'friends_load_theme_block', array( $this, 'block_theme' ) ); | |
| 129 | + add_action( 'friends_load_theme_google-reader', array( $this, 'google_reader_theme' ) ); | |
| 130 | + add_action( 'friends_load_theme_mastodon', array( $this, 'mastodon_theme' ) ); | |
| 131 | + add_action( 'friends_load_theme_twitter', array( $this, 'twitter_theme' ) ); | |
| 132 | + add_action( 'friends_load_themes', array( $this, 'register_block_theme' ) ); | |
| 133 | + add_action( 'friends_load_themes', array( $this, 'register_google_reader_theme' ) ); | |
| 134 | + add_action( 'friends_load_themes', array( $this, 'register_mastodon_theme' ) ); | |
| 135 | + add_action( 'friends_load_themes', array( $this, 'register_twitter_theme' ) ); | |
| 136 | + add_action( 'init', array( $this, 'register_block_templates' ) ); | |
| 137 | + add_action( 'friends_template_paths', array( $this, 'friends_template_paths' ) ); | |
| 91 | 138 | add_action( 'wp_enqueue_scripts', array( $this, 'dequeue_scripts' ), 99999 ); |
| 92 | 139 | add_action( 'wp_footer', array( $this, 'dequeue_scripts' ) ); |
| 93 | 140 | add_action( 'the_post', array( $this, 'the_post' ), 10, 2 ); |
| 94 | 141 | add_action( 'parse_query', array( $this, 'parse_query' ) ); |
| 95 | 142 | add_filter( 'body_class', array( $this, 'add_body_class' ) ); |
| 143 | + add_filter( 'block_type_metadata_settings', array( $this, 'block_type_metadata_settings' ), 15 ); | |
| 144 | + add_filter( 'pre_render_block', array( $this, 'render_friends_template_part' ), 10, 2 ); | |
| 145 | + add_filter( 'pre_get_block_file_template', array( $this, 'get_friends_block_file_template' ), 10, 3 ); | |
| 146 | + add_filter( 'get_block_templates', array( $this, 'add_friends_template_parts' ), 10, 3 ); | |
| 147 | + add_filter( 'tag_row_actions', array( $this, 'tag_row_actions' ), 10, 2 ); | |
| 96 | 148 | |
| 149 | + add_action( 'friends_after_header', array( $this, 'migration_notification' ) ); | |
| 150 | + add_action( 'wp_ajax_friends_dismiss_migration_notification', array( $this, 'ajax_dismiss_migration_notification' ) ); | |
| 151 | + | |
| 97 | 152 | add_filter( 'friends_override_author_name', array( $this, 'override_author_name' ), 10, 3 ); |
| 153 | + add_filter( 'friends_friend_posts_query_viewable', array( $this, 'expose_opml' ), 10, 2 ); | |
| 154 | + add_filter( 'get_comment_link', array( $this, 'friend_post_comment_link' ), 10, 2 ); | |
| 98 | 155 | } |
| 99 | 156 | |
| 100 | 157 | /** |
| 101 | 158 | * We're asking WordPress to handle the title for us. |
| @@ -100,23 +157,28 @@ | ||
| 100 | 157 | /** |
| 101 | 158 | * We're asking WordPress to handle the title for us. |
| 102 | 159 | */ |
| 103 | 160 | public function add_rewrite_rule() { |
| 104 | - add_rewrite_rule( | |
| 105 | - 'friends/(.*)/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$', | |
| 106 | - 'index.php?pagename=friends/$matches[1]&feed=$matches[2]', | |
| 107 | - 'top' | |
| 161 | + $existing_rules = get_option( 'rewrite_rules' ); | |
| 162 | + $needs_flush = false; | |
| 163 | + | |
| 164 | + $rules = array( | |
| 165 | + '^friends/(.*)/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$' => 'index.php?pagename=friends/$matches[1]&feed=$matches[2]', | |
| 166 | + '^friends/feed/?$' => 'index.php?pagename=friends&feed=feed', | |
| 167 | + '^friends/(.*)/(\d+)/?$' => 'index.php?pagename=friends/$matches[1]&page=$matches[2]', | |
| 168 | + '^friends/(.*)' => 'index.php?pagename=friends/$matches[1]', | |
| 108 | 169 | ); |
| 109 | - add_rewrite_rule( | |
| 110 | - 'friends/(.*)/(\d+)/?$', | |
| 111 | - 'index.php?pagename=friends/$matches[1]&page=$matches[2]', | |
| 112 | - 'top' | |
| 113 | - ); | |
| 114 | - add_rewrite_rule( | |
| 115 | - 'friends/(.*)', | |
| 116 | - 'index.php?pagename=friends/$matches[1]', | |
| 117 | - 'top' | |
| 118 | - ); | |
| 170 | + | |
| 171 | + foreach ( $rules as $rule => $rewrite ) { | |
| 172 | + if ( empty( $existing_rules[ $rule ] ) ) { | |
| 173 | + $needs_flush = true; | |
| 174 | + } | |
| 175 | + add_rewrite_rule( $rule, $rewrite, 'top' ); | |
| 176 | + } | |
| 177 | + | |
| 178 | + if ( $needs_flush ) { | |
| 179 | + flush_rewrite_rules(); | |
| 180 | + } | |
| 119 | 181 | } |
| 120 | 182 | |
| 121 | 183 | /** |
| 122 | 184 | * Run our add_theme_supports if on the frontend. |
| @@ -195,8 +257,19 @@ | ||
| 195 | 257 | 'before_title' => '<h5>', |
| 196 | 258 | 'after_title' => '</h5>', |
| 197 | 259 | ) |
| 198 | 260 | ); |
| 261 | + register_sidebar( | |
| 262 | + array( | |
| 263 | + 'name' => __( 'Friends Sidebar 2 (Twitter theme)', 'friends' ), | |
| 264 | + 'id' => 'friends-sidebar-2', | |
| 265 | + 'description' => __( 'Shown in the right column of the Twitter theme.', 'friends' ), | |
| 266 | + 'before_widget' => '<div class="friends-widget">', | |
| 267 | + 'after_widget' => '</div>', | |
| 268 | + 'before_title' => '<h5>', | |
| 269 | + 'after_title' => '</h5>', | |
| 270 | + ) | |
| 271 | + ); | |
| 199 | 272 | |
| 200 | 273 | if ( Friends::on_frontend() ) { |
| 201 | 274 | add_action( 'customize_register', '__return_true' ); |
| 202 | 275 | } |
| @@ -201,51 +274,176 @@ | ||
| 201 | 274 | add_action( 'customize_register', '__return_true' ); |
| 202 | 275 | } |
| 203 | 276 | } |
| 204 | 277 | |
| 278 | + public static function ensure_widget_editing( $components ) { | |
| 279 | + if ( ! is_array( $components ) ) { | |
| 280 | + $components = array(); | |
| 281 | + } | |
| 282 | + $components[] = 'widgets'; | |
| 283 | + return $components; | |
| 284 | + } | |
| 285 | + | |
| 286 | + public function load_theme() { | |
| 287 | + if ( ! is_user_logged_in() || ! Friends::on_frontend() ) { | |
| 288 | + return; | |
| 289 | + } | |
| 290 | + // Handle theme switching via GET parameter. | |
| 291 | + if ( isset( $_GET['friends_theme'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 292 | + $new_theme = sanitize_text_field( wp_unslash( $_GET['friends_theme'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 293 | + $themes = self::get_themes(); | |
| 294 | + if ( isset( $themes[ $new_theme ] ) ) { | |
| 295 | + update_user_option( get_current_user_id(), 'friends_frontend_theme', $new_theme ); | |
| 296 | + } | |
| 297 | + } | |
| 298 | + | |
| 299 | + $theme = 'default'; | |
| 300 | + $default_theme = get_user_option( 'friends_frontend_theme', get_current_user_id() ); | |
| 301 | + if ( $default_theme ) { | |
| 302 | + $theme = $default_theme; | |
| 303 | + } | |
| 304 | + if ( $this->post_format ) { | |
| 305 | + $post_type_theme = get_user_option( 'friends_frontend_theme_' . $this->post_format, get_current_user_id() ); | |
| 306 | + if ( $post_type_theme ) { | |
| 307 | + $theme = $post_type_theme; | |
| 308 | + } | |
| 309 | + } | |
| 310 | + if ( ! has_action( 'friends_load_theme_' . $theme ) ) { | |
| 311 | + $theme = 'default'; | |
| 312 | + } | |
| 313 | + $this->theme = $theme; | |
| 314 | + do_action( 'friends_load_theme_' . $theme ); | |
| 315 | + } | |
| 316 | + | |
| 317 | + public function friends_template_paths( $file_paths ) { | |
| 318 | + $backup_file_paths = $file_paths; | |
| 319 | + if ( has_filter( 'friends_template_paths_theme_' . $this->theme ) ) { | |
| 320 | + $file_paths = apply_filters( 'friends_template_paths_theme_' . $this->theme, $file_paths ); | |
| 321 | + } | |
| 322 | + if ( empty( $file_paths ) ) { | |
| 323 | + return $backup_file_paths; | |
| 324 | + } | |
| 325 | + | |
| 326 | + return $file_paths; | |
| 327 | + } | |
| 328 | + | |
| 205 | 329 | /** |
| 330 | + * Get an asset version that changes when the local file changes. | |
| 331 | + * | |
| 332 | + * @param string $file Relative asset path. | |
| 333 | + * @return string Asset version. | |
| 334 | + */ | |
| 335 | + private function get_asset_version( $file ) { | |
| 336 | + $path = dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file; | |
| 337 | + if ( file_exists( $path ) ) { | |
| 338 | + return Friends::VERSION . '-' . filemtime( $path ); | |
| 339 | + } | |
| 340 | + | |
| 341 | + return Friends::VERSION; | |
| 342 | + } | |
| 343 | + | |
| 344 | + public function default_theme() { | |
| 345 | + $handle = 'friends'; | |
| 346 | + $file = 'friends.css'; | |
| 347 | + $version = $this->get_asset_version( $file ); | |
| 348 | + wp_enqueue_style( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array(), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ) ); | |
| 349 | + | |
| 350 | + $file = 'friends-default-theme.js'; | |
| 351 | + wp_enqueue_script( 'friends-default-theme', plugins_url( $file, FRIENDS_PLUGIN_FILE ), array( 'jquery', 'friends' ), $this->get_asset_version( $file ), true ); | |
| 352 | + } | |
| 353 | + | |
| 354 | + /** | |
| 206 | 355 | * Reference our script for the /friends page |
| 207 | 356 | */ |
| 208 | 357 | public function enqueue_scripts() { |
| 358 | + if ( ! is_user_logged_in() || ! Friends::on_frontend() ) { | |
| 359 | + return; | |
| 360 | + } | |
| 209 | 361 | global $wp_query; |
| 210 | 362 | |
| 211 | - if ( is_user_logged_in() && Friends::on_frontend() ) { | |
| 212 | - $handle = 'friends'; | |
| 213 | - $file = 'friends.js'; | |
| 214 | - $version = Friends::VERSION; | |
| 215 | - wp_enqueue_script( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array( 'common', 'jquery', 'wp-util' ), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ) ); | |
| 363 | + $handle = 'friends'; | |
| 364 | + $file = 'friends.js'; | |
| 365 | + $version = $this->get_asset_version( $file ); | |
| 366 | + wp_enqueue_script( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array( 'common', 'jquery', 'wp-util' ), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ), true ); | |
| 216 | 367 | |
| 217 | - $query_vars = serialize( $this->get_minimal_query_vars( $wp_query->query_vars ) ); | |
| 368 | + $query_vars = wp_json_encode( $this->get_minimal_query_vars( $wp_query->query_vars ) ); | |
| 218 | 369 | |
| 219 | - $variables = array( | |
| 220 | - 'emojis_json' => plugins_url( 'emojis.json', FRIENDS_PLUGIN_FILE ), | |
| 221 | - 'ajax_url' => admin_url( 'admin-ajax.php' ), | |
| 222 | - 'text_link_expired' => __( 'The link has expired. A new link has been generated, please click it again.', 'friends' ), | |
| 223 | - 'text_undo' => __( 'Undo' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain | |
| 224 | - 'text_trash_post' => __( 'Trash this post', 'friends' ), | |
| 225 | - 'text_del_convers' => __( 'Do you really want to delete this conversation?', 'friends' ), | |
| 226 | - 'text_no_more_posts' => __( 'No more posts available.', 'friends' ), | |
| 227 | - 'query_vars' => $query_vars, | |
| 228 | - 'qv_sign' => sha1( wp_salt( 'nonce' ) . $query_vars ), | |
| 229 | - 'current_page' => get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1, | |
| 230 | - 'max_page' => $wp_query->max_num_pages, | |
| 231 | - ); | |
| 232 | - wp_localize_script( 'friends', 'friends', $variables ); | |
| 370 | + $variables = array( | |
| 371 | + 'emojis_json' => plugins_url( 'emojis.json', FRIENDS_PLUGIN_FILE ), | |
| 372 | + 'ajax_url' => admin_url( 'admin-ajax.php' ), | |
| 373 | + 'rest_base' => rest_url( 'friends/v1/' ), | |
| 374 | + 'rest_nonce' => wp_create_nonce( 'wp_rest' ), | |
| 375 | + 'text_link_expired' => __( 'The link has expired. A new link has been generated, please click it again.', 'friends' ), | |
| 376 | + 'text_undo' => __( 'Undo' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain | |
| 377 | + 'text_trash_post' => __( 'Trash this post', 'friends' ), | |
| 378 | + 'text_del_convers' => __( 'Do you really want to delete this conversation?', 'friends' ), | |
| 379 | + 'text_no_more_posts' => __( 'No more posts available.', 'friends' ), | |
| 380 | + 'text_checking_url' => __( 'Checking URL.', 'friends' ), | |
| 381 | + 'text_refreshed' => __( 'Refreshed', 'friends' ), | |
| 382 | + 'text_refreshing' => __( 'Refreshing', 'friends' ), | |
| 383 | + 'text_compact_mode' => __( 'Compact mode', 'friends' ), | |
| 384 | + 'text_expanded_mode' => __( 'Expanded mode', 'friends' ), | |
| 385 | + 'text_loading_comments' => __( 'Loading comments...', 'friends' ), | |
| 386 | + 'text_still_loading' => __( 'Still loading...', 'friends' ), | |
| 387 | + 'message_delivery_nonce' => wp_create_nonce( 'friends-message-delivery-statuses' ), | |
| 388 | + 'refresh_now' => isset( $_GET['refresh'] ) ? 'true' : 'false', // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 389 | + 'query_vars' => $query_vars, | |
| 390 | + 'qv_sign' => sha1( wp_salt( 'nonce' ) . $query_vars ), | |
| 391 | + 'current_page' => get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1, | |
| 392 | + 'max_page' => $wp_query->max_num_pages, | |
| 393 | + ); | |
| 233 | 394 | |
| 234 | - $handle = 'friends'; | |
| 235 | - $file = 'friends.css'; | |
| 236 | - $version = Friends::VERSION; | |
| 237 | - wp_enqueue_style( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array(), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ) ); | |
| 238 | - } | |
| 395 | + // translators: %s is a user handle. | |
| 396 | + $variables['text_confirm_delete_follower'] = __( 'Do you really want to delete the follower %s?', 'friends' ); | |
| 397 | + $variables['text_new_folder'] = __( 'Folder name:', 'friends' ); | |
| 398 | + $variables['text_loading'] = __( 'Loading...', 'friends' ); | |
| 399 | + $variables['text_discovering'] = __( 'Discovering feeds...', 'friends' ); | |
| 400 | + $variables['text_ready_to_follow'] = __( 'Ready', 'friends' ); | |
| 401 | + $variables['text_follow'] = __( 'Follow', 'friends' ); | |
| 402 | + $variables['text_follow_selected'] = __( 'Follow selected', 'friends' ); | |
| 403 | + $variables['text_review_selected'] = __( 'Review selected', 'friends' ); | |
| 404 | + $variables['text_skip'] = __( 'Skip', 'friends' ); | |
| 405 | + $variables['text_error'] = __( 'An error occurred.', 'friends' ); | |
| 406 | + $variables['text_display_name'] = __( 'Display Name', 'friends' ); | |
| 407 | + $variables['text_username'] = __( 'Username', 'friends' ); | |
| 408 | + $variables['text_post_format'] = __( 'Post Format', 'friends' ); | |
| 409 | + $variables['text_feed_parser'] = __( 'Parser', 'friends' ); | |
| 410 | + $variables['text_feed_metadata'] = __( 'Display feed metadata', 'friends' ); | |
| 411 | + $variables['text_hide_feed_metadata'] = __( 'Hide feed metadata', 'friends' ); | |
| 412 | + $variables['text_preview_feed'] = __( 'Preview feed', 'friends' ); | |
| 413 | + $variables['text_hide_preview'] = __( 'Hide preview', 'friends' ); | |
| 414 | + $variables['text_no_preview_items'] = __( 'No preview items were found.', 'friends' ); | |
| 415 | + $variables['text_add_feed_url'] = __( 'Add feed URL', 'friends' ); | |
| 416 | + $variables['text_feed_url'] = __( 'Feed URL', 'friends' ); | |
| 417 | + $variables['text_custom_feed'] = __( 'Custom feed', 'friends' ); | |
| 418 | + $variables['text_feed_url_required'] = __( 'Please enter a feed URL.', 'friends' ); | |
| 419 | + $variables['text_show_more_feeds'] = __( 'Show more feeds', 'friends' ); | |
| 420 | + $variables['text_show_unsupported_feeds'] = __( 'Show unsupported feeds', 'friends' ); | |
| 421 | + $variables['text_no_supported_feeds'] = __( 'No supported feeds discovered.', 'friends' ); | |
| 422 | + $variables['text_no_feed_selected'] = __( 'Please select at least one feed.', 'friends' ); | |
| 423 | + $variables['text_opml_no_feeds'] = __( 'No feeds were found in the OPML file.', 'friends' ); | |
| 424 | + $variables['text_opml_invalid'] = __( 'Could not parse the OPML file.', 'friends' ); | |
| 425 | + $variables['text_opml_select_all'] = __( 'Select all', 'friends' ); | |
| 426 | + $variables['text_opml_deselect_all'] = __( 'Deselect all', 'friends' ); | |
| 427 | + $variables['text_opml_import_selected'] = __( 'Review selected', 'friends' ); | |
| 428 | + $variables['text_opml_no_selected'] = __( 'Please select at least one feed.', 'friends' ); | |
| 429 | + $variables['text_opml_no_feeds_for_url'] = __( 'No feeds discovered.', 'friends' ); | |
| 430 | + $variables['post_formats'] = array_merge( array( 'autodetect' => __( 'Autodetect Post Format', 'friends' ) ), get_post_format_strings() ); | |
| 431 | + $variables['registered_parsers'] = array_map( 'wp_strip_all_tags', $this->friends->feed->get_registered_parsers() ); | |
| 432 | + wp_localize_script( 'friends', 'friends', $variables ); | |
| 239 | 433 | } |
| 240 | 434 | |
| 241 | 435 | public function dequeue_scripts() { |
| 242 | - if ( is_user_logged_in() && Friends::on_frontend() ) { | |
| 243 | - // Dequeue theme styles so taht they don't interact with the Friends frontend. | |
| 436 | + if ( is_user_logged_in() && Friends::on_frontend() && 'block' !== $this->theme ) { | |
| 437 | + // Dequeue theme styles so that they don't interact with the Friends frontend. | |
| 244 | 438 | $wp_styles = wp_styles(); |
| 439 | + $theme_root_path = wp_parse_url( get_theme_root_uri(), PHP_URL_PATH ); | |
| 245 | 440 | foreach ( $wp_styles->queue as $style ) { |
| 246 | 441 | $src = $wp_styles->registered[ $style ]->src; |
| 247 | - if ( 'global-styles' === $style || false !== strpos( $src, '/themes/' ) ) { | |
| 442 | + $src_path = wp_parse_url( $src, PHP_URL_PATH ); | |
| 443 | + $is_theme_style = $theme_root_path && $src_path | |
| 444 | + && 0 === strpos( $src_path, trailingslashit( $theme_root_path ) ); | |
| 445 | + if ( 'global-styles' === $style || $is_theme_style ) { | |
| 248 | 446 | wp_dequeue_style( $style ); |
| 249 | 447 | } |
| 250 | 448 | } |
| 251 | 449 | } |
| @@ -250,8 +448,49 @@ | ||
| 250 | 448 | } |
| 251 | 449 | } |
| 252 | 450 | } |
| 253 | 451 | |
| 452 | + public function migration_notification() { | |
| 453 | + if ( ! Friends::on_frontend() ) { | |
| 454 | + return; | |
| 455 | + } | |
| 456 | + if ( ! Friends::has_pending_migrations() ) { | |
| 457 | + return; | |
| 458 | + } | |
| 459 | + ?> | |
| 460 | + <div class="friends-migration-notification"> | |
| 461 | + <?php esc_html_e( 'Welcome to Friends 4.0!', 'friends' ); ?> | |
| 462 | + <?php esc_html_e( 'Some data migrations are still pending.', 'friends' ); ?> | |
| 463 | + <a href="<?php echo esc_url( admin_url( 'admin.php?page=friends' ) ); ?>"><?php esc_html_e( "Learn what's new \u{2192}", 'friends' ); ?></a> | |
| 464 | + <span class="friends-migration-notification-meta"> | |
| 465 | + <small class="friends-migration-reappear" hidden><?php esc_html_e( 'Will reappear in 2 days.', 'friends' ); ?></small> | |
| 466 | + <button id="friends-dismiss-migration-notification" data-nonce="<?php echo esc_attr( wp_create_nonce( 'friends-dismiss-migration-notification' ) ); ?>"><?php esc_html_e( 'Dismiss', 'friends' ); ?></button> | |
| 467 | + </span> | |
| 468 | + </div> | |
| 469 | + <script> | |
| 470 | + document.getElementById( 'friends-dismiss-migration-notification' ).addEventListener( 'click', function() { | |
| 471 | + var btn = this; | |
| 472 | + var notification = btn.closest( '.friends-migration-notification' ); | |
| 473 | + btn.hidden = true; | |
| 474 | + notification.querySelector( '.friends-migration-reappear' ).hidden = false; | |
| 475 | + fetch( <?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>, { | |
| 476 | + method: 'POST', | |
| 477 | + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
| 478 | + body: 'action=friends_dismiss_migration_notification&_ajax_nonce=' + encodeURIComponent( btn.dataset.nonce ), | |
| 479 | + } ).then( function() { | |
| 480 | + notification.remove(); | |
| 481 | + } ); | |
| 482 | + } ); | |
| 483 | + </script> | |
| 484 | + <?php | |
| 485 | + } | |
| 486 | + | |
| 487 | + public function ajax_dismiss_migration_notification() { | |
| 488 | + check_ajax_referer( 'friends-dismiss-migration-notification' ); | |
| 489 | + update_option( 'friends_migration_status', time(), false ); | |
| 490 | + wp_send_json_success(); | |
| 491 | + } | |
| 492 | + | |
| 254 | 493 | public function the_post( $post, $query ) { |
| 255 | 494 | Subscription::set_authordata_by_query( $query ); |
| 256 | 495 | } |
| 257 | 496 | |
| @@ -267,14 +506,449 @@ | ||
| 267 | 506 | */ |
| 268 | 507 | public function add_body_class( $classes ) { |
| 269 | 508 | if ( $this->friends->on_frontend() ) { |
| 270 | 509 | $classes[] = 'friends-page'; |
| 510 | + $classes[] = 'off-canvas'; | |
| 511 | + $classes[] = 'off-canvas-sidebar-show'; | |
| 271 | 512 | } |
| 272 | 513 | |
| 273 | 514 | return $classes; |
| 274 | 515 | } |
| 275 | 516 | |
| 517 | + public function register_google_reader_theme( Frontend $friends_frontend ) { | |
| 518 | + $friends_frontend->register_theme( 'Google Reader', 'google-reader' ); | |
| 519 | + add_filter( | |
| 520 | + 'friends_theme_name', | |
| 521 | + function ( $name, $slug ) { | |
| 522 | + return 'google-reader' === $slug ? __( 'Google Reader', 'friends' ) : $name; | |
| 523 | + }, | |
| 524 | + 10, | |
| 525 | + 2 | |
| 526 | + ); | |
| 527 | + } | |
| 528 | + | |
| 529 | + public function register_mastodon_theme( Frontend $friends_frontend ) { | |
| 530 | + $friends_frontend->register_theme( 'Mastodon', 'mastodon' ); | |
| 531 | + add_filter( | |
| 532 | + 'friends_theme_name', | |
| 533 | + function ( $name, $slug ) { | |
| 534 | + return 'mastodon' === $slug ? __( 'Mastodon', 'friends' ) : $name; | |
| 535 | + }, | |
| 536 | + 10, | |
| 537 | + 2 | |
| 538 | + ); | |
| 539 | + } | |
| 540 | + | |
| 541 | + public function mastodon_theme() { | |
| 542 | + $handle = 'friends-mastodon'; | |
| 543 | + $file = 'templates/mastodon/mastodon.css'; | |
| 544 | + $version = $this->get_asset_version( $file ); | |
| 545 | + wp_enqueue_style( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array(), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ) ); | |
| 546 | + | |
| 547 | + $file = 'templates/mastodon/mastodon.js'; | |
| 548 | + wp_enqueue_script( 'friends-mastodon', plugins_url( $file, FRIENDS_PLUGIN_FILE ), array( 'jquery', 'friends' ), $this->get_asset_version( $file ), true ); | |
| 549 | + wp_localize_script( | |
| 550 | + 'friends-mastodon', | |
| 551 | + 'friendsMastodon', | |
| 552 | + array( | |
| 553 | + 'mentionNonce' => wp_create_nonce( 'friends-mention-autocomplete' ), | |
| 554 | + ) | |
| 555 | + ); | |
| 556 | + | |
| 557 | + add_filter( | |
| 558 | + 'friends_template_paths_theme_mastodon', | |
| 559 | + function ( $file_paths ) { | |
| 560 | + $file_paths[0] = FRIENDS_PLUGIN_DIR . 'templates/mastodon/'; | |
| 561 | + return $file_paths; | |
| 562 | + } | |
| 563 | + ); | |
| 564 | + } | |
| 565 | + | |
| 566 | + public function register_twitter_theme( Frontend $friends_frontend ) { | |
| 567 | + $friends_frontend->register_theme( 'Twitter', 'twitter' ); | |
| 568 | + add_filter( | |
| 569 | + 'friends_theme_name', | |
| 570 | + function ( $name, $slug ) { | |
| 571 | + return 'twitter' === $slug ? __( 'Twitter', 'friends' ) : $name; | |
| 572 | + }, | |
| 573 | + 10, | |
| 574 | + 2 | |
| 575 | + ); | |
| 576 | + } | |
| 577 | + | |
| 578 | + public function twitter_theme() { | |
| 579 | + $handle = 'friends-twitter'; | |
| 580 | + $file = 'templates/twitter/twitter.css'; | |
| 581 | + $version = $this->get_asset_version( $file ); | |
| 582 | + wp_enqueue_style( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array(), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ) ); | |
| 583 | + | |
| 584 | + $file = 'templates/twitter/twitter.js'; | |
| 585 | + wp_enqueue_script( 'friends-twitter', plugins_url( $file, FRIENDS_PLUGIN_FILE ), array( 'jquery', 'friends' ), $this->get_asset_version( $file ), true ); | |
| 586 | + wp_localize_script( | |
| 587 | + 'friends-twitter', | |
| 588 | + 'friendsTwitter', | |
| 589 | + array( | |
| 590 | + 'mentionNonce' => wp_create_nonce( 'friends-mention-autocomplete' ), | |
| 591 | + ) | |
| 592 | + ); | |
| 593 | + | |
| 594 | + add_filter( | |
| 595 | + 'friends_template_paths_theme_twitter', | |
| 596 | + function ( $file_paths ) { | |
| 597 | + $file_paths[0] = FRIENDS_PLUGIN_DIR . 'templates/twitter/'; | |
| 598 | + return $file_paths; | |
| 599 | + } | |
| 600 | + ); | |
| 601 | + } | |
| 602 | + | |
| 603 | + public function google_reader_theme() { | |
| 604 | + $handle = 'friends-google-reader'; | |
| 605 | + $file = 'templates/google-reader/google-reader.css'; | |
| 606 | + $version = $this->get_asset_version( $file ); | |
| 607 | + wp_enqueue_style( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array(), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ) ); | |
| 608 | + | |
| 609 | + $file = 'templates/google-reader/google-reader.js'; | |
| 610 | + wp_enqueue_script( 'friends-google-reader', plugins_url( $file, FRIENDS_PLUGIN_FILE ), array( 'jquery' ), $this->get_asset_version( $file ), true ); | |
| 611 | + | |
| 612 | + add_filter( | |
| 613 | + 'friends_template_paths_theme_google-reader', | |
| 614 | + function ( $file_paths ) { | |
| 615 | + // Add Google Reader templates at highest priority (lowest number), fall back to defaults. | |
| 616 | + $file_paths[0] = FRIENDS_PLUGIN_DIR . 'templates/google-reader/'; | |
| 617 | + return $file_paths; | |
| 618 | + } | |
| 619 | + ); | |
| 620 | + } | |
| 621 | + | |
| 276 | 622 | /** |
| 623 | + * Add a theme switcher submenu to the Friends admin bar menu. | |
| 624 | + * | |
| 625 | + * @param \WP_Admin_Bar $wp_admin_bar The admin bar instance. | |
| 626 | + */ | |
| 627 | + public function admin_bar_theme_switcher( $wp_admin_bar ) { | |
| 628 | + if ( ! is_user_logged_in() || ! Friends::on_frontend() ) { | |
| 629 | + return; | |
| 630 | + } | |
| 631 | + | |
| 632 | + $current_theme = get_user_option( 'friends_frontend_theme', get_current_user_id() ); | |
| 633 | + if ( ! $current_theme ) { | |
| 634 | + $current_theme = 'default'; | |
| 635 | + } | |
| 636 | + | |
| 637 | + $themes = self::get_themes(); | |
| 638 | + | |
| 639 | + $wp_admin_bar->add_node( | |
| 640 | + array( | |
| 641 | + 'parent' => 'friends-menu', | |
| 642 | + 'id' => 'friends-theme', | |
| 643 | + 'title' => esc_html__( 'Theme', 'friends' ), | |
| 644 | + 'href' => admin_url( 'admin.php?page=friends-settings' ), | |
| 645 | + ) | |
| 646 | + ); | |
| 647 | + | |
| 648 | + $wp_admin_bar->add_group( | |
| 649 | + array( | |
| 650 | + 'parent' => 'friends-theme', | |
| 651 | + 'id' => 'friends-theme-list', | |
| 652 | + ) | |
| 653 | + ); | |
| 654 | + | |
| 655 | + foreach ( $themes as $slug => $name ) { | |
| 656 | + $wp_admin_bar->add_node( | |
| 657 | + array( | |
| 658 | + 'parent' => 'friends-theme-list', | |
| 659 | + 'id' => 'friends-theme-' . $slug, | |
| 660 | + 'title' => ( $slug === $current_theme ? '✓ ' : '' ) . esc_html( $name ), | |
| 661 | + 'href' => add_query_arg( 'friends_theme', $slug ), | |
| 662 | + ) | |
| 663 | + ); | |
| 664 | + } | |
| 665 | + } | |
| 666 | + | |
| 667 | + public function register_block_theme( Frontend $friends_frontend ) { | |
| 668 | + $friends_frontend->register_theme( 'Block Theme', 'block' ); | |
| 669 | + add_filter( | |
| 670 | + 'friends_theme_name', | |
| 671 | + function ( $name, $slug ) { | |
| 672 | + return 'block' === $slug ? __( 'Block Theme', 'friends' ) : $name; | |
| 673 | + }, | |
| 674 | + 10, | |
| 675 | + 2 | |
| 676 | + ); | |
| 677 | + } | |
| 678 | + | |
| 679 | + public function block_theme() { | |
| 680 | + // No theme swap needed; templates are registered as plugin templates | |
| 681 | + // and injected via template_override. | |
| 682 | + $handle = 'friends-blocks'; | |
| 683 | + $file = 'friends-blocks.css'; | |
| 684 | + $version = $this->get_asset_version( $file ); | |
| 685 | + wp_enqueue_style( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array(), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ) ); | |
| 686 | + } | |
| 687 | + | |
| 688 | + /** | |
| 689 | + * Register Friends block templates as plugin templates. | |
| 690 | + */ | |
| 691 | + public function register_block_templates() { | |
| 692 | + if ( ! wp_is_block_theme() || ! class_exists( 'WP_Block_Templates_Registry' ) ) { | |
| 693 | + return; | |
| 694 | + } | |
| 695 | + | |
| 696 | + $registry = \WP_Block_Templates_Registry::get_instance(); | |
| 697 | + $templates = array( | |
| 698 | + 'friends//friends-index' => array( | |
| 699 | + 'title' => __( 'Friends Feed', 'friends' ), | |
| 700 | + 'content' => 'index', | |
| 701 | + ), | |
| 702 | + 'friends//friends-author-index' => array( | |
| 703 | + 'title' => __( 'Friends Author Feed', 'friends' ), | |
| 704 | + 'content' => 'friends-author-index', | |
| 705 | + ), | |
| 706 | + 'friends//friends-followers' => array( | |
| 707 | + 'title' => __( 'Friends Followers', 'friends' ), | |
| 708 | + 'content' => 'friends-followers', | |
| 709 | + ), | |
| 710 | + 'friends//friends-add-friend' => array( | |
| 711 | + 'title' => __( 'Friends Add Friend', 'friends' ), | |
| 712 | + 'content' => 'friends-add-friend', | |
| 713 | + ), | |
| 714 | + 'friends//friends-subscriptions' => array( | |
| 715 | + 'title' => __( 'Friends Following', 'friends' ), | |
| 716 | + 'content' => 'friends-subscriptions', | |
| 717 | + ), | |
| 718 | + 'friends//friends-single' => array( | |
| 719 | + 'title' => __( 'Friends Single Post', 'friends' ), | |
| 720 | + 'content' => 'single-friend_post_cache', | |
| 721 | + ), | |
| 722 | + ); | |
| 723 | + | |
| 724 | + foreach ( $templates as $name => $args ) { | |
| 725 | + $file = FRIENDS_PLUGIN_DIR . 'themes/friends/templates/' . $args['content'] . '.html'; | |
| 726 | + if ( file_exists( $file ) ) { | |
| 727 | + $registry->register( | |
| 728 | + $name, | |
| 729 | + array( | |
| 730 | + 'title' => $args['title'], | |
| 731 | + 'content' => file_get_contents( $file ), // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents | |
| 732 | + ) | |
| 733 | + ); | |
| 734 | + } | |
| 735 | + } | |
| 736 | + } | |
| 737 | + | |
| 738 | + /** | |
| 739 | + * Render template parts for the friends plugin. | |
| 740 | + * | |
| 741 | + * @param string|null $pre_render The pre-rendered content. | |
| 742 | + * @param array $parsed_block The parsed block. | |
| 743 | + * @return string|null The rendered content or null. | |
| 744 | + */ | |
| 745 | + public function render_friends_template_part( $pre_render, $parsed_block ) { | |
| 746 | + if ( 'core/template-part' !== $parsed_block['blockName'] ) { | |
| 747 | + return $pre_render; | |
| 748 | + } | |
| 749 | + | |
| 750 | + $attrs = $parsed_block['attrs']; | |
| 751 | + if ( ! isset( $attrs['theme'] ) || 'friends' !== $attrs['theme'] || ! isset( $attrs['slug'] ) ) { | |
| 752 | + return $pre_render; | |
| 753 | + } | |
| 754 | + | |
| 755 | + // Check for user-customized version in the database first. | |
| 756 | + $custom_query = new \WP_Query( | |
| 757 | + array( | |
| 758 | + 'post_type' => 'wp_template_part', | |
| 759 | + 'post_status' => 'publish', | |
| 760 | + 'post_name__in' => array( $attrs['slug'] ), | |
| 761 | + 'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query | |
| 762 | + array( | |
| 763 | + 'taxonomy' => 'wp_theme', | |
| 764 | + 'field' => 'name', | |
| 765 | + 'terms' => 'friends', | |
| 766 | + ), | |
| 767 | + ), | |
| 768 | + 'posts_per_page' => 1, | |
| 769 | + 'no_found_rows' => true, | |
| 770 | + ) | |
| 771 | + ); | |
| 772 | + | |
| 773 | + if ( $custom_query->have_posts() ) { | |
| 774 | + $content = $custom_query->posts[0]->post_content; | |
| 775 | + } else { | |
| 776 | + $file = FRIENDS_PLUGIN_DIR . 'themes/friends/parts/' . $attrs['slug'] . '.html'; | |
| 777 | + if ( ! file_exists( $file ) ) { | |
| 778 | + return $pre_render; | |
| 779 | + } | |
| 780 | + $content = file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents | |
| 781 | + } | |
| 782 | + | |
| 783 | + $tag = isset( $attrs['tagName'] ) ? $attrs['tagName'] : 'div'; | |
| 784 | + | |
| 785 | + return '<' . $tag . ' class="wp-block-template-part">' . do_blocks( $content ) . '</' . $tag . '>'; | |
| 786 | + } | |
| 787 | + | |
| 788 | + /** | |
| 789 | + * Build a WP_Block_Template for a friends template part. | |
| 790 | + * | |
| 791 | + * @param string $slug The template part slug. | |
| 792 | + * @param string $file The file path. | |
| 793 | + * @return WP_Block_Template | |
| 794 | + */ | |
| 795 | + private function build_friends_template_part( $slug, $file ) { | |
| 796 | + $template = new \WP_Block_Template(); | |
| 797 | + $template->id = 'friends//' . $slug; | |
| 798 | + $template->theme = 'friends'; | |
| 799 | + $template->plugin = 'friends'; | |
| 800 | + $template->slug = $slug; | |
| 801 | + $template->source = 'plugin'; | |
| 802 | + $template->origin = 'plugin'; | |
| 803 | + $template->type = 'wp_template_part'; | |
| 804 | + $template->title = ucwords( str_replace( '-', ' ', $slug ) ); | |
| 805 | + $template->status = 'publish'; | |
| 806 | + $template->has_theme_file = true; | |
| 807 | + $template->is_custom = false; | |
| 808 | + $template->content = file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents | |
| 809 | + $template->area = WP_TEMPLATE_PART_AREA_UNCATEGORIZED; | |
| 810 | + | |
| 811 | + return $template; | |
| 812 | + } | |
| 813 | + | |
| 814 | + /** | |
| 815 | + * Resolve a friends template part by ID. | |
| 816 | + * | |
| 817 | + * @param WP_Block_Template|null $block_template The found block template. | |
| 818 | + * @param string $id Template unique identifier. | |
| 819 | + * @param string $template_type Template type. | |
| 820 | + * @return WP_Block_Template|null | |
| 821 | + */ | |
| 822 | + public function get_friends_block_file_template( $block_template, $id, $template_type ) { | |
| 823 | + if ( 'wp_template_part' !== $template_type ) { | |
| 824 | + return $block_template; | |
| 825 | + } | |
| 826 | + | |
| 827 | + $parts = explode( '//', $id, 2 ); | |
| 828 | + if ( count( $parts ) < 2 || 'friends' !== $parts[0] ) { | |
| 829 | + return $block_template; | |
| 830 | + } | |
| 831 | + | |
| 832 | + $slug = $parts[1]; | |
| 833 | + $file = FRIENDS_PLUGIN_DIR . 'themes/friends/parts/' . $slug . '.html'; | |
| 834 | + if ( ! file_exists( $file ) ) { | |
| 835 | + return $block_template; | |
| 836 | + } | |
| 837 | + | |
| 838 | + return $this->build_friends_template_part( $slug, $file ); | |
| 839 | + } | |
| 840 | + | |
| 841 | + /** | |
| 842 | + * Add friends template parts to block template queries. | |
| 843 | + * | |
| 844 | + * @param WP_Block_Template[] $query_result Array of found block templates. | |
| 845 | + * @param array $query Arguments to retrieve templates. | |
| 846 | + * @param string $template_type Template type. | |
| 847 | + * @return WP_Block_Template[] | |
| 848 | + */ | |
| 849 | + public function add_friends_template_parts( $query_result, $query, $template_type ) { | |
| 850 | + if ( 'wp_template_part' !== $template_type ) { | |
| 851 | + return $query_result; | |
| 852 | + } | |
| 853 | + | |
| 854 | + $parts_dir = FRIENDS_PLUGIN_DIR . 'themes/friends/parts/'; | |
| 855 | + if ( ! is_dir( $parts_dir ) ) { | |
| 856 | + return $query_result; | |
| 857 | + } | |
| 858 | + | |
| 859 | + $slugs_to_include = isset( $query['slug__in'] ) ? $query['slug__in'] : array(); | |
| 860 | + | |
| 861 | + foreach ( glob( $parts_dir . '*.html' ) as $file ) { | |
| 862 | + $slug = basename( $file, '.html' ); | |
| 863 | + | |
| 864 | + if ( $slugs_to_include && ! in_array( $slug, $slugs_to_include, true ) ) { | |
| 865 | + continue; | |
| 866 | + } | |
| 867 | + | |
| 868 | + // Don't add if already in results. | |
| 869 | + $exists = false; | |
| 870 | + foreach ( $query_result as $existing ) { | |
| 871 | + if ( $existing->slug === $slug && 'friends' === $existing->theme ) { | |
| 872 | + $exists = true; | |
| 873 | + break; | |
| 874 | + } | |
| 875 | + } | |
| 876 | + if ( ! $exists ) { | |
| 877 | + $query_result[] = $this->build_friends_template_part( $slug, $file ); | |
| 878 | + } | |
| 879 | + } | |
| 880 | + | |
| 881 | + return $query_result; | |
| 882 | + } | |
| 883 | + | |
| 884 | + public function block_type_metadata_settings( $settings ) { | |
| 885 | + if ( ! Friends::on_frontend() || ! isset( $settings['name'] ) ) { | |
| 886 | + return $settings; | |
| 887 | + } | |
| 888 | + if ( 'core/post-author-name' === $settings['name'] ) { | |
| 889 | + $settings['render_callback'] = function ( $attributes, $content, $block ) { | |
| 890 | + if ( isset( $block->context['postId'] ) ) { | |
| 891 | + $author = User::get_post_author( get_post( $block->context['postId'] ) ); | |
| 892 | + } else { | |
| 893 | + return ''; | |
| 894 | + } | |
| 895 | + if ( empty( $author ) || is_wp_error( $author ) ) { | |
| 896 | + return ''; | |
| 897 | + } | |
| 898 | + | |
| 899 | + $author_name = $author->display_name; | |
| 900 | + $author_name_html = apply_filters( 'friends_author_display_name_html', esc_html( $author_name ), $author_name, $author ); | |
| 901 | + $override_author_name = apply_filters( 'friends_override_author_name', '', $author_name, $block->context['postId'] ); | |
| 902 | + if ( isset( $attributes['isLink'] ) && $attributes['isLink'] ) { | |
| 903 | + $author_name_html = sprintf( '<a href="%1$s" target="%2$s" class="wp-block-post-author-name__link">%3$s</a>', esc_url( $author->get_local_friends_page_url() ), esc_attr( $attributes['linkTarget'] ), $author_name_html ); | |
| 904 | + } | |
| 905 | + | |
| 906 | + if ( $override_author_name && trim( str_replace( $override_author_name, '', $author_name ) ) === $author_name ) { | |
| 907 | + $author_name_html .= ' – ' . esc_html( $override_author_name ); | |
| 908 | + } | |
| 909 | + | |
| 910 | + $classes = array(); | |
| 911 | + if ( isset( $attributes['textAlign'] ) ) { | |
| 912 | + $classes[] = 'has-text-align-' . $attributes['textAlign']; | |
| 913 | + } | |
| 914 | + if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) { | |
| 915 | + $classes[] = 'has-link-color'; | |
| 916 | + } | |
| 917 | + $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => implode( ' ', $classes ) ) ); | |
| 918 | + | |
| 919 | + return sprintf( | |
| 920 | + '<div %1$s>%2$s</div>', | |
| 921 | + $wrapper_attributes, | |
| 922 | + wp_kses( | |
| 923 | + $author_name_html, | |
| 924 | + array_merge( | |
| 925 | + wp_kses_allowed_html( 'post' ), | |
| 926 | + array( | |
| 927 | + 'a' => array( | |
| 928 | + 'class' => true, | |
| 929 | + 'href' => true, | |
| 930 | + 'target' => true, | |
| 931 | + ), | |
| 932 | + ) | |
| 933 | + ) | |
| 934 | + ) | |
| 935 | + ); | |
| 936 | + }; | |
| 937 | + } | |
| 938 | + return $settings; | |
| 939 | + } | |
| 940 | + public function tag_row_actions( $actions, $tag ) { | |
| 941 | + $actions['view-friends'] = sprintf( | |
| 942 | + '<a href="%s">%s</a>', | |
| 943 | + esc_url( home_url( '/friends/tag/' . $tag->name ) ), | |
| 944 | + __( 'View on your Friends page', 'friends' ) | |
| 945 | + ); | |
| 946 | + return $actions; | |
| 947 | + } | |
| 948 | + | |
| 949 | + | |
| 950 | + /** | |
| 277 | 951 | * Gets the minimal query variables. |
| 278 | 952 | * |
| 279 | 953 | * @param array $query_vars The query variables. |
| 280 | 954 | * |
| @@ -280,22 +954,35 @@ | ||
| 280 | 954 | * |
| 281 | 955 | * @return array The minimal query variables. |
| 282 | 956 | */ |
| 283 | 957 | private function get_minimal_query_vars( $query_vars ) { |
| 284 | - return array_filter( array_intersect_key( $query_vars, array_flip( array( 'p', 'page_id', 'pagename', 'author', 'author__not_in', 'post_type', 'post_status', 'posts_per_page', 'order', 'tax_query' ) ) ) ); | |
| 958 | + return array_filter( array_intersect_key( $query_vars, array_flip( array( 'p', 'page_id', 'pagename', 'author', 'author__not_in', 'post_type', 'post_status', 'posts_per_page', 'order', 'tax_query', 's' ) ) ) ); | |
| 285 | 959 | } |
| 286 | 960 | |
| 287 | - | |
| 288 | 961 | public function wp_ajax_reblog() { |
| 289 | - if ( ! current_user_can( Friends::REQUIRED_ROLE ) ) { | |
| 962 | + if ( ! current_user_can( Friends::REQUIRED_ROLE ) || ! isset( $_POST['post_id'] ) ) { | |
| 290 | 963 | wp_send_json_error( 'error' ); |
| 291 | 964 | } |
| 292 | 965 | |
| 293 | - $post = get_post( $_POST['post_id'] ); | |
| 966 | + check_ajax_referer( 'friends-reblog' ); | |
| 967 | + | |
| 968 | + $post = get_post( intval( $_POST['post_id'] ) ); | |
| 294 | 969 | if ( ! $post || ! Friends::check_url( $post->guid ) ) { |
| 295 | 970 | wp_send_json_error( 'unknown-post', array( 'guid' => $post->guid ) ); |
| 296 | 971 | } |
| 972 | + $reblog_post_id = get_post_meta( get_the_ID(), 'reblogged', true ); | |
| 973 | + if ( $reblog_post_id ) { | |
| 974 | + wp_send_json_success( | |
| 975 | + array( | |
| 976 | + 'post_id' => $reblog_post_id, | |
| 977 | + 'redirect' => get_edit_post_link( $reblog_post_id, 'js' ), | |
| 978 | + ) | |
| 979 | + ); | |
| 297 | 980 | |
| 981 | + wp_send_json_error( 'already-reblogged', array( 'guid' => $post->guid ) ); | |
| 982 | + return; | |
| 983 | + } | |
| 984 | + | |
| 298 | 985 | /** |
| 299 | 986 | * Reblogs a post |
| 300 | 987 | * |
| 301 | 988 | * @param int|null $reblog_post_id The post ID of the reblogged post. Default null. |
| @@ -300,9 +987,9 @@ | ||
| 300 | 987 | * |
| 301 | 988 | * @param int|null $reblog_post_id The post ID of the reblogged post. Default null. |
| 302 | 989 | * @param WP_Post $post The post object. |
| 303 | 990 | */ |
| 304 | - $reblog_post_id = apply_filters( 'friends_reblog', null, $post ); | |
| 991 | + $reblog_post_id = apply_filters( 'friends_reblog', null, $post, 'draft' ); | |
| 305 | 992 | if ( ! $reblog_post_id || is_wp_error( $reblog_post_id ) ) { |
| 306 | 993 | wp_send_json_error( 'error' ); |
| 307 | 994 | } |
| 308 | 995 | |
| @@ -307,26 +994,23 @@ | ||
| 307 | 994 | } |
| 308 | 995 | |
| 309 | 996 | wp_send_json_success( |
| 310 | 997 | array( |
| 311 | - 'post_id' => $reblog_post_id, | |
| 998 | + 'post_id' => $reblog_post_id, | |
| 999 | + 'redirect' => get_edit_post_link( $reblog_post_id, 'js' ), | |
| 312 | 1000 | ) |
| 313 | 1001 | ); |
| 314 | 1002 | } |
| 315 | 1003 | |
| 316 | 1004 | public function reblog_button() { |
| 317 | - $button_label = apply_filters( 'friends_reblog_button_label', _x( 'Reblog', 'button', 'friends' ) ); | |
| 318 | - | |
| 319 | 1005 | Friends::template_loader()->get_template_part( |
| 320 | 1006 | 'frontend/parts/reblog-button', |
| 321 | 1007 | null, |
| 322 | - array( | |
| 323 | - 'button-label' => $button_label, | |
| 324 | - ) | |
| 1008 | + array() | |
| 325 | 1009 | ); |
| 326 | 1010 | } |
| 327 | 1011 | |
| 328 | - public static function reblog( $ret, $post ) { | |
| 1012 | + public static function reblog( $ret, $post, $post_status = 'publish' ) { | |
| 329 | 1013 | if ( ! $post ) { |
| 330 | 1014 | return $ret; |
| 331 | 1015 | } |
| 332 | 1016 | $post = get_post( $post ); |
| @@ -339,9 +1023,9 @@ | ||
| 339 | 1023 | $friend = User::get_post_author( $post ); |
| 340 | 1024 | $author = $friend->display_name; |
| 341 | 1025 | } |
| 342 | 1026 | |
| 343 | - $reblog = '<!-- wp:paragraph -->' . PHP_EOL . '<p>'; | |
| 1027 | + $reblog = '<!-- wp:paragraph {"className":"friends-reblog"} -->' . PHP_EOL . '<p class="friends-reblog">'; | |
| 344 | 1028 | $reblog .= sprintf( |
| 345 | 1029 | // translators: %s is a link. |
| 346 | 1030 | __( 'Reblog via %s', 'friends' ), |
| 347 | 1031 | '<a href="' . esc_url( $post->guid ) . '">' . esc_html( $author ) . '</a>' |
| @@ -346,23 +1030,54 @@ | ||
| 346 | 1030 | __( 'Reblog via %s', 'friends' ), |
| 347 | 1031 | '<a href="' . esc_url( $post->guid ) . '">' . esc_html( $author ) . '</a>' |
| 348 | 1032 | ); |
| 349 | 1033 | |
| 1034 | + $reblog_title = sprintf( | |
| 1035 | + // translators: %1$s is the author, %2$s is the post title. | |
| 1036 | + __( 'Reblog of %1$s: %2$s', 'friends' ), | |
| 1037 | + $author, | |
| 1038 | + $post->post_title | |
| 1039 | + ); | |
| 1040 | + | |
| 350 | 1041 | $reblog .= PHP_EOL . '</p>' . PHP_EOL . '<!-- /wp:paragraph -->' . PHP_EOL; |
| 1042 | + | |
| 1043 | + // Wrap the post content in a quote block. | |
| 1044 | + $reblog .= '<!-- wp:quote {"className":"friends-reblog"} -->' . PHP_EOL . '<blockquote class="wp-block-quote friends-reblog">'; | |
| 1045 | + $reblog .= $post->post_content; | |
| 1046 | + $reblog .= '</blockquote>' . PHP_EOL . '<!-- /wp:quote -->'; | |
| 1047 | + | |
| 351 | 1048 | $new_post = array( |
| 352 | - 'post_title' => $post->title, | |
| 1049 | + 'post_title' => $reblog_title, | |
| 353 | 1050 | 'post_author' => get_current_user_id(), |
| 354 | - 'post_status' => 'publish', | |
| 1051 | + 'post_status' => $post_status, | |
| 355 | 1052 | 'post_type' => 'post', |
| 356 | - 'post_content' => $reblog . $post->post_content, | |
| 1053 | + 'post_format' => get_post_format( $post ), | |
| 1054 | + 'post_content' => $reblog, | |
| 357 | 1055 | ); |
| 1056 | + /** | |
| 1057 | + * Allows changing a reblog post before it gets posted. | |
| 1058 | + * | |
| 1059 | + * @param array $new_post A post array to be handed to wp_insert_post. | |
| 1060 | + * | |
| 1061 | + * Additionally, the array contains the post_format which can also be changed. | |
| 1062 | + * | |
| 1063 | + * Example: | |
| 1064 | + * ```php | |
| 1065 | + * add_filter( 'friends_reblog_pre_insert_post', function( $new_post ) { | |
| 1066 | + * $new_post['post_type'] = 'custom_post_type'; | |
| 1067 | + * $new_post['post_format'] = 'aside'; // always set the post_format to aside, regardless of the original post format. | |
| 1068 | + * return $new_post; | |
| 1069 | + * } ); | |
| 1070 | + * ``` | |
| 1071 | + */ | |
| 1072 | + $new_post = apply_filters( 'friends_reblog_pre_insert_post', $new_post ); | |
| 358 | 1073 | $new_post_id = wp_insert_post( $new_post ); |
| 359 | 1074 | |
| 360 | - set_post_format( $new_post_id, get_post_format( $post ) ); | |
| 1075 | + set_post_format( $new_post_id, $new_post['post_format'] ); | |
| 361 | 1076 | update_post_meta( $new_post_id, 'reblog', $post->guid ); |
| 362 | 1077 | update_post_meta( $new_post_id, 'reblog_of', $post->ID ); |
| 363 | 1078 | update_post_meta( $post->ID, 'reblogged', $new_post_id ); |
| 364 | - update_post_meta( $post->ID, 'reblogged_by', get_current_user_id() ); | |
| 1079 | + update_post_meta( $post->ID, 'reblogged_by', $new_post['post_author'] ); | |
| 365 | 1080 | |
| 366 | 1081 | return $new_post_id; |
| 367 | 1082 | } |
| 368 | 1083 | |
| @@ -385,20 +1100,74 @@ | ||
| 385 | 1100 | return $reblogged; |
| 386 | 1101 | } |
| 387 | 1102 | |
| 388 | 1103 | /** |
| 1104 | + * The Ajax function to autocomplete @mentions in the compose box. | |
| 1105 | + */ | |
| 1106 | + public function ajax_mention_autocomplete() { | |
| 1107 | + check_ajax_referer( 'friends-mention-autocomplete' ); | |
| 1108 | + | |
| 1109 | + if ( ! current_user_can( Friends::REQUIRED_ROLE ) ) { | |
| 1110 | + wp_send_json_error(); | |
| 1111 | + exit; | |
| 1112 | + } | |
| 1113 | + | |
| 1114 | + if ( ! isset( $_POST['q'] ) ) { | |
| 1115 | + wp_send_json_success( array() ); | |
| 1116 | + exit; | |
| 1117 | + } | |
| 1118 | + | |
| 1119 | + $q = sanitize_text_field( wp_unslash( $_POST['q'] ) ); | |
| 1120 | + $users = User_Query::search( '*' . $q . '*' ); | |
| 1121 | + $results = array(); | |
| 1122 | + | |
| 1123 | + foreach ( $users->get_results() as $friend ) { | |
| 1124 | + $handle = null; | |
| 1125 | + | |
| 1126 | + foreach ( $friend->get_feeds() as $feed ) { | |
| 1127 | + $ap_actor_id = $feed->get_ap_actor_id(); | |
| 1128 | + if ( $ap_actor_id && class_exists( '\Activitypub\Collection\Remote_Actors' ) ) { | |
| 1129 | + $acct = \Activitypub\Collection\Remote_Actors::get_acct( $ap_actor_id ); | |
| 1130 | + if ( $acct ) { | |
| 1131 | + $handle = $acct; | |
| 1132 | + break; | |
| 1133 | + } | |
| 1134 | + } | |
| 1135 | + } | |
| 1136 | + | |
| 1137 | + if ( ! $handle ) { | |
| 1138 | + $handle = $friend->user_login; | |
| 1139 | + if ( $friend->user_url ) { | |
| 1140 | + $host = wp_parse_url( $friend->user_url, PHP_URL_HOST ); | |
| 1141 | + if ( $host ) { | |
| 1142 | + $handle = $friend->user_login . '@' . $host; | |
| 1143 | + } | |
| 1144 | + } | |
| 1145 | + } | |
| 1146 | + | |
| 1147 | + $results[] = array( | |
| 1148 | + 'handle' => $handle, | |
| 1149 | + 'display_name' => $friend->display_name, | |
| 1150 | + 'avatar' => $friend->get_avatar_url(), | |
| 1151 | + ); | |
| 1152 | + } | |
| 1153 | + | |
| 1154 | + wp_send_json_success( $results ); | |
| 1155 | + } | |
| 1156 | + | |
| 1157 | + /** | |
| 389 | 1158 | * The Ajax function to be called upon posting from /friends |
| 390 | 1159 | */ |
| 391 | 1160 | public function ajax_frontend_publish_post() { |
| 392 | - if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'friends_publish' ) ) { | |
| 1161 | + if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'friends_publish' ) ) { | |
| 393 | 1162 | return false; |
| 394 | 1163 | } |
| 395 | 1164 | $p = array( |
| 396 | 1165 | 'post_type' => 'post', |
| 397 | - 'post_title' => isset( $_POST['title'] ) ? $_POST['title'] : '', | |
| 398 | - 'post_content' => isset( $_POST['content'] ) ? $_POST['content'] : '', | |
| 399 | - 'post_status' => isset( $_POST['status'] ) ? $_POST['status'] : '', | |
| 400 | - 'post_format' => isset( $_POST['format'] ) ? $_POST['format'] : '', | |
| 1166 | + 'post_title' => isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : '', | |
| 1167 | + 'post_content' => isset( $_POST['content'] ) ? sanitize_textarea_field( wp_unslash( $_POST['content'] ) ) : '', | |
| 1168 | + 'post_status' => isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : '', | |
| 1169 | + 'post_format' => isset( $_POST['format'] ) ? sanitize_text_field( wp_unslash( $_POST['format'] ) ) : '', | |
| 401 | 1170 | ); |
| 402 | 1171 | |
| 403 | 1172 | if ( empty( $p['post_status'] ) ) { |
| 404 | 1173 | $p['post_status'] = 'publish'; |
| @@ -406,9 +1175,9 @@ | ||
| 406 | 1175 | $result = 'empty'; |
| 407 | 1176 | |
| 408 | 1177 | if ( ! empty( $_POST['in_reply_to'] ) ) { |
| 409 | 1178 | $p['meta_input'] = array( |
| 410 | - 'activitypub_in_reply_to' => $_POST['in_reply_to'], | |
| 1179 | + 'activitypub_in_reply_to' => sanitize_text_field( wp_unslash( $_POST['in_reply_to'] ) ), | |
| 411 | 1180 | ); |
| 412 | 1181 | } |
| 413 | 1182 | |
| 414 | 1183 | if ( ! empty( $p['post_content'] ) || ! empty( $p['post_title'] ) ) { |
| @@ -417,15 +1186,23 @@ | ||
| 417 | 1186 | set_post_format( $post_id, $p['post_format'] ); |
| 418 | 1187 | } |
| 419 | 1188 | $result = is_wp_error( $post_id ) ? 'error' : 'success'; |
| 420 | 1189 | } |
| 1190 | + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash | |
| 1191 | + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 421 | 1192 | if ( ! empty( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) === 'xmlhttprequest' ) { |
| 422 | 1193 | echo esc_html( $result ); |
| 423 | 1194 | exit; |
| 424 | - } else { | |
| 1195 | + } | |
| 1196 | + | |
| 1197 | + if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) { | |
| 425 | 1198 | wp_safe_redirect( remove_query_arg( 'in_reply_to', add_query_arg( 'result', $result, $_SERVER['HTTP_REFERER'] ) ) ); |
| 426 | 1199 | exit; |
| 427 | 1200 | } |
| 1201 | + wp_safe_redirect( home_url( '/friends/' ) ); | |
| 1202 | + exit; | |
| 1203 | + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash | |
| 1204 | + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 428 | 1205 | } |
| 429 | 1206 | |
| 430 | 1207 | /** |
| 431 | 1208 | * The Ajax function to change the post format from the Frontend. |
| @@ -430,10 +1207,10 @@ | ||
| 430 | 1207 | /** |
| 431 | 1208 | * The Ajax function to change the post format from the Frontend. |
| 432 | 1209 | */ |
| 433 | 1210 | public function ajax_change_post_format() { |
| 434 | - $post_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; | |
| 435 | - $post_format = isset( $_POST['format'] ) ? $_POST['format'] : 'standard'; | |
| 1211 | + $post_id = isset( $_POST['id'] ) ? intval( $_POST['id'] ) : 0; | |
| 1212 | + $post_format = isset( $_POST['format'] ) ? sanitize_text_field( wp_unslash( $_POST['format'] ) ) : 'standard'; | |
| 436 | 1213 | |
| 437 | 1214 | check_ajax_referer( "friends-change-post-format_$post_id" ); |
| 438 | 1215 | |
| 439 | 1216 | if ( ! current_user_can( Friends::REQUIRED_ROLE, $post_id ) ) { |
| @@ -461,9 +1238,9 @@ | ||
| 461 | 1238 | * @param string $original_text The original text. |
| 462 | 1239 | * |
| 463 | 1240 | * @return float The read time in seconds. |
| 464 | 1241 | */ |
| 465 | - private static function calculate_read_time( $original_text ) { | |
| 1242 | + public static function calculate_read_time( $original_text ) { | |
| 466 | 1243 | // from wp_trim_words(). |
| 467 | 1244 | $text = wp_strip_all_tags( $original_text ); |
| 468 | 1245 | |
| 469 | 1246 | /* |
| @@ -493,22 +1270,51 @@ | ||
| 493 | 1270 | |
| 494 | 1271 | return count( $words_array ) / $words_per_minute * 60 + $additional_time; |
| 495 | 1272 | } |
| 496 | 1273 | |
| 1274 | + public function register_theme( $name, $slug ) { | |
| 1275 | + self::$themes[ $slug ] = $name; | |
| 1276 | + } | |
| 1277 | + | |
| 1278 | + public static function get_themes() { | |
| 1279 | + $themes = array_merge( | |
| 1280 | + array( | |
| 1281 | + 'default' => __( 'Friends Default Theme', 'friends' ), | |
| 1282 | + ), | |
| 1283 | + self::$themes | |
| 1284 | + ); | |
| 1285 | + | |
| 1286 | + foreach ( $themes as $slug => $name ) { | |
| 1287 | + $themes[ $slug ] = apply_filters( 'friends_theme_name', $name, $slug ); | |
| 1288 | + } | |
| 1289 | + | |
| 1290 | + return $themes; | |
| 1291 | + } | |
| 497 | 1292 | /** |
| 498 | 1293 | * Handles the post loop on the Friends page. |
| 499 | 1294 | */ |
| 500 | 1295 | public static function have_posts() { |
| 501 | 1296 | $friends = Friends::get_instance(); |
| 1297 | + $known_author = $friends->frontend->author; | |
| 1298 | + $known_avatar = null; | |
| 1299 | + if ( $known_author instanceof User ) { | |
| 1300 | + $known_avatar = $known_author->get_avatar_url(); | |
| 1301 | + } | |
| 502 | 1302 | while ( have_posts() ) { |
| 503 | 1303 | global $post; |
| 504 | 1304 | the_post(); |
| 505 | 1305 | $args = array( |
| 506 | 1306 | 'friends' => $friends, |
| 507 | - 'avatar' => get_post_meta( get_the_ID(), 'gravatar', true ), | |
| 508 | 1307 | ); |
| 1308 | + $args['item_friend_user'] = User::get_post_author( $post ); | |
| 509 | 1309 | |
| 510 | - $args['friend_user'] = User::get_post_author( $post ); | |
| 1310 | + if ( $known_author ) { | |
| 1311 | + $args['friend_user'] = $known_author; | |
| 1312 | + $args['avatar'] = $known_avatar; | |
| 1313 | + } else { | |
| 1314 | + $args['friend_user'] = $args['item_friend_user']; | |
| 1315 | + $args['avatar'] = $args['friend_user']->get_avatar_url(); | |
| 1316 | + } | |
| 511 | 1317 | |
| 512 | 1318 | $read_time = self::calculate_read_time( get_the_content() ); |
| 513 | 1319 | if ( $read_time >= 60 ) { |
| 514 | 1320 | $mins = ceil( $read_time / MINUTE_IN_SECONDS ); |
| @@ -530,17 +1336,28 @@ | ||
| 530 | 1336 | /** |
| 531 | 1337 | * The Ajax function to load more posts for infinite scrolling. |
| 532 | 1338 | */ |
| 533 | 1339 | public function ajax_load_next_page() { |
| 1340 | + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 1341 | + // phpcs:disable WordPress.Security.NonceVerification.Missing | |
| 1342 | + if ( ! isset( $_POST['query_vars'] ) || ! isset( $_POST['page'] ) || ! isset( $_POST['qv_sign'] ) ) { | |
| 1343 | + wp_send_json_error(); | |
| 1344 | + exit; | |
| 1345 | + } | |
| 534 | 1346 | $query_vars = wp_unslash( $_POST['query_vars'] ); |
| 1347 | + // We do have a nonce verification here. | |
| 535 | 1348 | if ( sha1( wp_salt( 'nonce' ) . $query_vars ) !== $_POST['qv_sign'] ) { |
| 536 | 1349 | wp_send_json_error(); |
| 537 | 1350 | exit; |
| 538 | 1351 | } |
| 539 | - $query_vars = unserialize( $query_vars ); | |
| 1352 | + | |
| 1353 | + $query_vars = json_decode( $query_vars, true ); | |
| 540 | 1354 | $query_vars['paged'] = intval( $_POST['page'] ) + 1; |
| 1355 | + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 1356 | + // phpcs:enable WordPress.Security.NonceVerification.Missing | |
| 541 | 1357 | |
| 542 | - query_posts( $query_vars ); | |
| 1358 | + // We actually want to render the query loop, so we use query_posts. | |
| 1359 | + query_posts( $query_vars ); // phpcs:ignore WordPress.WP.DiscouragedFunctions.query_posts_query_posts | |
| 543 | 1360 | ob_start(); |
| 544 | 1361 | if ( have_posts() ) { |
| 545 | 1362 | self::have_posts(); |
| 546 | 1363 | } else { |
| @@ -553,78 +1370,87 @@ | ||
| 553 | 1370 | wp_send_json_success( $posts ); |
| 554 | 1371 | } |
| 555 | 1372 | |
| 556 | 1373 | /** |
| 557 | - * Get metadata for in_reply_to_preview. | |
| 558 | - * | |
| 559 | - * @param string $url The url. | |
| 560 | - * | |
| 561 | - * @return array|WP_Error The in reply to metadata. | |
| 1374 | + * The Ajax function to autocomplete search. | |
| 562 | 1375 | */ |
| 563 | - public function get_in_reply_to_metadata( $url ) { | |
| 564 | - $meta = apply_filters( 'friends_get_activitypub_metadata', array(), $url ); | |
| 565 | - if ( is_wp_error( $meta ) ) { | |
| 566 | - return $meta; | |
| 1376 | + public function ajax_autocomplete() { | |
| 1377 | + check_ajax_referer( 'friends-autocomplete' ); | |
| 1378 | + if ( ! isset( $_POST['q'] ) ) { | |
| 1379 | + wp_send_json_error(); | |
| 1380 | + exit; | |
| 567 | 1381 | } |
| 568 | 1382 | |
| 569 | - if ( ! $meta || ! isset( $meta['attributedTo'] ) ) { | |
| 570 | - return new \WP_Error( 'no-activitypub', 'No ActivityPub metadata found.' ); | |
| 571 | - } | |
| 1383 | + $q = sanitize_text_field( wp_unslash( $_POST['q'] ) ); | |
| 1384 | + $results = array(); | |
| 572 | 1385 | |
| 573 | - $html = 'URL: ' . make_clickable( $meta['id'] ); | |
| 574 | - $html .= '<blockquote>' . force_balance_tags( wp_kses_post( $meta['content'] ) ) . '</blockquote>'; | |
| 1386 | + /** | |
| 1387 | + $result = '<a href="' . esc_url( add_query_arg( 'name', $q, admin_url( 'admin.php?page=add-friend' ) ) ) . '" class="has-icon-left">'; | |
| 1388 | + $result .= '<span class="ab-icon dashicons dashicons-businessperson"><span class="ab-icon dashicons dashicons-plus"></span></span>'; | |
| 1389 | + $result .= 'Add friend'; | |
| 1390 | + $result .= ' <small>'; | |
| 1391 | + $result .= esc_html( $q ); | |
| 1392 | + $result .= '</small></a>'; | |
| 1393 | + $results[] = $result; | |
| 1394 | + */ | |
| 1395 | + $results = apply_filters( 'friends_search_autocomplete', $results, $q ); | |
| 575 | 1396 | |
| 576 | - $webfinger = apply_filters( 'friends_get_activitypub_metadata', array(), $meta['attributedTo'] ); | |
| 577 | - $mention = ''; | |
| 578 | - if ( $webfinger && ! is_wp_error( $webfinger ) ) { | |
| 579 | - $mention = '@' . $webfinger['preferredUsername'] . '@' . parse_url( $url, PHP_URL_HOST ); | |
| 580 | - } | |
| 1397 | + $result = '<a href="' . esc_url( add_query_arg( 's', $q, home_url( '/friends/' ) ) ) . '" class="has-icon-left">'; | |
| 1398 | + $result .= '<span class="ab-icon dashicons dashicons-search"></span>'; | |
| 1399 | + $result .= 'Search for '; | |
| 1400 | + $result .= ' <small>'; | |
| 1401 | + $result .= esc_html( $q ); | |
| 1402 | + $result .= '</small></a>'; | |
| 1403 | + $results[] = $result; | |
| 581 | 1404 | |
| 582 | - return array( | |
| 583 | - 'url' => $url, | |
| 584 | - 'html' => $html, | |
| 585 | - 'author' => $meta['attributedTo'], | |
| 586 | - 'mention' => $mention, | |
| 587 | - ); | |
| 1405 | + wp_send_json_success( '<li class="menu-item">' . implode( '</li><li class="menu-item">', $results ) . '</li>' ); | |
| 588 | 1406 | } |
| 589 | 1407 | |
| 590 | 1408 | /** |
| 591 | - * The Ajax function to fill the in-reply-to-preview. | |
| 1409 | + * The Add user search entries to the autocomplete results. | |
| 1410 | + * | |
| 1411 | + * @param array $results The autocomplete results. | |
| 1412 | + * @param string $q The query. | |
| 1413 | + * | |
| 1414 | + * @return array The autocomplete results. | |
| 592 | 1415 | */ |
| 593 | - public function ajax_in_reply_to_preview() { | |
| 594 | - $url = wp_unslash( $_POST['url'] ); | |
| 1416 | + public function autocomplete_user_search( $results, $q ) { | |
| 1417 | + $users = User_Query::search( '*' . $q . '*' ); | |
| 595 | 1418 | |
| 596 | - if ( ! wp_parse_url( $url ) ) { | |
| 597 | - wp_send_json_error(); | |
| 598 | - exit; | |
| 1419 | + foreach ( $users->get_results() as $friend ) { | |
| 1420 | + $result = '<a href="' . esc_url( $friend->get_local_friends_page_url() ) . '" class="has-icon-left">'; | |
| 1421 | + $result .= '<span class="ab-icon dashicons dashicons-businessperson"></span>'; | |
| 1422 | + $result .= str_ireplace( $q, '<mark>' . $q . '</mark>', $friend->display_name ); | |
| 1423 | + $result .= ' <small>'; | |
| 1424 | + $result .= str_ireplace( $q, '<mark>' . $q . '</mark>', $friend->user_login ); | |
| 1425 | + $result .= '</small></a>'; | |
| 1426 | + $results[] = $result; | |
| 599 | 1427 | } |
| 600 | 1428 | |
| 601 | - $meta = $this->get_in_reply_to_metadata( $_POST['url'] ); | |
| 1429 | + return $results; | |
| 1430 | + } | |
| 602 | 1431 | |
| 603 | - if ( is_wp_error( $meta ) ) { | |
| 604 | - wp_send_json_error( $meta->get_error_message() ); | |
| 605 | - exit; | |
| 606 | - } | |
| 607 | - wp_send_json_success( $meta ); | |
| 608 | - } | |
| 609 | 1432 | /** |
| 610 | - * The Ajax function to autocomplete search. | |
| 1433 | + * Filter comment links to use ActivityPub source URL in AJAX context. | |
| 1434 | + * | |
| 1435 | + * The ActivityPub plugin skips its filter when is_admin() is true, | |
| 1436 | + * but AJAX requests go through admin-ajax.php where is_admin() returns true. | |
| 1437 | + * | |
| 1438 | + * @param string $comment_link The comment permalink. | |
| 1439 | + * @param WP_Comment $comment The comment object. | |
| 1440 | + * @return string The filtered comment link. | |
| 611 | 1441 | */ |
| 612 | - public function ajax_autocomplete() { | |
| 613 | - $q = wp_unslash( $_POST['q'] ); | |
| 614 | - $users = User_Query::search( '*' . $q . '*' ); | |
| 615 | - $results = array(); | |
| 616 | - foreach ( $users->get_results() as $friend ) { | |
| 617 | - $result = '<li class="menu-item">'; | |
| 618 | - $result .= '<a href="' . esc_url( $friend->get_local_friends_page_url() ) . '" class="has-icon-left">'; | |
| 619 | - $result .= str_ireplace( $q, '<mark>' . $q . '</mark>', $friend->display_name ); | |
| 620 | - $result .= ' <small>'; | |
| 621 | - $result .= str_ireplace( $q, '<mark>' . $q . '</mark>', $friend->user_login ); | |
| 622 | - $result .= '</small></a></li>'; | |
| 623 | - $results[] = $result; | |
| 1442 | + public function activitypub_comment_link( $comment_link, $comment ) { | |
| 1443 | + if ( ! $comment || 'comment' !== $comment->comment_type ) { | |
| 1444 | + return $comment_link; | |
| 624 | 1445 | } |
| 625 | 1446 | |
| 626 | - wp_send_json_success( implode( PHP_EOL, $results ) ); | |
| 1447 | + $source_url = get_comment_meta( $comment->comment_ID, 'source_url', true ); | |
| 1448 | + if ( $source_url ) { | |
| 1449 | + return $source_url; | |
| 1450 | + } | |
| 1451 | + | |
| 1452 | + return $comment_link; | |
| 627 | 1453 | } |
| 628 | 1454 | |
| 629 | 1455 | /** |
| 630 | 1456 | * The Ajax function to load comments. |
| @@ -629,9 +1455,9 @@ | ||
| 629 | 1455 | /** |
| 630 | 1456 | * The Ajax function to load comments. |
| 631 | 1457 | */ |
| 632 | 1458 | public function ajax_load_comments() { |
| 633 | - if ( ! isset( $_POST['post_id'] ) || ! intval( $_POST['post_id'] ) ) { | |
| 1459 | + if ( ! isset( $_POST['post_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing | |
| 634 | 1460 | wp_send_json_error(); |
| 635 | 1461 | exit; |
| 636 | 1462 | } |
| 637 | 1463 | |
| @@ -637,84 +1463,68 @@ | ||
| 637 | 1463 | |
| 638 | 1464 | $post_id = intval( $_POST['post_id'] ); |
| 639 | 1465 | check_ajax_referer( "comments-$post_id" ); |
| 640 | 1466 | |
| 641 | - $comments = get_comments( | |
| 642 | - array( | |
| 643 | - 'post_id' => $post_id, | |
| 644 | - ) | |
| 1467 | + $comments = apply_filters( | |
| 1468 | + 'friends_get_comments', | |
| 1469 | + get_comments( | |
| 1470 | + array( | |
| 1471 | + 'post_id' => $post_id, | |
| 1472 | + 'status' => 'approve', | |
| 1473 | + 'order' => 'ASC', | |
| 1474 | + ) | |
| 1475 | + ), | |
| 1476 | + $post_id | |
| 645 | 1477 | ); |
| 646 | 1478 | |
| 647 | - $author_id = get_post_field( 'post_author', $post_id ); | |
| 648 | - $friend_user = new User( $author_id ); | |
| 1479 | + $friend_user = User::get_post_author( get_post( $post_id ) ); | |
| 1480 | + $user_feed_url = get_post_meta( $post_id, 'feed_url', true ); | |
| 1481 | + $user_feed = $this->friends->feed->get_user_feed_by_url( $user_feed_url ); | |
| 649 | 1482 | |
| 650 | - $comments_url = get_post_meta( $post_id, Feed::COMMENTS_FEED_META, true ); | |
| 651 | - if ( ! $comments_url && empty( $comments ) ) { | |
| 652 | - wp_send_json_error( __( 'No comments feed available.', 'friends' ) ); | |
| 653 | - exit; | |
| 654 | - } | |
| 1483 | + remove_all_filters( 'comment_form_before' ); | |
| 1484 | + remove_all_filters( 'comment_form_after' ); | |
| 655 | 1485 | |
| 656 | - if ( $friend_user->is_friend_url( $comments_url ) && friends::has_required_privileges() || wp_doing_cron() ) { | |
| 657 | - $comments_url = apply_filters( 'friends_friend_private_feed_url', $comments_url, $friend_user ); | |
| 658 | - $comments_url = $this->friends->access_control->append_auth( $comments_url, $friend_user, 300 ); | |
| 659 | - } | |
| 1486 | + // Add filter to fix ActivityPub comment links in AJAX context. | |
| 1487 | + add_filter( 'get_comment_link', array( $this, 'activitypub_comment_link' ), 20, 2 ); | |
| 660 | 1488 | |
| 661 | - $feed_comments = $this->friends->feed->preview( 'simplepie', $comments_url ); | |
| 662 | - if ( empty( $comments ) && ( is_wp_error( $feed_comments ) || ! is_array( $feed_comments ) ) ) { | |
| 663 | - wp_send_json_error( '<small>' . __( 'Unfortunately, comments were not available via RSS.', 'friends' ) . '</small>' ); | |
| 664 | - exit; | |
| 665 | - } elseif ( is_wp_error( $feed_comments ) ) { | |
| 666 | - $feed_comments = array(); | |
| 1489 | + if ( empty( $comments ) ) { | |
| 1490 | + $content = apply_filters( 'friends_no_comments_feed_available', __( 'No comments yet.', 'friends' ), $post_id, $friend_user, $user_feed ); | |
| 1491 | + } else { | |
| 1492 | + $template_loader = Friends::template_loader(); | |
| 1493 | + ob_start(); | |
| 1494 | + ?> | |
| 1495 | + <h5><?php esc_html_e( 'Comments' ); /* phpcs:ignore WordPress.WP.I18n.MissingArgDomain */ ?></h5> | |
| 1496 | + <ol class="comment-list"> | |
| 1497 | + <?php | |
| 1498 | + wp_list_comments( | |
| 1499 | + array( | |
| 1500 | + 'style' => 'ol', | |
| 1501 | + 'short_ping' => true, | |
| 1502 | + 'avatar_size' => 24, | |
| 1503 | + ), | |
| 1504 | + $comments | |
| 1505 | + ); | |
| 1506 | + ?> | |
| 1507 | + </ol><!-- .comment-list --> | |
| 1508 | + <?php | |
| 1509 | + $content = ob_get_contents(); | |
| 1510 | + ob_end_clean(); | |
| 667 | 1511 | } |
| 668 | 1512 | |
| 669 | - $template_loader = Friends::template_loader(); | |
| 670 | - ob_start(); | |
| 671 | - ?> | |
| 672 | - <h5><?php esc_html_e( 'Comments' ); /* phpcs:ignore WordPress.WP.I18n.MissingArgDomain */ ?></h5> | |
| 673 | - <?php | |
| 674 | - foreach ( $comments as $comment ) { | |
| 675 | - $template_loader->get_template_part( | |
| 676 | - 'frontend/parts/comment', | |
| 677 | - null, | |
| 678 | - array( | |
| 679 | - 'author' => $comment->comment_author, | |
| 680 | - 'date' => $comment->comment_date, | |
| 681 | - 'permalink' => $comment->guid . '#comment-' . $comment->comment_ID, | |
| 682 | - 'post_content' => $comment->comment_content, | |
| 683 | - ) | |
| 684 | - ); | |
| 685 | - } | |
| 686 | - foreach ( $feed_comments as $comment ) { | |
| 687 | - $template_loader->get_template_part( | |
| 688 | - 'frontend/parts/comment', | |
| 689 | - null, | |
| 690 | - array( | |
| 691 | - 'author' => $comment->author, | |
| 692 | - 'date' => $comment->date, | |
| 693 | - 'permalink' => $comment->permalink, | |
| 694 | - 'post_content' => $comment->post_content, | |
| 695 | - ) | |
| 696 | - ); | |
| 1513 | + remove_filter( 'get_comment_link', array( $this, 'activitypub_comment_link' ), 20 ); | |
| 697 | 1514 | |
| 698 | - } | |
| 699 | - ?> | |
| 700 | - <p> | |
| 701 | - <a href="<?php echo esc_url( get_comments_link( $post_id ) ); ?>"><?php esc_html_e( 'Leave a Comment' ); /* phpcs:ignore WordPress.WP.I18n.MissingArgDomain */ ?></a> | |
| 702 | - </p> | |
| 703 | - <?php | |
| 704 | - $content = ob_get_contents(); | |
| 705 | - ob_end_clean(); | |
| 1515 | + $content = apply_filters( 'friends_comments_content', $content, $post_id, $friend_user, $user_feed ); | |
| 706 | 1516 | |
| 707 | 1517 | wp_send_json_success( $content ); |
| 708 | 1518 | } |
| 709 | 1519 | |
| 710 | 1520 | public function ajax_star_friend_user() { |
| 711 | - if ( ! isset( $_POST['friend_id'] ) || ! $_POST['friend_id'] ) { | |
| 1521 | + if ( ! isset( $_POST['friend_id'] ) || ! isset( $_POST['starred'] ) ) { | |
| 712 | 1522 | wp_send_json_error(); |
| 713 | 1523 | exit; |
| 714 | 1524 | } |
| 715 | 1525 | |
| 716 | - $friend_id = wp_unslash( $_POST['friend_id'] ); | |
| 1526 | + $friend_id = sanitize_text_field( wp_unslash( $_POST['friend_id'] ) ); | |
| 717 | 1527 | check_ajax_referer( "star-$friend_id" ); |
| 718 | 1528 | |
| 719 | 1529 | $friend_user = User::get_by_username( $friend_id ); |
| 720 | 1530 | |
| @@ -728,15 +1538,66 @@ | ||
| 728 | 1538 | ); |
| 729 | 1539 | } |
| 730 | 1540 | |
| 731 | 1541 | /** |
| 1542 | + * Ajax handler to move a subscription to a folder. | |
| 1543 | + */ | |
| 1544 | + public function ajax_move_to_folder() { | |
| 1545 | + if ( ! current_user_can( Friends::REQUIRED_ROLE ) || ! isset( $_POST['friend_id'] ) ) { | |
| 1546 | + wp_send_json_error(); | |
| 1547 | + exit; | |
| 1548 | + } | |
| 1549 | + | |
| 1550 | + check_ajax_referer( 'friends-move-to-folder' ); | |
| 1551 | + | |
| 1552 | + $friend_id = sanitize_text_field( wp_unslash( $_POST['friend_id'] ) ); | |
| 1553 | + $folder_id = isset( $_POST['folder_id'] ) ? intval( $_POST['folder_id'] ) : 0; | |
| 1554 | + | |
| 1555 | + $friend_user = User::get_by_username( $friend_id ); | |
| 1556 | + if ( ! $friend_user || is_wp_error( $friend_user ) || ! ( $friend_user instanceof Subscription ) ) { | |
| 1557 | + wp_send_json_error( 'invalid-user' ); | |
| 1558 | + exit; | |
| 1559 | + } | |
| 1560 | + | |
| 1561 | + $result = $friend_user->move_to_folder( $folder_id ); | |
| 1562 | + wp_send_json_success( array( 'moved' => $result ) ); | |
| 1563 | + } | |
| 1564 | + | |
| 1565 | + /** | |
| 1566 | + * Ajax handler to create a new folder. | |
| 1567 | + */ | |
| 1568 | + public function ajax_create_folder() { | |
| 1569 | + if ( ! current_user_can( Friends::REQUIRED_ROLE ) || ! isset( $_POST['name'] ) ) { | |
| 1570 | + wp_send_json_error(); | |
| 1571 | + exit; | |
| 1572 | + } | |
| 1573 | + | |
| 1574 | + check_ajax_referer( 'friends-move-to-folder' ); | |
| 1575 | + | |
| 1576 | + $name = sanitize_text_field( wp_unslash( $_POST['name'] ) ); | |
| 1577 | + $parent_id = isset( $_POST['parent_id'] ) ? intval( $_POST['parent_id'] ) : 0; | |
| 1578 | + | |
| 1579 | + $folder = Subscription::create_folder( $name, $parent_id ); | |
| 1580 | + if ( is_wp_error( $folder ) ) { | |
| 1581 | + wp_send_json_error( $folder->get_error_message() ); | |
| 1582 | + exit; | |
| 1583 | + } | |
| 1584 | + | |
| 1585 | + wp_send_json_success( | |
| 1586 | + array( | |
| 1587 | + 'term_id' => $folder->term_id, | |
| 1588 | + 'name' => $folder->name, | |
| 1589 | + ) | |
| 1590 | + ); | |
| 1591 | + } | |
| 1592 | + | |
| 1593 | + /** | |
| 732 | 1594 | * Ensure that untrashed friends posts go back to published. |
| 733 | 1595 | * |
| 734 | 1596 | * @param string $new_status The new status of the post being restored. |
| 735 | 1597 | * @param int $post_id The ID of the post being restored. |
| 736 | - * @param string $previous_status The status of the post at the point where it was trashed. | |
| 737 | 1598 | */ |
| 738 | - public function untrash_post_status( $new_status, $post_id, $previous_status ) { | |
| 1599 | + public function untrash_post_status( $new_status, $post_id ) { | |
| 739 | 1600 | if ( ! in_array( get_post_type( $post_id ), apply_filters( 'friends_frontend_post_types', array() ), true ) ) { |
| 740 | 1601 | return $new_status; |
| 741 | 1602 | } |
| 742 | 1603 | return 'publish'; |
| @@ -752,32 +1613,169 @@ | ||
| 752 | 1613 | if ( ! Friends::on_frontend() ) { |
| 753 | 1614 | return $template; |
| 754 | 1615 | } |
| 755 | 1616 | |
| 756 | - if ( isset( $_GET['refresh'] ) ) { | |
| 757 | - add_filter( 'notify_about_new_friend_post', '__return_false', 999 ); | |
| 758 | - add_filter( | |
| 759 | - 'wp_feed_options', | |
| 760 | - function ( $feed ) { | |
| 761 | - $feed->enable_cache( false ); | |
| 1617 | + global $args; | |
| 1618 | + $args = array( | |
| 1619 | + 'friends' => $this->friends, | |
| 1620 | + 'friend_user' => $this->author, | |
| 1621 | + 'post_format' => $this->post_format, | |
| 1622 | + ); | |
| 1623 | + | |
| 1624 | + if ( $this->template ) { | |
| 1625 | + global $wp_query; | |
| 1626 | + $wp_query->is_404 = false; | |
| 1627 | + | |
| 1628 | + status_header( 200 ); | |
| 1629 | + | |
| 1630 | + if ( 'block' === $this->theme ) { | |
| 1631 | + $block_template_content = $this->get_block_template_content_for( $this->template ); | |
| 1632 | + if ( false !== $block_template_content ) { | |
| 1633 | + global $_wp_current_template_content, $_wp_current_template_id; | |
| 1634 | + $_wp_current_template_content = $block_template_content; | |
| 1635 | + $_wp_current_template_id = get_stylesheet() . '//friends-' . basename( $this->template ); | |
| 1636 | + return ABSPATH . WPINC . '/template-canvas.php'; | |
| 762 | 1637 | } |
| 763 | - ); | |
| 764 | - $this->friends->feed->retrieve_friend_posts( true ); | |
| 1638 | + } | |
| 1639 | + | |
| 1640 | + if ( 'frontend/index' === $this->template ) { | |
| 1641 | + $args['frontend_default_view'] = get_user_option( 'friends_frontend_default_view', get_current_user_id() ); | |
| 1642 | + | |
| 1643 | + } | |
| 1644 | + return Friends::template_loader()->get_template_part( $this->template, null, $args, false ); | |
| 765 | 1645 | } |
| 766 | 1646 | |
| 767 | - global $args; | |
| 768 | - $args = array( | |
| 769 | - 'friends' => $this->friends, | |
| 770 | - 'friend_user' => $this->author, | |
| 771 | - 'frontend_default_view' => get_option( 'friends_frontend_default_view', 'expanded' ), | |
| 772 | - 'blocks-everywhere' => get_user_option( 'friends_blocks_everywhere' ), | |
| 1647 | + if ( 'block' === $this->theme ) { | |
| 1648 | + global $wp_query; | |
| 1649 | + if ( $wp_query->is_single ) { | |
| 1650 | + $template_key = 'frontend/single'; | |
| 1651 | + $template_id = 'friends-single'; | |
| 1652 | + } elseif ( $this->author ) { | |
| 1653 | + $template_key = 'frontend/author-index'; | |
| 1654 | + $template_id = 'friends-author-index'; | |
| 1655 | + } else { | |
| 1656 | + $template_key = 'frontend/index'; | |
| 1657 | + $template_id = 'friends-index'; | |
| 1658 | + } | |
| 1659 | + $block_template_content = $this->get_block_template_content_for( $template_key ); | |
| 1660 | + if ( false !== $block_template_content ) { | |
| 1661 | + global $_wp_current_template_content, $_wp_current_template_id; | |
| 1662 | + $_wp_current_template_content = $block_template_content; | |
| 1663 | + $_wp_current_template_id = get_stylesheet() . '//' . $template_id; | |
| 1664 | + return ABSPATH . WPINC . '/template-canvas.php'; | |
| 1665 | + } | |
| 1666 | + } | |
| 1667 | + | |
| 1668 | + $args['frontend_default_view'] = get_user_option( 'friends_frontend_default_view', get_current_user_id() ); | |
| 1669 | + $args['blocks-everywhere'] = false; | |
| 1670 | + | |
| 1671 | + if ( isset( $_GET['welcome'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 1672 | + $args['show_welcome'] = true; | |
| 1673 | + } | |
| 1674 | + | |
| 1675 | + return Friends::template_loader()->get_template_part( 'frontend/index', $this->post_format, $args, false ); | |
| 1676 | + } | |
| 1677 | + | |
| 1678 | + public function get_block_template_content_for( $template_path ) { | |
| 1679 | + $map = array( | |
| 1680 | + 'frontend/index' => 'index', | |
| 1681 | + 'frontend/author-index' => 'friends-author-index', | |
| 1682 | + 'frontend/single' => 'single-friend_post_cache', | |
| 1683 | + 'frontend/followers' => 'friends-followers', | |
| 1684 | + 'frontend/add-friend' => 'friends-add-friend', | |
| 1685 | + 'frontend/subscriptions' => 'friends-subscriptions', | |
| 773 | 1686 | ); |
| 1687 | + if ( ! isset( $map[ $template_path ] ) ) { | |
| 1688 | + return false; | |
| 1689 | + } | |
| 1690 | + $template = $map[ $template_path ]; | |
| 1691 | + if ( 'frontend/index' === $template_path && isset( $_GET['welcome'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 1692 | + $template = 'welcome'; | |
| 1693 | + } | |
| 1694 | + $file = FRIENDS_PLUGIN_DIR . 'themes/friends/templates/' . $template . '.html'; | |
| 1695 | + if ( ! file_exists( $file ) ) { | |
| 1696 | + return false; | |
| 1697 | + } | |
| 1698 | + return file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents | |
| 1699 | + } | |
| 774 | 1700 | |
| 775 | - if ( isset( $_GET['in_reply_to'] ) && wp_parse_url( $_GET['in_reply_to'] ) ) { | |
| 776 | - $args['in_reply_to'] = $args['friends']->frontend->get_in_reply_to_metadata( $_GET['in_reply_to'] ); | |
| 1701 | + public function get_static_frontend_template( $path ) { | |
| 1702 | + global $friends_args; | |
| 1703 | + global $wp_query; | |
| 1704 | + $wp_query->is_singular = false; | |
| 1705 | + | |
| 1706 | + switch ( $path ) { | |
| 1707 | + case 'add-friend': | |
| 1708 | + $path = 'frontend/add-friend'; | |
| 1709 | + break; | |
| 1710 | + | |
| 1711 | + case 'subscriptions': | |
| 1712 | + wp_safe_redirect( home_url( '/friends/following/' ) ); | |
| 1713 | + exit; | |
| 1714 | + | |
| 1715 | + case 'following': | |
| 1716 | + $friends_args = array(); | |
| 1717 | + $path = 'frontend/subscriptions'; | |
| 1718 | + break; | |
| 1719 | + | |
| 1720 | + case 'messages': | |
| 1721 | + $friends_args = array( | |
| 1722 | + 'title' => __( 'Direct Messages', 'friends' ), | |
| 1723 | + 'no-bottom-margin' => true, | |
| 1724 | + ); | |
| 1725 | + $path = 'frontend/messages'; | |
| 1726 | + break; | |
| 1727 | + | |
| 1728 | + case 'followers': | |
| 1729 | + if ( ! class_exists( '\Activitypub\Collection\Followers' ) ) { | |
| 1730 | + return 'frontend/index'; | |
| 1731 | + } | |
| 1732 | + | |
| 1733 | + $friends_args = array(); | |
| 1734 | + if ( isset( $_GET['mutual'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 1735 | + $friends_args['only_mutual'] = true; | |
| 1736 | + $friends_args['title'] = __( 'Your Mutual Followers', 'friends' ); | |
| 1737 | + } else { | |
| 1738 | + $friends_args['only_mutual'] = false; | |
| 1739 | + $friends_args['title'] = __( 'Your Followers', 'friends' ); | |
| 1740 | + } | |
| 1741 | + $friends_args['user_id'] = get_current_user_id(); | |
| 1742 | + $path = 'frontend/followers'; | |
| 1743 | + break; | |
| 1744 | + | |
| 1745 | + case 'blog-followers': | |
| 1746 | + $friends_args = array(); | |
| 1747 | + if ( ! class_exists( '\Activitypub\Collection\Actors' ) ) { | |
| 1748 | + return 'frontend/index'; | |
| 1749 | + } | |
| 1750 | + if ( isset( $_GET['mutual'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 1751 | + $friends_args['only_mutual'] = true; | |
| 1752 | + $friends_args['title'] = __( 'Your Mutual Blog Followers', 'friends' ); | |
| 1753 | + } else { | |
| 1754 | + $friends_args['only_mutual'] = false; | |
| 1755 | + $friends_args['title'] = __( 'Your Blog Followers', 'friends' ); | |
| 1756 | + } | |
| 1757 | + $friends_args['user_id'] = \Activitypub\Collection\Actors::BLOG_USER_ID; | |
| 1758 | + $path = 'frontend/followers'; | |
| 1759 | + break; | |
| 1760 | + case 'mutual': | |
| 1761 | + if ( ! class_exists( '\Activitypub\Collection\Followers' ) ) { | |
| 1762 | + return 'frontend/index'; | |
| 1763 | + } | |
| 1764 | + | |
| 1765 | + $friends_args = array(); | |
| 1766 | + $friends_args['title'] = __( 'Friends', 'friends' ); | |
| 1767 | + $friends_args['filter'] = 'following'; | |
| 1768 | + $friends_args['user_id'] = get_current_user_id(); | |
| 1769 | + $path = 'frontend/followers'; | |
| 1770 | + break; | |
| 1771 | + | |
| 1772 | + default: | |
| 1773 | + return 'frontend/index'; | |
| 777 | 1774 | } |
| 778 | 1775 | |
| 779 | - return Friends::template_loader()->get_template_part( 'frontend/index', null, $args, false ); | |
| 1776 | + $wp_query->is_404 = false; | |
| 1777 | + return $path; | |
| 780 | 1778 | } |
| 781 | 1779 | |
| 782 | 1780 | /** |
| 783 | 1781 | * Modify the Friends page title depending on context, for example add an author name or post format. |
| @@ -788,9 +1786,9 @@ | ||
| 788 | 1786 | */ |
| 789 | 1787 | public function header_widget_title( $title ) { |
| 790 | 1788 | $title = '<a href="' . esc_url( home_url( '/friends/' ) ) . '">' . esc_html( $title ) . '</a>'; |
| 791 | 1789 | if ( $this->author ) { |
| 792 | - $title .= ' » ' . '<a href="' . esc_url( $this->author->get_local_friends_page_url() ) . '">' . esc_html( $this->author->display_name ) . '</a>'; | |
| 1790 | + $title .= ' » <a href="' . esc_url( $this->author->get_local_friends_page_url() ) . '">' . esc_html( $this->author->display_name ) . '</a>'; | |
| 793 | 1791 | } |
| 794 | 1792 | if ( $this->post_format ) { |
| 795 | 1793 | $post_formats = get_post_format_strings(); |
| 796 | 1794 | $title .= ' » ' . $post_formats[ $this->post_format ]; |
| @@ -805,9 +1803,9 @@ | ||
| 805 | 1803 | * @param string $text The link text. |
| 806 | 1804 | * @param array $html_attributes HTML attributes. |
| 807 | 1805 | * @param User $friend_user The friend user. |
| 808 | 1806 | */ |
| 809 | - public function link( $url, $text, array $html_attributes = array(), User $friend_user = null ) { | |
| 1807 | + public function link( $url, $text, array $html_attributes = array(), ?User $friend_user = null ) { | |
| 810 | 1808 | echo wp_kses( |
| 811 | 1809 | self::get_link( $url, $text, $html_attributes, $friend_user ), |
| 812 | 1810 | array( |
| 813 | 1811 | 'a' => array( |
| @@ -838,28 +1836,13 @@ | ||
| 838 | 1836 | * @param User $friend_user The friend user. |
| 839 | 1837 | * |
| 840 | 1838 | * @return string The link. |
| 841 | 1839 | */ |
| 842 | - public static function get_link( $url, $text, array $html_attributes = array(), User $friend_user = null ) { | |
| 1840 | + public static function get_link( $url, $text, array $html_attributes = array(), ?User $friend_user = null ) { | |
| 843 | 1841 | if ( is_null( $friend_user ) ) { |
| 844 | 1842 | $friend_user = new User( get_the_author_meta( 'ID' ) ); |
| 845 | 1843 | } |
| 846 | 1844 | |
| 847 | - if ( $friend_user->is_friend_url( $url ) && $friend_user->is_valid_friend() ) { | |
| 848 | - $html_attributes['target'] = '_blank'; | |
| 849 | - $html_attributes['rel'] = 'noopener noreferrer'; | |
| 850 | - if ( ! isset( $html_attributes['class'] ) ) { | |
| 851 | - $html_attributes['class'] = ''; | |
| 852 | - } | |
| 853 | - $html_attributes['class'] = trim( $html_attributes['class'] . ' friends-auth-link' ); | |
| 854 | - if ( ! isset( $html_attributes['dashicon_back'] ) ) { | |
| 855 | - $html_attributes['dashicon_back'] = 'admin-users'; | |
| 856 | - } | |
| 857 | - $html_attributes['data-token'] = $friend_user->get_friend_auth(); | |
| 858 | - $html_attributes['data-nonce'] = wp_create_nonce( 'auth-link-' . $url ); | |
| 859 | - $html_attributes['data-friend'] = $friend_user->user_login; | |
| 860 | - } | |
| 861 | - | |
| 862 | 1845 | $link = '<a href="' . esc_url( $url ) . '"'; |
| 863 | 1846 | foreach ( $html_attributes as $name => $value ) { |
| 864 | 1847 | if ( ! in_array( $name, array( 'title', 'target', 'rel', 'class', 'style', 'data-nonce', 'data-cnonce', 'data-token', 'data-friend', 'data-id', 'data-url' ) ) ) { |
| 865 | 1848 | continue; |
| @@ -865,13 +1848,13 @@ | ||
| 865 | 1848 | continue; |
| 866 | 1849 | } |
| 867 | 1850 | $link .= ' ' . $name . '="' . esc_attr( $value ) . '"'; |
| 868 | 1851 | } |
| 869 | - $link .= '>'; | |
| 1852 | + $link .= ' title="' . esc_attr( $text ) . '">'; | |
| 870 | 1853 | if ( isset( $html_attributes['dashicon_front'] ) ) { |
| 871 | 1854 | $link .= '<span class="dashicons dashicons-' . esc_attr( $html_attributes['dashicon_front'] ) . '"></span>'; |
| 872 | 1855 | } |
| 873 | - $link .= esc_html( $text ); | |
| 1856 | + $link .= '<span class="text">' . esc_html( $text ) . '</span>'; | |
| 874 | 1857 | if ( isset( $html_attributes['dashicon_back'] ) ) { |
| 875 | 1858 | $link .= '<span class="dashicons dashicons-' . esc_attr( $html_attributes['dashicon_back'] ) . '"></span>'; |
| 876 | 1859 | } |
| 877 | 1860 | $link .= '</a>'; |
| @@ -878,8 +1861,58 @@ | ||
| 878 | 1861 | |
| 879 | 1862 | return $link; |
| 880 | 1863 | } |
| 881 | 1864 | |
| 1865 | + public static function get_widget_open_state( $widget ) { | |
| 1866 | + static $state = null; | |
| 1867 | + if ( is_null( $state ) ) { | |
| 1868 | + $state = get_user_meta( get_current_user_id(), 'friends_widget_state', true ); | |
| 1869 | + if ( ! is_array( $state ) ) { | |
| 1870 | + $state = array(); | |
| 1871 | + } | |
| 1872 | + } | |
| 1873 | + return isset( $state[ $widget ] ) && is_string( $state[ $widget ] ) ? $state[ $widget ] : 'open'; | |
| 1874 | + } | |
| 1875 | + | |
| 1876 | + public function ajax_set_widget_open_state() { | |
| 1877 | + if ( ! isset( $_POST['widget'] ) || ! isset( $_POST['state'] ) ) { | |
| 1878 | + wp_send_json_error(); | |
| 1879 | + exit; | |
| 1880 | + } | |
| 1881 | + | |
| 1882 | + check_ajax_referer( 'friends_widget_state' ); | |
| 1883 | + | |
| 1884 | + $widget = sanitize_text_field( wp_unslash( $_POST['widget'] ) ); | |
| 1885 | + $open_state = sanitize_text_field( wp_unslash( $_POST['state'] ) ); | |
| 1886 | + | |
| 1887 | + $state = get_user_meta( get_current_user_id(), 'friends_widget_state', true ); | |
| 1888 | + if ( ! is_array( $state ) ) { | |
| 1889 | + $state = array(); | |
| 1890 | + } | |
| 1891 | + $state[ $widget ] = $open_state; | |
| 1892 | + update_user_meta( get_current_user_id(), 'friends_widget_state', $state ); | |
| 1893 | + | |
| 1894 | + wp_send_json_success(); | |
| 1895 | + } | |
| 1896 | + | |
| 1897 | + public function ajax_get_post_counts() { | |
| 1898 | + check_ajax_referer( 'friends_post_counts' ); | |
| 1899 | + $counts = array_fill_keys( get_post_format_slugs(), 0 ); | |
| 1900 | + | |
| 1901 | + foreach ( $this->friends->get_post_count_by_post_format( true ) as $post_format => $count ) { | |
| 1902 | + $counts[ $post_format ] = $this->friends->get_post_format_plural_string( $post_format, $count ); | |
| 1903 | + } | |
| 1904 | + | |
| 1905 | + $post_counts = $this->friends->get_post_count_by_post_status( true ); | |
| 1906 | + if ( isset( $post_counts->trash ) && $post_counts->trash ) { | |
| 1907 | + $counts['trash'] = sprintf( /* translators: %s is the number of hidden posts */_n( '%s hidden items', '%s hidden items', $post_counts->trash, 'friends' ), number_format_i18n( $post_counts->trash ) ); | |
| 1908 | + } else { | |
| 1909 | + $counts['trash'] = 0; | |
| 1910 | + } | |
| 1911 | + | |
| 1912 | + wp_send_json_success( $counts ); | |
| 1913 | + } | |
| 1914 | + | |
| 882 | 1915 | /** |
| 883 | 1916 | * Don't show the edit link for friend posts. |
| 884 | 1917 | * |
| 885 | 1918 | * @param string $link The edit link. |
| @@ -919,9 +1952,9 @@ | ||
| 919 | 1952 | * Link friend posts to the remote site. |
| 920 | 1953 | * |
| 921 | 1954 | * @param string $post_link The post's permalink. |
| 922 | 1955 | * @param \WP_Post $post The post in question. |
| 923 | - * @reeturn string The overriden post link. | |
| 1956 | + * @return string The overriden post link. | |
| 924 | 1957 | */ |
| 925 | 1958 | public function friend_post_link( $post_link, \WP_Post $post ) { |
| 926 | 1959 | if ( $post && in_array( $post->post_type, apply_filters( 'friends_frontend_post_types', array() ), true ) ) { |
| 927 | 1960 | return get_the_guid( $post ); |
| @@ -929,8 +1962,29 @@ | ||
| 929 | 1962 | return $post_link; |
| 930 | 1963 | } |
| 931 | 1964 | |
| 932 | 1965 | /** |
| 1966 | + * Link friend post comments to the local friends page. | |
| 1967 | + * | |
| 1968 | + * @param string $link The comment permalink. | |
| 1969 | + * @param \WP_Comment $comment The comment object. | |
| 1970 | + * @return string The overriden comment link. | |
| 1971 | + */ | |
| 1972 | + public function friend_post_comment_link( $link, \WP_Comment $comment ) { | |
| 1973 | + // Don't override external ActivityPub comments - they should link to their source. | |
| 1974 | + if ( 'activitypub' === $comment->comment_type || 'activitypub' === get_comment_meta( $comment->comment_ID, 'protocol', true ) ) { | |
| 1975 | + return $link; | |
| 1976 | + } | |
| 1977 | + | |
| 1978 | + $post = get_post( $comment->comment_post_ID ); | |
| 1979 | + if ( $post && in_array( $post->post_type, apply_filters( 'friends_frontend_post_types', array() ), true ) ) { | |
| 1980 | + $friend_user = User::get_post_author( $post ); | |
| 1981 | + return $friend_user->get_local_friends_page_url( $post->ID ) . '#comment-' . $comment->comment_ID; | |
| 1982 | + } | |
| 1983 | + return $link; | |
| 1984 | + } | |
| 1985 | + | |
| 1986 | + /** | |
| 933 | 1987 | * Potentially override the post author name with metadata. |
| 934 | 1988 | * |
| 935 | 1989 | * @param string $overridden_author_name The already overridden author name. |
| 936 | 1990 | * @param string $author_name The author name. |
| @@ -948,8 +2002,15 @@ | ||
| 948 | 2002 | } |
| 949 | 2003 | return $author_name; |
| 950 | 2004 | } |
| 951 | 2005 | |
| 2006 | + public function expose_opml( $viewable, $pagename ) { | |
| 2007 | + if ( 'opml' === $pagename ) { | |
| 2008 | + return true; | |
| 2009 | + } | |
| 2010 | + return $viewable; | |
| 2011 | + } | |
| 2012 | + | |
| 952 | 2013 | /** |
| 953 | 2014 | * Render the Friends OPML |
| 954 | 2015 | * |
| 955 | 2016 | * @param bool $only_public Only public feed URLs. |
| @@ -954,12 +2015,18 @@ | ||
| 954 | 2015 | * |
| 955 | 2016 | * @param bool $only_public Only public feed URLs. |
| 956 | 2017 | */ |
| 957 | 2018 | protected function render_opml( $only_public = false ) { |
| 958 | - $user = wp_get_current_user(); | |
| 2019 | + if ( ! \is_user_logged_in() ) { | |
| 2020 | + $only_public = true; | |
| 2021 | + $user_id = Friends::get_main_friend_user_id(); | |
| 2022 | + $user = new User( $user_id ); | |
| 2023 | + } else { | |
| 2024 | + $user = wp_get_current_user(); | |
| 2025 | + } | |
| 959 | 2026 | |
| 960 | 2027 | // translators: %s is a name. |
| 961 | - $title = sprintf( __( "%s' Subscriptions", 'friends' ), $user->display_name ); | |
| 2028 | + $title = sprintf( __( '%s is following', 'friends' ), $user->display_name ); | |
| 962 | 2029 | $filename = 'friends-'; |
| 963 | 2030 | if ( ! $only_public ) { |
| 964 | 2031 | $title = __( 'My Friends', 'friends' ); |
| 965 | 2032 | $filename .= 'private-'; |
| @@ -968,9 +2035,9 @@ | ||
| 968 | 2035 | |
| 969 | 2036 | $feeds = array(); |
| 970 | 2037 | $users = array(); |
| 971 | 2038 | |
| 972 | - $friend_users = new User_Query( array( 'role__in' => array( 'friend', 'acquaintance', 'friend_request', 'subscription' ) ) ); | |
| 2039 | + $friend_users = User_Query::all_associated_users(); | |
| 973 | 2040 | foreach ( $friend_users->get_results() as $friend_user ) { |
| 974 | 2041 | $role = $friend_user->get_role_name( true, 9 ); |
| 975 | 2042 | if ( $only_public ) { |
| 976 | 2043 | $role = 'Feeds'; |
| @@ -988,8 +2055,11 @@ | ||
| 988 | 2055 | |
| 989 | 2056 | $need_local_feed = false; |
| 990 | 2057 | |
| 991 | 2058 | foreach ( $user_feeds as $feed ) { |
| 2059 | + if ( $feed->get_parser() === Feed_Parser_SimplePie::SLUG ) { | |
| 2060 | + break; | |
| 2061 | + } | |
| 992 | 2062 | switch ( $feed->get_mime_type() ) { |
| 993 | 2063 | case 'application/atom+xml': |
| 994 | 2064 | case 'application/atomxml': |
| 995 | 2065 | case 'application/rss+xml': |
| @@ -1023,14 +2093,19 @@ | ||
| 1023 | 2093 | if ( $only_public ) { |
| 1024 | 2094 | // Cannot create a public URL. |
| 1025 | 2095 | continue; |
| 1026 | 2096 | } |
| 1027 | - $xml_url = $feed->get_local_url() . '?auth=' . $_GET['auth']; | |
| 2097 | + $xml_url = $feed->get_local_url(); | |
| 2098 | + // phpcs:disable WordPress.Security.NonceVerification.Recommended | |
| 2099 | + if ( isset( $_GET['auth'] ) ) { | |
| 2100 | + $xml_url = add_query_arg( 'auth', sanitize_text_field( wp_unslash( $_GET['auth'] ) ), $xml_url ); | |
| 2101 | + // phpcs:enable WordPress.Security.NonceVerification.Recommended | |
| 2102 | + } | |
| 1028 | 2103 | } else { |
| 1029 | 2104 | if ( $only_public ) { |
| 1030 | 2105 | $xml_url = $feed->get_url(); |
| 1031 | 2106 | } else { |
| 1032 | - $xml_url = $feed->get_private_url( YEAR_IN_SECONDS ); | |
| 2107 | + $xml_url = $feed->get_url(); | |
| 1033 | 2108 | } |
| 1034 | 2109 | if ( 'application/atom+xml' === $feed->get_mime_type() ) { |
| 1035 | 2110 | $type = 'atom'; |
| 1036 | 2111 | } |
| @@ -1086,8 +2161,40 @@ | ||
| 1086 | 2161 | return false; |
| 1087 | 2162 | } |
| 1088 | 2163 | |
| 1089 | 2164 | /** |
| 2165 | + * Exclude the compose post format from the main RSS feed. | |
| 2166 | + * | |
| 2167 | + * @param \WP_Query $query The main query. | |
| 2168 | + * @return \WP_Query The modified main query. | |
| 2169 | + */ | |
| 2170 | + public function exclude_compose_format_from_feed( $query ) { | |
| 2171 | + if ( ! $query->is_main_query() || ! $query->is_feed() ) { | |
| 2172 | + return $query; | |
| 2173 | + } | |
| 2174 | + | |
| 2175 | + if ( ! get_option( 'friends_exclude_compose_format_from_feed' ) ) { | |
| 2176 | + return $query; | |
| 2177 | + } | |
| 2178 | + | |
| 2179 | + $format = get_option( 'friends_compose_post_format', 'status' ); | |
| 2180 | + if ( ! $format || 'standard' === $format ) { | |
| 2181 | + return $query; | |
| 2182 | + } | |
| 2183 | + | |
| 2184 | + $tax_query = (array) $query->get( 'tax_query' ); | |
| 2185 | + $tax_query[] = array( | |
| 2186 | + 'taxonomy' => 'post_format', | |
| 2187 | + 'field' => 'slug', | |
| 2188 | + 'terms' => array( 'post-format-' . $format ), | |
| 2189 | + 'operator' => 'NOT IN', | |
| 2190 | + ); | |
| 2191 | + $query->set( 'tax_query', $tax_query ); | |
| 2192 | + | |
| 2193 | + return $query; | |
| 2194 | + } | |
| 2195 | + | |
| 2196 | + /** | |
| 1090 | 2197 | * Modify the main query for the /friends page |
| 1091 | 2198 | * |
| 1092 | 2199 | * @param \WP_Query $query The main query. |
| 1093 | 2200 | * @return \WP_Query The modified main query. |
| @@ -1100,8 +2207,10 @@ | ||
| 1100 | 2207 | |
| 1101 | 2208 | $pagename = ''; |
| 1102 | 2209 | if ( isset( $wp_query->query['pagename'] ) ) { |
| 1103 | 2210 | $pagename = $wp_query->query['pagename']; |
| 2211 | + } elseif ( isset( $wp_query->query['category_name'] ) ) { | |
| 2212 | + $pagename = $wp_query->query['category_name']; | |
| 1104 | 2213 | } elseif ( isset( $wp_query->query['name'] ) ) { |
| 1105 | 2214 | $pagename = $wp_query->query['name']; |
| 1106 | 2215 | } |
| 1107 | 2216 | |
| @@ -1106,19 +2215,23 @@ | ||
| 1106 | 2215 | } |
| 1107 | 2216 | |
| 1108 | 2217 | $pagename_parts = explode( '/', trim( $pagename, '/' ) ); |
| 1109 | 2218 | $is_friends = array_shift( $pagename_parts ); |
| 2219 | + | |
| 1110 | 2220 | if ( 'friends' !== $is_friends ) { |
| 1111 | 2221 | return $query; |
| 1112 | 2222 | } |
| 1113 | 2223 | |
| 2224 | + if ( isset( $pagename_parts[0] ) && 'settings' === $pagename_parts[0] ) { | |
| 2225 | + wp_safe_redirect( self_admin_url( 'admin.php?page=friends-settings' ) ); | |
| 2226 | + exit; | |
| 2227 | + } | |
| 2228 | + | |
| 1114 | 2229 | // Not available for the general public or friends. |
| 1115 | 2230 | $viewable = $this->has_required_priviledges() && Friends::on_frontend(); |
| 1116 | 2231 | if ( $query->is_feed() ) { |
| 1117 | 2232 | // Feeds can be viewed through extra authentication. |
| 1118 | - if ( $this->friends->access_control->private_rss_is_authenticated() ) { | |
| 1119 | - $viewable = true; | |
| 1120 | - } elseif ( isset( $wp_query->query['pagename'] ) ) { | |
| 2233 | + if ( isset( $wp_query->query['pagename'] ) ) { | |
| 1121 | 2234 | if ( apply_filters( 'friends_friend_feed_viewable', false, isset( $pagename_parts[0] ) ? $pagename_parts[0] : false ) ) { |
| 1122 | 2235 | $viewable = true; |
| 1123 | 2236 | } |
| 1124 | 2237 | } |
| @@ -1130,9 +2243,9 @@ | ||
| 1130 | 2243 | } |
| 1131 | 2244 | } |
| 1132 | 2245 | |
| 1133 | 2246 | $page_id = get_query_var( 'page' ); |
| 1134 | - | |
| 2247 | + // phpcs:disable WordPress.Security.NonceVerification.Recommended | |
| 1135 | 2248 | if ( isset( $_GET['share'] ) ) { |
| 1136 | 2249 | $share_hash = hash( 'crc32b', apply_filters( 'friends_share_salt', wp_salt( 'nonce' ), $page_id ) . $page_id ); |
| 1137 | 2250 | if ( $_GET['share'] === $share_hash ) { |
| 1138 | 2251 | $viewable = true; |
| @@ -1148,9 +2261,9 @@ | ||
| 1148 | 2261 | wp_safe_redirect( home_url( '/friends/' ) ); |
| 1149 | 2262 | exit; |
| 1150 | 2263 | } |
| 1151 | 2264 | } elseif ( ! Friends::is_main_user() ) { |
| 1152 | - wp_die( __( 'You are not allowed to view this page.', 'friends' ) ); | |
| 2265 | + wp_die( esc_html__( 'You are not allowed to view this page.', 'friends' ) ); | |
| 1153 | 2266 | } |
| 1154 | 2267 | |
| 1155 | 2268 | return $query; |
| 1156 | 2269 | } |
| @@ -1185,9 +2298,9 @@ | ||
| 1185 | 2298 | } |
| 1186 | 2299 | |
| 1187 | 2300 | switch ( $current_part ) { |
| 1188 | 2301 | case 'opml': |
| 1189 | - return $this->render_opml( isset( $_REQUEST['public'] ) ); | |
| 2302 | + return $this->render_opml( isset( $_REQUEST['public'] ) && boolval( $_REQUEST['public'] ) ); | |
| 1190 | 2303 | |
| 1191 | 2304 | case 'type': |
| 1192 | 2305 | $post_format = array_shift( $pagename_parts ); |
| 1193 | 2306 | $tax_query = $this->friends->wp_query_get_post_format_tax_query( $tax_query, explode( ',', $post_format ) ); |
| @@ -1195,8 +2308,19 @@ | ||
| 1195 | 2308 | $this->post_format = $post_format; |
| 1196 | 2309 | } |
| 1197 | 2310 | break; |
| 1198 | 2311 | |
| 2312 | + case 'tag': | |
| 2313 | + if ( empty( $pagename_parts ) && $page_id ) { | |
| 2314 | + // Support numeric tags. | |
| 2315 | + $this->tag = strval( $page_id ); | |
| 2316 | + $page_id = false; | |
| 2317 | + } else { | |
| 2318 | + $this->tag = array_shift( $pagename_parts ); | |
| 2319 | + } | |
| 2320 | + $tax_query = $this->friends->wp_query_get_post_tag_tax_query( $tax_query, $this->tag ); | |
| 2321 | + break; | |
| 2322 | + | |
| 1199 | 2323 | default: // Maybe an author. |
| 1200 | 2324 | $author = User::get_by_username( $current_part ); |
| 1201 | 2325 | if ( false === $author || is_wp_error( $author ) ) { |
| 1202 | 2326 | if ( $query->is_feed() ) { |
| @@ -1203,8 +2327,18 @@ | ||
| 1203 | 2327 | status_header( 404 ); |
| 1204 | 2328 | $query->set_404(); |
| 1205 | 2329 | return $query; |
| 1206 | 2330 | } |
| 2331 | + | |
| 2332 | + $template = $this->get_static_frontend_template( $current_part ); | |
| 2333 | + if ( 'frontend/index' !== $template ) { | |
| 2334 | + $wp_query->is_404 = false; | |
| 2335 | + $query->is_404 = false; | |
| 2336 | + status_header( 200 ); | |
| 2337 | + add_filter( 'pre_handle_404', '__return_true' ); | |
| 2338 | + $this->template = $template; | |
| 2339 | + return $query; | |
| 2340 | + } | |
| 1207 | 2341 | wp_safe_redirect( home_url( '/friends/' ) ); |
| 1208 | 2342 | exit; |
| 1209 | 2343 | } |
| 1210 | 2344 | |
| @@ -1216,8 +2350,10 @@ | ||
| 1216 | 2350 | $this->is_friends_page = true; |
| 1217 | 2351 | $query->is_friends_page = true; |
| 1218 | 2352 | $query->is_singular = false; |
| 1219 | 2353 | $query->is_single = false; |
| 2354 | + $query->is_category = false; | |
| 2355 | + $query->is_archive = false; | |
| 1220 | 2356 | $query->queried_object = null; |
| 1221 | 2357 | $query->queried_object_id = null; |
| 1222 | 2358 | $post_types = apply_filters( 'friends_frontend_post_types', array() ); |
| 1223 | 2359 | |
| @@ -1229,22 +2365,23 @@ | ||
| 1229 | 2365 | $query->set( 'post_type', $post_types ); |
| 1230 | 2366 | $query->set( 'tax_query', $tax_query ); |
| 1231 | 2367 | |
| 1232 | 2368 | if ( ( isset( $_GET['share'] ) && $_GET['share'] === $share_hash ) || $viewable ) { |
| 1233 | - $post_status = array( 'publish', 'private' ); | |
| 2369 | + $post_status = array( 'publish', 'private', 'future' ); | |
| 1234 | 2370 | if ( isset( $_GET['show-hidden'] ) ) { |
| 1235 | 2371 | $post_status[] = 'trash'; |
| 1236 | 2372 | } |
| 1237 | 2373 | $query->set( 'post_status', $post_status ); |
| 1238 | 2374 | } |
| 2375 | + // phpcs:enable WordPress.Security.NonceVerification.Recommended | |
| 1239 | 2376 | $query->is_page = false; |
| 1240 | - $query->is_comments_feed = false; | |
| 2377 | + $query->is_comment_feed = false; | |
| 1241 | 2378 | $query->set( 'pagename', null ); |
| 1242 | - if ( 'collapsed' === get_option( 'friends_frontend_default_view', 'expanded' ) && get_option( 'posts_per_page' ) < 20 ) { | |
| 1243 | - if ( 'status' === $post_format ) { | |
| 2379 | + $query->set( 'category_name', null ); | |
| 2380 | + if ( get_option( 'posts_per_page' ) < 20 ) { | |
| 2381 | + $query->set( 'posts_per_page', 20 ); | |
| 2382 | + if ( 'collapsed' === get_user_option( 'friends_frontend_default_view', get_current_user_id() ) && 'status' === $post_format ) { | |
| 1244 | 2383 | $query->set( 'posts_per_page', 30 ); |
| 1245 | - } else { | |
| 1246 | - $query->set( 'posts_per_page', 20 ); | |
| 1247 | 2384 | } |
| 1248 | 2385 | } |
| 1249 | 2386 | |
| 1250 | 2387 | if ( $page_id ) { |
| @@ -1250,11 +2387,14 @@ | ||
| 1250 | 2387 | if ( $page_id ) { |
| 1251 | 2388 | $query->set( 'page_id', $page_id ); |
| 1252 | 2389 | if ( ! $this->author ) { |
| 1253 | 2390 | $post = get_post( $page_id ); |
| 1254 | - $author = User::get_post_author( $post ); | |
| 1255 | - if ( false !== $author ) { | |
| 1256 | - $this->author = $author; | |
| 2391 | + | |
| 2392 | + if ( $post ) { | |
| 2393 | + $author = User::get_post_author( $post ); | |
| 2394 | + if ( false !== $author ) { | |
| 2395 | + $this->author = $author; | |
| 2396 | + } | |
| 1257 | 2397 | } |
| 1258 | 2398 | } |
| 1259 | 2399 | $query->is_single = true; |
| 1260 | 2400 | $query->is_singular = true; |
| @@ -1263,9 +2403,9 @@ | ||
| 1263 | 2403 | } |
| 1264 | 2404 | |
| 1265 | 2405 | if ( $this->author ) { |
| 1266 | 2406 | if ( $this->author instanceof User ) { |
| 1267 | - $authordata = $this->author; | |
| 2407 | + $authordata = $this->author; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited | |
| 1268 | 2408 | $this->author->modify_query_by_author( $query ); |
| 1269 | 2409 | if ( ! $page_id ) { |
| 1270 | 2410 | $query->is_author = true; |
| 1271 | 2411 | } |
| @@ -1282,8 +2422,17 @@ | ||
| 1282 | 2422 | if ( $hide_from_friends_page ) { |
| 1283 | 2423 | $query->set( 'author__not_in', $hide_from_friends_page ); |
| 1284 | 2424 | } |
| 1285 | 2425 | } |
| 2426 | + | |
| 2427 | + // phpcs:disable WordPress.Security.NonceVerification.Recommended | |
| 2428 | + if ( isset( $_GET['order'] ) ) { | |
| 2429 | + $order = strtoupper( sanitize_text_field( wp_unslash( $_GET['order'] ) ) ); | |
| 2430 | + if ( in_array( $order, array( 'ASC', 'DESC' ), true ) ) { | |
| 2431 | + $query->set( 'order', $order ); | |
| 2432 | + } | |
| 2433 | + } | |
| 2434 | + // phpcs:enable WordPress.Security.NonceVerification.Recommended | |
| 1286 | 2435 | |
| 1287 | 2436 | return $query; |
| 1288 | 2437 | } |
| 1289 | 2438 | } |