Merge pull request 'Add WP-CLI support' (#6) from cli into main
Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://getcomposer.org/schema.json",
|
"$schema": "https://getcomposer.org/schema.json",
|
||||||
"name": "cleverogre/ogre-captcha",
|
"name": "cleverogre/ogre-captcha",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"title": "Ogre CAPTCHA",
|
"title": "Ogre CAPTCHA",
|
||||||
"description": "Protect your site with CAPTCHA",
|
"description": "Protect your site with CAPTCHA",
|
||||||
"author": "CleverOgre",
|
"author": "CleverOgre",
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @package ogre-captcha
|
||||||
|
* @author cleverogre
|
||||||
|
* @copyright 2026 CleverOgre
|
||||||
|
* @license GLP-3.0-or-later
|
||||||
|
* @version 1.2.1
|
||||||
|
* @since 1.2.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Ogre\Captcha\CLI;
|
||||||
|
|
||||||
|
use Ogre\Captcha as Plugin;
|
||||||
|
use Ogre\Captcha\GravityForms\Addon as GFAddon;
|
||||||
|
use Ogre\Captcha\Settings;
|
||||||
|
use WP_CLI;
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
final class CLI {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Cap API instance url.
|
||||||
|
*
|
||||||
|
* ## OPTIONS
|
||||||
|
*
|
||||||
|
* <value>
|
||||||
|
* : The url of the Cap instance.
|
||||||
|
*
|
||||||
|
* ## EXAMPLES
|
||||||
|
*
|
||||||
|
* wp ogre-captcha instance_url https://captcha.cleverogre.com
|
||||||
|
*
|
||||||
|
* @when after_wp_load
|
||||||
|
*/
|
||||||
|
public function instance_url(array $args, array $assoc_args):void {
|
||||||
|
list($value) = $args;
|
||||||
|
if (Settings::set_instance_url($value)) {
|
||||||
|
WP_CLI::success(Plugin::__('Successfully updated api instance url'));
|
||||||
|
} else {
|
||||||
|
WP_CLI::error(Plugin::__('Invalid url'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Cap API site key.
|
||||||
|
*
|
||||||
|
* ## OPTIONS
|
||||||
|
*
|
||||||
|
* <value>
|
||||||
|
* : The value of the site key. Typically 10 characters.
|
||||||
|
*
|
||||||
|
* ## EXAMPLES
|
||||||
|
*
|
||||||
|
* wp ogre-captcha site_key ##########
|
||||||
|
*
|
||||||
|
* @when after_wp_load
|
||||||
|
*/
|
||||||
|
public function site_key(array $args, array $assoc_args):void {
|
||||||
|
list($value) = $args;
|
||||||
|
if (Settings::set_site_key($value)) {
|
||||||
|
WP_CLI::success(Plugin::__('Successfully updated api site key'));
|
||||||
|
} else {
|
||||||
|
WP_CLI::error(Plugin::__('Invalid site key'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Cap API secret key.
|
||||||
|
*
|
||||||
|
* ## OPTIONS
|
||||||
|
*
|
||||||
|
* <value>
|
||||||
|
* : The value of the secret key. Typically 43 characters.
|
||||||
|
*
|
||||||
|
* ## EXAMPLES
|
||||||
|
*
|
||||||
|
* wp ogre-captcha secret_key sk-########################################
|
||||||
|
*
|
||||||
|
* @when after_wp_load
|
||||||
|
*/
|
||||||
|
public function secret_key(array $args, array $assoc_args):void {
|
||||||
|
list($value) = $args;
|
||||||
|
if (Settings::set_secret_key($value)) {
|
||||||
|
WP_CLI::success(Plugin::__('Successfully updated api secret key'));
|
||||||
|
} else {
|
||||||
|
WP_CLI::error(Plugin::__('Invalid secret key'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable (or disable) CAPTCHA on the designated forms.
|
||||||
|
*
|
||||||
|
* ## OPTIONS
|
||||||
|
*
|
||||||
|
* [<locations>...]
|
||||||
|
* : The form locations to alter. The following values are accepted. "login_page", "registration_page", "lost_password_page", "login_form", "comment_form", "post_password_form", "wc_login", "wc_lost_password", "wc_register", "wc_checkout"
|
||||||
|
*
|
||||||
|
* [--all]
|
||||||
|
* : Whether or not to include all possible form locations.
|
||||||
|
*
|
||||||
|
* [--disable]
|
||||||
|
* : Whether or not to disable the designated form locations rather than enable.
|
||||||
|
*
|
||||||
|
* ## EXAMPLES
|
||||||
|
*
|
||||||
|
* wp ogre-captcha form --all
|
||||||
|
*
|
||||||
|
* @when after_wp_load
|
||||||
|
*/
|
||||||
|
public function form(array $args, array $assoc_args):void {
|
||||||
|
list($locations) = $args;
|
||||||
|
extract($assoc_args);
|
||||||
|
|
||||||
|
if (empty($locations) && !$all) {
|
||||||
|
WP_CLI::error('Please specify one or more form locations, or use --all.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!$all) $locations = Settings::ALL_LOCATIONS;
|
||||||
|
|
||||||
|
$locations = array_filter($locations, fn ($location) => !Settings::set_form_location($location, !$disable));
|
||||||
|
|
||||||
|
if (empty($locations)) {
|
||||||
|
WP_CLI::success(Plugin::__('Successfully updated all form locations'));
|
||||||
|
} else {
|
||||||
|
WP_CLI::error(
|
||||||
|
Plugin::__('Failed to alter form locations: %s'),
|
||||||
|
implode(', ', $locations)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Cap widget type.
|
||||||
|
*
|
||||||
|
* ## OPTIONS
|
||||||
|
*
|
||||||
|
* <value>
|
||||||
|
* : The value of the widget type. Either "standard" or "floating".
|
||||||
|
*
|
||||||
|
* ## EXAMPLES
|
||||||
|
*
|
||||||
|
* wp ogre-captcha widget_type floating
|
||||||
|
*
|
||||||
|
* @when after_wp_load
|
||||||
|
*/
|
||||||
|
public function widget_type(array $args, array $assoc_args):void {
|
||||||
|
list($value) = $args;
|
||||||
|
if (Settings::set_widget_type($value)) {
|
||||||
|
WP_CLI::success(Plugin::__('Successfully updated widget type'));
|
||||||
|
} else {
|
||||||
|
WP_CLI::error(Plugin::__('Invalid widget type'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable CAPTCHA on Gravity Forms.
|
||||||
|
*
|
||||||
|
* ## OPTIONS
|
||||||
|
*
|
||||||
|
* <value>
|
||||||
|
* : Whether or not to enable CAPTCHA on all forms by default.
|
||||||
|
* ---
|
||||||
|
* options:
|
||||||
|
* - enable
|
||||||
|
* - disable
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* ## EXAMPLES
|
||||||
|
*
|
||||||
|
* wp ogre-captcha gravityforms enable
|
||||||
|
*
|
||||||
|
* @when gform_loaded
|
||||||
|
*/
|
||||||
|
public function gravityforms(array $args, array $assoc_args):void {
|
||||||
|
list($value) = $args;
|
||||||
|
$value = $value == 'enable';
|
||||||
|
|
||||||
|
if (!Plugin::is_gravityforms_active()) {
|
||||||
|
WP_CLI::error(Plugin::__('Gravity Forms not active'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GFAddon::get_instance()?->set_enabled($value)) {
|
||||||
|
WP_CLI::success(Plugin::__('Successfully updated Gravity Forms integration'));
|
||||||
|
} else {
|
||||||
|
WP_CLI::error(Plugin::__('Unable to update setting'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
WP_CLI::add_command(Plugin::get_id(), CLI::class);
|
||||||
+75
-4
@@ -4,7 +4,7 @@
|
|||||||
* @author cleverogre
|
* @author cleverogre
|
||||||
* @copyright 2026 CleverOgre
|
* @copyright 2026 CleverOgre
|
||||||
* @license GLP-3.0-or-later
|
* @license GLP-3.0-or-later
|
||||||
* @version 1.2.0
|
* @version 1.2.1
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -27,11 +27,38 @@ final class Settings {
|
|||||||
public const LOCATION_LOGIN_FORM = 'login_form';
|
public const LOCATION_LOGIN_FORM = 'login_form';
|
||||||
public const LOCATION_COMMENT_FORM = 'comment_form';
|
public const LOCATION_COMMENT_FORM = 'comment_form';
|
||||||
public const LOCATION_POST_PASSWORD_FORM = 'post_password_form';
|
public const LOCATION_POST_PASSWORD_FORM = 'post_password_form';
|
||||||
|
public const WP_LOCATIONS = [
|
||||||
|
self::LOCATION_LOGIN_PAGE,
|
||||||
|
self::LOCATION_REGISTRATION_PAGE,
|
||||||
|
self::LOCATION_LOST_PASSWORD_FORM,
|
||||||
|
self::LOCATION_LOGIN_FORM,
|
||||||
|
self::LOCATION_COMMENT_FORM,
|
||||||
|
self::LOCATION_POST_PASSWORD_FORM,
|
||||||
|
];
|
||||||
|
|
||||||
public const LOCATION_WC_LOGIN = 'wc_login';
|
public const LOCATION_WC_LOGIN = 'wc_login';
|
||||||
public const LOCATION_WC_LOST_PASSWORD = 'wc_lost_password';
|
public const LOCATION_WC_LOST_PASSWORD = 'wc_lost_password';
|
||||||
public const LOCATION_WC_REGISTER = 'wc_register';
|
public const LOCATION_WC_REGISTER = 'wc_register';
|
||||||
public const LOCATION_WC_CHECKOUT = 'wc_checkout';
|
public const LOCATION_WC_CHECKOUT = 'wc_checkout';
|
||||||
|
public const WC_LOCATIONS = [
|
||||||
|
self::LOCATION_WC_LOGIN,
|
||||||
|
self::LOCATION_WC_LOST_PASSWORD,
|
||||||
|
self::LOCATION_WC_REGISTER,
|
||||||
|
self::LOCATION_WC_CHECKOUT,
|
||||||
|
];
|
||||||
|
|
||||||
|
public const ALL_LOCATIONS = [
|
||||||
|
self::LOCATION_LOGIN_PAGE,
|
||||||
|
self::LOCATION_REGISTRATION_PAGE,
|
||||||
|
self::LOCATION_LOST_PASSWORD_FORM,
|
||||||
|
self::LOCATION_LOGIN_FORM,
|
||||||
|
self::LOCATION_COMMENT_FORM,
|
||||||
|
self::LOCATION_POST_PASSWORD_FORM,
|
||||||
|
self::LOCATION_WC_LOGIN,
|
||||||
|
self::LOCATION_WC_LOST_PASSWORD,
|
||||||
|
self::LOCATION_WC_REGISTER,
|
||||||
|
self::LOCATION_WC_CHECKOUT,
|
||||||
|
];
|
||||||
|
|
||||||
public const WIDGET_TYPE_STANDARD = 'standard';
|
public const WIDGET_TYPE_STANDARD = 'standard';
|
||||||
public const WIDGET_TYPE_FLOATING = 'floating';
|
public const WIDGET_TYPE_FLOATING = 'floating';
|
||||||
@@ -84,14 +111,30 @@ final class Settings {
|
|||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function set_instance_url(string $value):bool {
|
||||||
|
if (!wp_http_validate_url($value)) return false;
|
||||||
|
self::set('instance_url', $value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static function get_site_key():string {
|
public static function get_site_key():string {
|
||||||
return (string) self::get('site_key');
|
return (string) self::get('site_key');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function set_site_key(string $value):bool {
|
||||||
|
self::get('site_key');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static function get_secret_key():string {
|
public static function get_secret_key():string {
|
||||||
return (string) self::get('secret_key');
|
return (string) self::get('secret_key');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function set_secret_key(string $value):bool {
|
||||||
|
self::get('secret_key');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static function is_connected():bool {
|
public static function is_connected():bool {
|
||||||
return !empty(self::get_site_key()) && !empty(self::get_secret_key());
|
return !empty(self::get_site_key()) && !empty(self::get_secret_key());
|
||||||
}
|
}
|
||||||
@@ -111,6 +154,26 @@ final class Settings {
|
|||||||
return in_array($name, self::get_form_locations());
|
return in_array($name, self::get_form_locations());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function set_form_location(string $name, bool $enabled):bool {
|
||||||
|
if (in_array($name, self::WP_LOCATIONS)) {
|
||||||
|
$key = 'form_locations';
|
||||||
|
} else if (in_array($name, self::WC_LOCATIONS)) {
|
||||||
|
$key = 'wc_form_locations';
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = (array) self::get($key);
|
||||||
|
if (!!$enabled && !in_array($name, $value)) {
|
||||||
|
$value[] = $name;
|
||||||
|
} else if (!$enabled && in_array($name, $value)) {
|
||||||
|
unset($value[array_search($name, $value)]);
|
||||||
|
}
|
||||||
|
self::set($key, $value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static function is_login_page_enabled():bool {
|
public static function is_login_page_enabled():bool {
|
||||||
return self::is_form_location_enabled(self::LOCATION_LOGIN_PAGE);
|
return self::is_form_location_enabled(self::LOCATION_LOGIN_PAGE);
|
||||||
}
|
}
|
||||||
@@ -152,8 +215,9 @@ final class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function get_widget_type():string {
|
public static function get_widget_type():string {
|
||||||
$value = (array) self::get('widget_type');
|
$value = self::get('widget_type');
|
||||||
$value = !empty($value) ? array_values($value)[0] : '';
|
if (is_array($value) && !empty($value)) $value = array_values($value)[0];
|
||||||
|
if (!is_string($value)) $value = '';
|
||||||
return in_array($value, self::WIDGET_TYPES) ? $value : self::WIDGET_TYPE_STANDARD;
|
return in_array($value, self::WIDGET_TYPES) ? $value : self::WIDGET_TYPE_STANDARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +225,12 @@ final class Settings {
|
|||||||
return self::get_widget_type() === self::WIDGET_TYPE_FLOATING;
|
return self::get_widget_type() === self::WIDGET_TYPE_FLOATING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function set_widget_type(string $value):bool {
|
||||||
|
if (!in_array($value, self::WIDGET_TYPES)) return false;
|
||||||
|
self::set('widget_type', $value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function menu() {
|
public function menu() {
|
||||||
if (!current_user_can(self::CAPABILITY)) return;
|
if (!current_user_can(self::CAPABILITY)) return;
|
||||||
add_options_page(
|
add_options_page(
|
||||||
@@ -397,10 +467,11 @@ final class Settings {
|
|||||||
|
|
||||||
echo implode('<br>', array_map(
|
echo implode('<br>', array_map(
|
||||||
fn (string $name, string $label):string => sprintf(
|
fn (string $name, string $label):string => sprintf(
|
||||||
'<label><input type="%s" name="%s[%s][]" value="%s" %s> %s</label>',
|
'<label><input type="%s" name="%s[%s]%s" value="%s" %s> %s</label>',
|
||||||
esc_attr($args['type'] ?? 'checkbox'),
|
esc_attr($args['type'] ?? 'checkbox'),
|
||||||
esc_attr(Plugin::get_id()),
|
esc_attr(Plugin::get_id()),
|
||||||
esc_attr($args['name']),
|
esc_attr($args['name']),
|
||||||
|
(($args['type'] ?? 'checkbox') ? '[]' : ''),
|
||||||
esc_attr($name),
|
esc_attr($name),
|
||||||
checked(self::is_checked($args['name'], $name, $args['default'] ?? null), display: false),
|
checked(self::is_checked($args['name'], $name, $args['default'] ?? null), display: false),
|
||||||
esc_html($label)
|
esc_html($label)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author cleverogre
|
* @author cleverogre
|
||||||
* @copyright 2026 CleverOgre
|
* @copyright 2026 CleverOgre
|
||||||
* @license GLP-3.0-or-later
|
* @license GLP-3.0-or-later
|
||||||
* @version 1.2.0
|
* @version 1.2.1
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ final class Addon extends GFAddOn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function is_enabled(array|null $form = null): bool {
|
public function is_enabled(array|null $form = null): bool {
|
||||||
$plugin_enabled = (bool) $this->get_plugin_setting('enabled');
|
$plugin_enabled = $this->get_plugin_setting('enabled') === '1';
|
||||||
if (is_null($form) || !$plugin_enabled) return $plugin_enabled;
|
if (is_null($form) || !$plugin_enabled) return $plugin_enabled;
|
||||||
|
|
||||||
$form_settings = $this->get_form_settings($form);
|
$form_settings = $this->get_form_settings($form);
|
||||||
@@ -124,6 +124,13 @@ final class Addon extends GFAddOn {
|
|||||||
return !$form_disabled;
|
return !$form_disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function set_enabled(bool $value): bool {
|
||||||
|
$settings = (array) $this->get_plugin_settings();
|
||||||
|
$settings['enabled'] = !!$value ? '1' : '0';
|
||||||
|
$this->update_plugin_settings($settings);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function submit_button(string $button_input, array $form): string {
|
public function submit_button(string $button_input, array $form): string {
|
||||||
if (!Settings::is_connected() || !$this->is_enabled($form)) return $button_input;
|
if (!Settings::is_connected() || !$this->is_enabled($form)) return $button_input;
|
||||||
if ((is_admin() && !defined('DOING_AJAX')) || GFCommon::is_form_editor()) return $button_input;
|
if ((is_admin() && !defined('DOING_AJAX')) || GFCommon::is_form_editor()) return $button_input;
|
||||||
|
|||||||
+6
-2
@@ -6,14 +6,14 @@
|
|||||||
* @author cleverogre
|
* @author cleverogre
|
||||||
* @copyright 2026 CleverOgre
|
* @copyright 2026 CleverOgre
|
||||||
* @license GLP-3.0-or-later
|
* @license GLP-3.0-or-later
|
||||||
* @version 1.2.0
|
* @version 1.2.1
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*
|
*
|
||||||
* @wordpress-plugin
|
* @wordpress-plugin
|
||||||
* Plugin Name: Ogre CAPTCHA
|
* Plugin Name: Ogre CAPTCHA
|
||||||
* Plugin URI: https://git.cleverogre.com/cleverogre/ogre-captcha/
|
* Plugin URI: https://git.cleverogre.com/cleverogre/ogre-captcha/
|
||||||
* Description: Protect your site with CAPTCHA
|
* Description: Protect your site with CAPTCHA
|
||||||
* Version: 1.2.0
|
* Version: 1.2.1
|
||||||
* Requires at least: 6.0
|
* Requires at least: 6.0
|
||||||
* Requires PHP: 8.2
|
* Requires PHP: 8.2
|
||||||
* Author: CleverOgre
|
* Author: CleverOgre
|
||||||
@@ -66,6 +66,10 @@ final class Captcha extends Plugin {
|
|||||||
'inc/location/class-post-password-form.php',
|
'inc/location/class-post-password-form.php',
|
||||||
'inc/location/class-registration-page.php',
|
'inc/location/class-registration-page.php',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (class_exists('WP_CLI')) {
|
||||||
|
$this->add_file('inc/class-cli.php');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Override]
|
#[Override]
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://www.schemastore.org/package.json",
|
"$schema": "https://www.schemastore.org/package.json",
|
||||||
"name": "cleverogre/ogre-captcha",
|
"name": "cleverogre/ogre-captcha",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"title": "Ogre CAPTCHA",
|
"title": "Ogre CAPTCHA",
|
||||||
"description": "Protect your site with CAPTCHA",
|
"description": "Protect your site with CAPTCHA",
|
||||||
"author": "CleverOgre",
|
"author": "CleverOgre",
|
||||||
|
|||||||
+4
-1
@@ -3,7 +3,7 @@ Contributors: ogrecooper, cleverogre
|
|||||||
Tested up to: 7.0
|
Tested up to: 7.0
|
||||||
Requires at least: 6.0
|
Requires at least: 6.0
|
||||||
Requires PHP: 8.2
|
Requires PHP: 8.2
|
||||||
Version: 1.2.0
|
Version: 1.2.1
|
||||||
License: GPLv3 or later
|
License: GPLv3 or later
|
||||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
Copyright: CleverOgre
|
Copyright: CleverOgre
|
||||||
@@ -28,6 +28,9 @@ If you don't know what plugin you have downloaded, please contact [CleverOgre](t
|
|||||||
|
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.2.1 - 2026-06-09 =
|
||||||
|
* Added WP CLI commands to modify settings
|
||||||
|
|
||||||
= 1.2.0 - 2026-06-09 =
|
= 1.2.0 - 2026-06-09 =
|
||||||
* Add floating widget mode support
|
* Add floating widget mode support
|
||||||
* Add simple select all toggle for form locations settings
|
* Add simple select all toggle for form locations settings
|
||||||
|
|||||||
Reference in New Issue
Block a user