*/ public static function reasons(): array { return [ 'couldnt-start' => __('I couldn\'t see how to get started', 'sarcio'), 'not-what-i-need' => __('It didn\'t do what I needed', 'sarcio'), 'no-account' => __('I didn\'t want to create an account', 'sarcio'), 'too-expensive' => __('It costs too much', 'sarcio'), 'broke-something' => __('Something on my site broke', 'sarcio'), 'just-testing' => __('I was only trying it out', 'sarcio'), 'other' => __('Something else', 'sarcio'), ]; } /** @return array|null */ public static function payload(string $reason, string $details, bool $connected): ?array { if (!array_key_exists($reason, self::reasons())) { return null; } $details = trim(sanitize_textarea_field($details)); $payload = ['reason' => $reason]; if ($details !== '') { $payload['details'] = mb_substr($details, 0, self::DETAILS_MAX); } return $payload + [ 'pluginVersion' => SARCIO_WP_VERSION, 'wpVersion' => (string) get_bloginfo('version'), 'connected' => $connected, 'locale' => (string) get_locale(), ]; } /** @param array $payload */ public static function send(array $payload): void { wp_remote_post(Options::apiUrl() . '/api/plugin-feedback', [ 'blocking' => false, 'body' => (string) wp_json_encode($payload), 'headers' => ['Content-Type' => 'application/json'], 'redirection' => 0, 'timeout' => 3, ]); } public function register(): void { add_action('admin_enqueue_scripts', [$this, 'enqueue']); add_action('admin_footer-plugins.php', [$this, 'renderDialog']); add_action('rest_api_init', [$this, 'registerRoutes']); } public function registerRoutes(): void { register_rest_route(InboxRoutes::NAMESPACE, '/feedback/deactivate', [ 'args' => [ 'details' => ['type' => 'string'], 'reason' => ['enum' => array_keys(self::reasons()), 'required' => true, 'type' => 'string'], ], 'callback' => [$this, 'submit'], 'methods' => WP_REST_Server::CREATABLE, 'permission_callback' => static fn (): bool => current_user_can(self::CAPABILITY), ]); } public function submit(WP_REST_Request $request): WP_REST_Response { $payload = self::payload((string) $request->get_param('reason'), (string) $request->get_param('details'), Connect::connected()); if ($payload === null) { return new WP_REST_Response(['message' => __('Choose one of the answers.', 'sarcio')], 400); } self::send($payload); return new WP_REST_Response(null, 204); } public function enqueue(string $hook): void { if ($hook !== 'plugins.php' || !current_user_can(self::CAPABILITY)) { return; } wp_add_inline_style('common', self::asset('css/deactivate.css')); wp_register_script(self::SCRIPT, false, [], SARCIO_WP_VERSION, true); wp_enqueue_script(self::SCRIPT); wp_add_inline_script(self::SCRIPT, 'var sarcioDeactivate=' . InlineJson::encode([ 'nonce' => wp_create_nonce('wp_rest'), 'plugin' => $this->basename, 'url' => esc_url_raw(rest_url(InboxRoutes::NAMESPACE . '/feedback/deactivate')), ]) . ';', 'before'); wp_add_inline_script(self::SCRIPT, self::asset('js/deactivate.js')); } public function renderDialog(): void { if (!current_user_can(self::CAPABILITY)) { return; } echo ''; echo '
'; echo '

' . esc_html__('Before you go, what didn\'t work?', 'sarcio') . '

'; echo '
' . esc_html__('Before you go, what didn\'t work?', 'sarcio') . ''; foreach (self::reasons() as $key => $label) { printf( '

', esc_attr($key), esc_html($label), ); } echo '
'; printf( '

', esc_html__('Anything else? (optional)', 'sarcio'), (int) self::DETAILS_MAX, ); echo '

' . esc_html__('Sending this shares only your answer, the plugin and WordPress versions, the language of the site and whether it was connected. No address, name or email.', 'sarcio') . '

'; echo '
'; echo ''; echo ''; echo '
'; } /** The plugin's own bundled asset, printed inline so it works wherever the plugin is installed. */ private static function asset(string $file): string { $path = dirname(__DIR__, 2) . '/assets/' . $file; if (!is_readable($path)) { return ''; } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- the plugin's own bundled file. $source = file_get_contents($path); return is_string($source) ? $source : ''; } }