add_rewrite_rules(); flush_rewrite_rules(); update_option( self::REWRITE_VERSION_OPTION, self::VERSION ); if ( false === get_option( self::OPTION_KEY ) ) { add_option( self::OPTION_KEY, self::default_settings() ); } } public static function deactivate() { flush_rewrite_rules(); delete_option( self::REWRITE_VERSION_OPTION ); } public function maybe_flush_rewrite_rules() { $installed_version = get_option( self::REWRITE_VERSION_OPTION, '' ); if ( self::VERSION === $installed_version ) { return; } flush_rewrite_rules( false ); update_option( self::REWRITE_VERSION_OPTION, self::VERSION ); } // ------------------------------------------------------------------------- // Rewrite Rules & Query Vars // ------------------------------------------------------------------------- public function add_rewrite_rules() { add_rewrite_rule( '^manifest\.json$', 'index.php?kgv_pwa=manifest', 'top' ); add_rewrite_rule( '^sw\.js$', 'index.php?kgv_pwa=sw', 'top' ); } public function add_query_vars( $vars ) { $vars[] = 'kgv_pwa'; return $vars; } // ------------------------------------------------------------------------- // Manifest Endpoint // ------------------------------------------------------------------------- public function handle_manifest() { if ( 'manifest' !== get_query_var( 'kgv_pwa' ) ) { return; } if ( ! is_user_logged_in() ) { status_header( 403 ); exit; } $settings = $this->get_settings(); $manifest = array( 'name' => $settings['app_name'], 'short_name' => $settings['short_name'], 'description' => $settings['description'], 'start_url' => home_url( '/' ), 'scope' => home_url( '/' ), 'display' => $settings['display'], 'background_color' => $settings['background_color'], 'theme_color' => $settings['theme_color'], 'lang' => str_replace( '_', '-', get_locale() ), 'icons' => $this->get_manifest_icons( $settings ), ); header( 'Content-Type: application/manifest+json; charset=utf-8' ); header( 'Cache-Control: max-age=3600' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo wp_json_encode( $manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); exit; } private function get_manifest_icons( $settings ) { $icons = array(); foreach ( array( '192', '512' ) as $size ) { $key = 'icon_' . $size; if ( ! empty( $settings[ $key ] ) ) { $icons[] = array( 'src' => esc_url_raw( $settings[ $key ] ), 'sizes' => $size . 'x' . $size, 'type' => 'image/png', 'purpose' => 'any maskable', ); } } // Fallback: WordPress-Site-Icon if ( empty( $icons ) ) { $site_icon_512 = get_site_icon_url( 512 ); $site_icon_192 = get_site_icon_url( 192 ); if ( $site_icon_512 ) { $icons[] = array( 'src' => $site_icon_512, 'sizes' => '512x512', 'type' => 'image/png', 'purpose' => 'any', ); } if ( $site_icon_192 ) { $icons[] = array( 'src' => $site_icon_192, 'sizes' => '192x192', 'type' => 'image/png', 'purpose' => 'any', ); } } return $icons; } // ------------------------------------------------------------------------- // Service Worker Endpoint // ------------------------------------------------------------------------- public function handle_service_worker() { if ( 'sw' !== get_query_var( 'kgv_pwa' ) ) { return; } if ( ! is_user_logged_in() ) { status_header( 403 ); exit; } $sw_file = plugin_dir_path( __FILE__ ) . 'assets/js/service-worker.js'; if ( ! file_exists( $sw_file ) ) { status_header( 404 ); exit; } header( 'Content-Type: application/javascript; charset=utf-8' ); header( 'Service-Worker-Allowed: /' ); header( 'Cache-Control: no-cache, no-store, must-revalidate' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents readfile( $sw_file ); exit; } // ------------------------------------------------------------------------- // Frontend: Head Tags & Script // ------------------------------------------------------------------------- public function output_head_tags() { if ( ! is_user_logged_in() ) { return; } $settings = $this->get_settings(); $app_name = esc_attr( $settings['app_name'] ); $theme_hex = esc_attr( $settings['theme_color'] ); $icon_url = ! empty( $settings['icon_192'] ) ? esc_url( $settings['icon_192'] ) : ''; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; if ( $icon_url ) { echo '' . "\n"; } } private function get_manifest_url() { // Query-URL funktioniert auch dann, wenn Pretty-Rewrite noch nicht aktiv ist. return home_url( '/?kgv_pwa=manifest' ); } private function get_service_worker_url() { // Query-URL funktioniert auch dann, wenn Pretty-Rewrite noch nicht aktiv ist. return home_url( '/?kgv_pwa=sw' ); } public function enqueue_assets() { if ( ! is_user_logged_in() ) { return; } $sw_url = $this->get_service_worker_url(); $scope = trailingslashit( home_url( '/' ) ); wp_enqueue_style( 'kgv-pwa', plugin_dir_url( __FILE__ ) . 'assets/css/kgv-pwa.css', array(), self::VERSION ); wp_enqueue_script( 'kgv-pwa-register', plugin_dir_url( __FILE__ ) . 'assets/js/pwa-register.js', array(), self::VERSION, true ); wp_localize_script( 'kgv-pwa-register', 'kgvPwaConfig', array( 'swUrl' => esc_url_raw( $sw_url ), 'scope' => esc_url_raw( $scope ), 'requireLogin' => true, 'isLoggedIn' => is_user_logged_in(), 'iosInstallNotice' => __( 'Auf iPhone/iPad: Im Browser auf Teilen tippen und dann "Zum Home-Bildschirm" waehlen.', 'kgv-pwa' ), ) ); } /** * Shortcode [kgv_pwa_install_button] – rendert einen Installations-Button. * Kann in Seiten, Posts und Widgets verwendet werden. * * Attribute: * label – Beschriftung des Buttons (Standard: "App installieren") * class – Zusätzliche CSS-Klassen */ public function render_install_button( $atts ) { if ( ! is_user_logged_in() ) { return ''; } $atts = shortcode_atts( array( 'label' => __( 'App installieren', 'kgv-pwa' ), 'class' => '', ), $atts, 'kgv_pwa_install_button' ); $classes = 'kgv-pwa-install-btn'; if ( ! empty( $atts['class'] ) ) { $classes .= ' ' . sanitize_html_class( $atts['class'] ); } return sprintf( '', esc_attr( $classes ), esc_html( $atts['label'] ) ); } // ------------------------------------------------------------------------- // Admin Settings // ------------------------------------------------------------------------- public function add_settings_page() { add_options_page( __( 'KGV PWA', 'kgv-pwa' ), __( 'KGV PWA', 'kgv-pwa' ), 'manage_options', 'kgv-pwa', array( $this, 'render_settings_page' ) ); } public function register_settings() { register_setting( 'kgv_pwa_settings_group', self::OPTION_KEY, array( $this, 'sanitize_settings' ) ); } public function sanitize_settings( $input ) { $defaults = self::default_settings(); $output = array(); $output['app_name'] = isset( $input['app_name'] ) ? sanitize_text_field( $input['app_name'] ) : $defaults['app_name']; $output['short_name'] = isset( $input['short_name'] ) ? sanitize_text_field( $input['short_name'] ) : $defaults['short_name']; $output['description'] = isset( $input['description'] ) ? sanitize_textarea_field( $input['description'] ) : ''; $output['theme_color'] = isset( $input['theme_color'] ) && preg_match( '/^#[0-9a-fA-F]{6}$/', $input['theme_color'] ) ? $input['theme_color'] : $defaults['theme_color']; $output['background_color'] = isset( $input['background_color'] ) && preg_match( '/^#[0-9a-fA-F]{6}$/', $input['background_color'] ) ? $input['background_color'] : $defaults['background_color']; $valid_display = array( 'standalone', 'fullscreen', 'minimal-ui', 'browser' ); $output['display'] = isset( $input['display'] ) && in_array( $input['display'], $valid_display, true ) ? $input['display'] : $defaults['display']; $output['icon_192'] = isset( $input['icon_192'] ) ? esc_url_raw( $input['icon_192'] ) : ''; $output['icon_512'] = isset( $input['icon_512'] ) ? esc_url_raw( $input['icon_512'] ) : ''; return $output; } private static function default_settings() { return array( 'app_name' => get_bloginfo( 'name' ), 'short_name' => get_bloginfo( 'name' ), 'description' => get_bloginfo( 'description' ), 'theme_color' => '#2e7d32', 'background_color' => '#ffffff', 'display' => 'standalone', 'icon_192' => '', 'icon_512' => '', ); } public function get_settings() { $settings = get_option( self::OPTION_KEY, array() ); if ( ! is_array( $settings ) ) { $settings = array(); } return wp_parse_args( $settings, self::default_settings() ); } public function render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $settings = $this->get_settings(); $option_prefix = esc_attr( self::OPTION_KEY ); $display_modes = array( 'standalone' => 'Standalone', 'fullscreen' => 'Fullscreen', 'minimal-ui' => 'Minimal UI', 'browser' => 'Browser', ); ?>

get_manifest_url() ); ?>  |  get_service_worker_url() ); ?>