*/ private const OTHER_TRACKERS = [ 'matomo/matomo.php', 'wp-piwik/wp-piwik.php' ]; /** * Render the screen. * * @internal Hooked callback. */ public function render(): void { if ( ! current_user_can( Menu::CAPABILITY ) ) { wp_die( esc_html__( 'شما اجازه دسترسی به این صفحه را ندارید.', 'rahkar-toolkit' ) ); } $result = $this->handleSubmission(); $title = __( 'اتصال به Matomo', 'rahkar-toolkit' ); Ui::pageOpen( $title, __( 'کد ردیاب Matomo را روی همه‌ی صفحه‌های فروشگاه می‌گذارد و محصول، سبد، جست‌وجو و سفارش را کامل به Matomo می‌دهد. تا نشانی و شناسه‌ی سایت را وارد نکنید، هیچ چیزی فرستاده نمی‌شود.', 'rahkar-toolkit' ), Help::link( MatomoModule::SLUG, $title ) ); if ( null !== $result ) { echo wp_kses( Ui::notice( $result['ok'] ? 'success' : 'error', $result['message'] ), Ui::allowedHtml() ); } $other = $this->otherTracker(); if ( '' !== $other ) { /* translators: %s: plugin file */ echo wp_kses( Ui::notice( 'warning', sprintf( __( 'افزونه‌ی %s هم ردیاب Matomo دارد. دو ردیاب یعنی هر بازدید دو بار شمرده می‌شود؛ یکی را خاموش کنید.', 'rahkar-toolkit' ), $other ) ), Ui::allowedHtml() ); } $site = Config::site(); if ( null !== $site ) { $this->renderStatus( $site ); } $this->renderForm( $site ); Ui::pageClose(); } /** * Where the tracker sends, and the last server-side send. * * @param array{url: string, site: int} $site Server and site. */ private function renderStatus( array $site ): void { $status = Config::status(); $token = null !== Config::token(); Ui::statsOpen(); echo wp_kses( Ui::stat( (string) $site['site'], __( 'شناسه‌ی سایت', 'rahkar-toolkit' ) ), Ui::allowedHtml() ); echo wp_kses( Ui::stat( $token ? __( 'روشن', 'rahkar-toolkit' ) : __( 'بدون توکن', 'rahkar-toolkit' ), __( 'ثبت سفارش از سرور', 'rahkar-toolkit' ) ), Ui::allowedHtml() ); echo wp_kses( Ui::stat( $status['at'] > 0 ? sprintf( /* translators: %s: human time difference */ __( '%s پیش', 'rahkar-toolkit' ), human_time_diff( $status['at'] ) ) : '—', __( 'آخرین ارسال از سرور', 'rahkar-toolkit' ), '', '' === $status['error'] ? '' : 'error' ), Ui::allowedHtml() ); echo wp_kses( Ui::stat( number_format_i18n( $status['sent'] ), __( 'سفارش‌های ثبت‌شده از سرور', 'rahkar-toolkit' ) ), Ui::allowedHtml() ); Ui::statsClose(); if ( '' !== $status['error'] ) { /* translators: 1: order id, 2: what went wrong */ echo wp_kses( Ui::notice( 'warning', sprintf( __( 'سفارش %1$s: %2$s', 'rahkar-toolkit' ), $status['order'], $status['error'] ), __( 'آخرین خطای ارسال از سرور', 'rahkar-toolkit' ) ), Ui::allowedHtml() ); } } /** * The settings form. * * @param array{url: string, site: int}|null $site Current server and site. */ private function renderForm( ?array $site ): void { Ui::panelOpen( __( 'سرور Matomo', 'rahkar-toolkit' ), __( 'کد ردیابی که Matomo در «تنظیم ردیابی» می‌دهد را بچسبانید، یا نشانی و شناسه‌ی سایت را خودتان بنویسید. خود کد ذخیره نمی‌شود؛ فقط همین دو.', 'rahkar-toolkit' ), 'chart' ); Ui::formOpen(); wp_nonce_field( self::NONCE ); echo ''; Ui::rowOpen( __( 'کد ردیابی', 'rahkar-toolkit' ), 'rahkar-matomo-code', __( 'اختیاری. اگر پر باشد، نشانی و شناسه از همین خوانده می‌شوند.', 'rahkar-toolkit' ) ); echo wp_kses( Ui::textarea( 'matomo_code', '', 6, [ 'id' => 'rahkar-matomo-code' ], true ), Ui::allowedHtml() ); Ui::rowClose(); Ui::rowOpen( __( 'نشانی سرور', 'rahkar-toolkit' ), 'rahkar-matomo-url', __( 'مثل https://matomo.example.ir — فقط HTTPS.', 'rahkar-toolkit' ) ); echo wp_kses( Ui::input( 'url', 'matomo_url', null === $site ? '' : $site['url'], [ 'id' => 'rahkar-matomo-url' ], true ), Ui::allowedHtml() ); Ui::rowClose(); Ui::rowOpen( __( 'شناسه‌ی سایت', 'rahkar-toolkit' ), 'rahkar-matomo-site' ); echo wp_kses( Ui::input( 'number', 'matomo_site', null === $site ? '' : (string) $site['site'], [ 'id' => 'rahkar-matomo-site', 'min' => '1' ], true ), Ui::allowedHtml() ); Ui::rowClose(); Ui::rowOpen( __( 'توکن ثبت از سرور', 'rahkar-toolkit' ), 'rahkar-matomo-token', __( 'اختیاری. توکنی با دسترسی «نوشتن» فقط روی همین سایت. با آن، سفارشی که روزها بعد تأیید شود سر جای خودش ثبت می‌شود و IP خریدار هم می‌رود. خالی بگذارید تا توکن فعلی بماند.', 'rahkar-toolkit' ) ); echo wp_kses( Ui::input( 'password', 'matomo_token', '', [ 'id' => 'rahkar-matomo-token', 'autocomplete' => 'new-password', 'placeholder' => null !== Config::token() ? __( 'ذخیره شده', 'rahkar-toolkit' ) : '', ], true ), Ui::allowedHtml() ); echo wp_kses( Ui::toggle( 'matomo_token_clear', false, [], __( 'پاک کردن توکن', 'rahkar-toolkit' ) ), Ui::allowedHtml() ); Ui::rowClose(); Ui::toolbarOpen( false ); echo wp_kses( Ui::button( __( 'ذخیره', 'rahkar-toolkit' ), 'primary', [ 'type' => 'submit' ], '', 'check' ), Ui::allowedHtml() ); Ui::toolbarClose(); Ui::formClose(); Ui::panelClose(); } /** * Save the server, site and token. * * @return array{ok: bool, message: string}|null */ private function handleSubmission(): ?array { if ( ! isset( $_POST['rahkar_matomo_action'] ) ) { return null; } check_admin_referer( self::NONCE ); // The pasted code is only read, never stored or printed; a script it holds is // exactly what must not be sanitised into something else before parsing. $code = isset( $_POST['matomo_code'] ) ? (string) wp_unslash( $_POST['matomo_code'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Read by Parse::snippet() with strict patterns and then discarded. $url = isset( $_POST['matomo_url'] ) ? esc_url_raw( wp_unslash( $_POST['matomo_url'] ) ) : ''; $site = isset( $_POST['matomo_site'] ) ? absint( wp_unslash( $_POST['matomo_site'] ) ) : 0; if ( '' !== trim( $code ) ) { $found = Parse::snippet( $code ); if ( null === $found ) { return [ 'ok' => false, 'message' => __( 'نشانی سرور و شناسه‌ی سایت در این کد پیدا نشد، یا نشانی HTTPS نیست.', 'rahkar-toolkit' ), ]; } $url = $found['url']; $site = $found['site']; } $server = Parse::serverUrl( $url ); if ( null === $server || $site < 1 ) { return [ 'ok' => false, 'message' => __( 'نشانی HTTPS سرور Matomo و شناسه‌ی سایت هر دو لازم‌اند.', 'rahkar-toolkit' ), ]; } if ( wp_parse_url( $server, PHP_URL_HOST ) === wp_parse_url( home_url(), PHP_URL_HOST ) ) { return [ 'ok' => false, 'message' => __( 'این ماژول برای Matomo روی سروری جداست. Matomo نصب‌شده داخل همین وردپرس افزونه‌ی ردیاب خودش را دارد.', 'rahkar-toolkit' ), ]; } Config::save( $server, $site ); $token = isset( $_POST['matomo_token'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['matomo_token'] ) ) ) : ''; if ( isset( $_POST['matomo_token_clear'] ) ) { Config::saveToken( '' ); } elseif ( '' !== $token ) { if ( 1 !== preg_match( '/^[A-Za-z0-9]{32,64}$/', $token ) ) { return [ 'ok' => false, 'message' => __( 'نشانی و شناسه ذخیره شدند، ولی توکن شکل توکن Matomo را ندارد و ذخیره نشد.', 'rahkar-toolkit' ), ]; } Config::saveToken( $token ); } return [ 'ok' => true, /* translators: 1: server address, 2: site id */ 'message' => sprintf( __( 'ذخیره شد: سایت %2$s روی %1$s. اگر ماژول روشن است، ردیاب از همین حالا روی صفحه‌هاست.', 'rahkar-toolkit' ), $server, $site ), ]; } /** * The file of another active Matomo plugin, or ''. */ private function otherTracker(): string { $active = get_option( 'active_plugins', [] ); foreach ( self::OTHER_TRACKERS as $file ) { if ( is_array( $active ) && in_array( $file, $active, true ) ) { return $file; } } return ''; } }