403 ) ); } self::render( isset( $_GET['act'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display only. exit; } /** * "Stubli app" link in the toolbar. * * @param \WP_Admin_Bar $bar Toolbar. */ public static function admin_bar( $bar ) { if ( current_user_can( 'publish_posts' ) ) { $bar->add_node( array( 'id' => 'stubli-app', 'title' => __( 'Stubli app', 'stubli' ), 'href' => home_url( '/app/' ), ) ); } } /** * The web app manifest, for Add to Home Screen. */ private static function manifest() { $icons = STUBLI_PLUGIN_URL . 'assets/app/'; header( 'Content-Type: application/manifest+json; charset=utf-8' ); echo wp_json_encode( array( 'name' => 'Stubli', 'short_name' => 'Stubli', 'start_url' => home_url( '/app/' ), 'scope' => home_url( '/app/' ), 'display' => 'standalone', 'background_color' => '#ffffff', 'theme_color' => '#fbfaf8', 'icons' => array( array( 'src' => $icons . 'icon-192.png', 'sizes' => '192x192', 'type' => 'image/png', 'purpose' => 'any maskable', ), array( 'src' => $icons . 'icon-512.png', 'sizes' => '512x512', 'type' => 'image/png', 'purpose' => 'any maskable', ), ), ) ); exit; } /** * Data the app starts with. * * @return array */ private static function boot() { $user = wp_get_current_user(); $collections = array(); $default = (int) get_option( 'default_category' ); foreach ( get_categories( array( 'hide_empty' => false, 'orderby' => 'count', 'order' => 'DESC' ) ) as $term ) { // WordPress's untouched "Uncategorized" is no collection: posts there // show in All on the page (0.44.0). Renamed, it's a collection like any other. if ( $default === (int) $term->term_id && Posts::is_unsorted( $term ) ) { continue; } $collections[] = html_entity_decode( $term->name, ENT_QUOTES, 'UTF-8' ); } return array( 'rest' => esc_url_raw( rest_url( REST_NS . '/' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'version' => VERSION, 'demo' => Demo::on() ? Demo::words() : null, 'ajax' => admin_url( 'admin-ajax.php' ), 'home' => home_url( '/' ), 'journal' => home_url( '/journal/' ), 'name' => self::first_name( $user ), 'settings' => admin_url( 'options-general.php?page=stubli' ), 'dashboard' => admin_url(), 'collections' => array_values( array_unique( $collections ) ), 'noneOk' => Posts::is_unsorted( get_term( $default, 'category' ) ), 'collOrder' => array_column( Arrange::collections(), 'name' ), 'maxPhotos' => Posts::MAX_PHOTOS, 'maxCaption' => Posts::MAX_CAPTION_LENGTH, 'maxNote' => Posts::MAX_NOTE_LENGTH, 'friends' => count( (array) get_option( 'stubli_friends', array() ) ), 'requests' => Notes::waiting(), 'unread' => Notes::unread(), 'maxJournal' => 5000, 'today' => wp_date( 'l, j F' ), 'waiting' => self::waiting(), 'setupDone' => (bool) get_option( 'stubli_setup_done' ), 'firstDay' => self::first_day(), 'logo' => Seasonal::logo(), 'maxUpload' => (int) wp_max_upload_size(), 'videoTypes' => Posts::video_types_allowed(), 'noVideo' => Posts::no_video_message(), 'sw' => home_url( '/app/notifications' ), 'swScope' => home_url( '/app/' ), 'maxPost' => (int) wp_convert_hr_to_bytes( (string) ini_get( 'post_max_size' ) ), 'mode' => Setup::mode(), 'hour' => (int) wp_date( 'G' ), ); } /** * How many comments and guest book entries wait for an OK (not set aside). * * @return int */ private static function waiting() { $n = 0; foreach ( get_comments( array( 'status' => 'hold', 'number' => 100, 'fields' => 'ids' ) ) as $id ) { if ( ! get_comment_meta( $id, 'stubli_later', true ) ) { ++$n; } } return $n; } /** * A name to greet the owner with: their first name, or a display name * that looks like a real name (not a username like "celestinetan"). * * @param \WP_User $user User. * @return string */ private static function first_name( $user ) { if ( '' !== trim( (string) $user->first_name ) ) { return trim( (string) $user->first_name ); } $d = trim( (string) $user->display_name ); if ( '' === $d || strtolower( $d ) === strtolower( (string) $user->user_login ) || strtolower( $d ) === strtolower( (string) $user->user_nicename ) ) { // Fall back to the name on your page ("Ana Keller" → "Ana"). $d = trim( wp_specialchars_decode( (string) get_bloginfo( 'name' ), ENT_QUOTES ) ); if ( '' === $d || preg_match( '/^(your|my|the)\b/i', $d ) || preg_match( '/^(stubli|wordpress|my blog|site title)$/i', $d ) ) { return ''; } } if ( preg_match( '/\s/', $d ) ) { return (string) strtok( $d, ' ' ); } return preg_match( '/^\p{Lu}/u', $d ) ? $d : ''; } /** * Font faces from the Stubli theme, when it's active. * * @return string */ private static function fonts() { return function_exists( 'stubli_font_faces' ) ? stubli_font_faces() : ''; } /** * The app page. */ private static function render( $act = false ) { $assets = STUBLI_PLUGIN_URL . 'assets/app/'; // The app is its own page, outside the theme, but its files still go // through WordPress's script and style registry. wp_register_style( 'stubli-app', $assets . 'app.css', array(), VERSION ); wp_add_inline_style( 'stubli-app', wp_strip_all_tags( self::fonts() . Setup::accent_css() ) ); wp_register_script( 'stubli-app', $assets . 'app.js', array( 'wp-i18n' ), VERSION, true ); // The app's words in the site's language: from WordPress.org's language // packs, or from the plugin's own languages folder. wp_set_script_translations( 'stubli-app', 'stubli', STUBLI_PLUGIN_DIR . 'languages' ); wp_add_inline_script( 'stubli-app', 'window.STUBLI = ' . wp_json_encode( self::boot(), JSON_HEX_TAG | JSON_HEX_AMP ) . ';', 'before' ); status_header( 200 ); header( 'Content-Type: text/html; charset=utf-8' ); header( 'X-Robots-Tag: noindex, nofollow' ); ?>