From 6a18d031d42ea5c08e408d7860d41016f2557022 Mon Sep 17 00:00:00 2001 From: Matthieu Benoist <matthieu.benoist@randstaddigital.fr> Date: Fri, 2 Feb 2024 11:49:08 +0100 Subject: [PATCH] repository update --- app/acfField/GLCaptchEtatField.class.php | 174 ++++++++++++ app/admin/CaptchEtatAdmin.class.php | 263 ++++++++++++++++++ app/forms/CaptchEtatForms.class.php | 188 +++++++++++++ app/index.php | 2 + app/service/CaptchEtatService.class.php | 225 +++++++++++++++ app/wpcPlugin.class.php | 212 ++++++++++++++ assets/scripts/gl-captchetat-form.js | 15 + .../jquery-capcha/jquery-captcha.min.js | 2 + .../jquery-capcha/jquery-captcha.min.js.map | 1 + assets/styles/acf.css | 9 + assets/styles/styles.css | 11 + composer.json | 13 + index.php | 2 + languages/wp-capchetat.pot | 0 wp-captchetat.php | 71 +++++ 15 files changed, 1188 insertions(+) create mode 100644 app/acfField/GLCaptchEtatField.class.php create mode 100644 app/admin/CaptchEtatAdmin.class.php create mode 100644 app/forms/CaptchEtatForms.class.php create mode 100644 app/index.php create mode 100644 app/service/CaptchEtatService.class.php create mode 100644 app/wpcPlugin.class.php create mode 100644 assets/scripts/gl-captchetat-form.js create mode 100644 assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js create mode 100644 assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js.map create mode 100644 assets/styles/acf.css create mode 100644 assets/styles/styles.css create mode 100644 composer.json create mode 100644 index.php create mode 100644 languages/wp-capchetat.pot create mode 100644 wp-captchetat.php diff --git a/app/acfField/GLCaptchEtatField.class.php b/app/acfField/GLCaptchEtatField.class.php new file mode 100644 index 0000000..021b14d --- /dev/null +++ b/app/acfField/GLCaptchEtatField.class.php @@ -0,0 +1,174 @@ +<?php + +namespace CaptchEtat\AcfField; + +use acf_field; +use CaptchEtat\Service\CaptchEtatService; + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + + +class GLCaptchEtatField extends acf_field +{ + protected $ceService; + protected $version = '1.0'; + protected $rand; + protected $key = ''; + + protected static $flag = false; + + protected static function __init(): void + { + add_action ('init', function() {acf_register_field_type(self::class); } ); + } + + public function __construct() + { + $this->rand = rand(); + $this->ceService = CaptchEtatService::register(); + + $this->name = 'CaptchETAT'; + $this->label = __('Validateur captcheta','GLCAPTCHETAT'); + $this->category = 'Custom'; + $this->defaults = []; + + add_filter('acf/load_field/type=CaptchETAT', function($field) { + $this->key = $field['key']; + $field['key'] = 'captchaFormulaireExtInput' . $this->rand; + $field['prefix'] = ''; + + return $field; + },200,1); + + add_filter('acf/prepare_field/type=CaptchETAT', function($field) { + + $field['key'] = $this->key; + $field['prefix'] = 'acf'; + + return $field; + },200,1); + + add_filter('acf/validate_save_post', array($this, 'validate_save_recaptcha_post'), 10, 0); + + parent::__construct(); + + } + + /** + * Settings to display when users configure a field of this type. + * + * These settings appear on the ACF “Edit Field Group” admin page when + * setting up the field. + * + * @param array $field + * @return void + */ + public function render_field_settings( $field ) + { + /* + * Repeat for each setting you wish to display for this field type. + */ + acf_render_field_setting( + $field, + array( + 'label' => $this->label, + 'instructions' => __( 'Adds a CaptchEtat field on your form','GLCAPTCHETAT' ), + 'name' => $this->name, + ) + ); + + // To render field settings on other tabs in ACF 6.0+: + // https://www.advancedcustomfields.com/resources/adding-custom-settings-fields/#moving-field-setting + } + + /** + * HTML content to show when a publisher edits the field on the edit screen. + * + * @param array $field The field settings and values. + * @return void + */ + public function render_field( $field ) + { + if (!is_admin()) { + if ($this->ceService->getShowCaptcha()) { + $html = '<div id="botdetect-captcha'.rand().'" data-captchastylename="captchaFR"></div> + <input class="captchetat-acf" id="acf-captchaFormulaireExtInput'.$this->rand.'" name="acf['.$field['key'].']" placeholder="Tapez le code lu ou entendu" type="text" required/> + <input id="captchaId'.$this->rand.'" name="captchaId" type="hidden" />'; + } else { + $html = + '<div>' + . __('Le service de captcha est indisponible.', 'acf-recaptcha'). + '</div>'; + } + } + else { + $html = '<div>' .__('Le service de sécurité est désactivé depuis l\'interface d\'administration.', 'acf-recaptcha').'</div>'; + } + + echo $html; + + } + + /** + * Enqueues CSS and JavaScript needed by HTML in the render_field() method. + * + * Callback for admin_enqueue_script. + * + * @return void + */ + public function input_admin_enqueue_scripts() + { + $url = WP_CAPTCHETAT_PLUGIN_URL . '/assets' ; + + wp_register_style( + 'WP-CAPTCHETAT', + "{$url}/styles/acf.css" + ); + wp_enqueue_style('WP-CAPTCHETAT'); + } + + + + + function validate_save_recaptcha_post() + { + session_start(); + // Determine if the form contains any 'captchetat' field types. + $form_contains_captcha = false; + $valid = false; + //if the service captchetat is not available the captcha is not shown so no validation needed + if (isset($_SESSION['captchETAT']) && $_SESSION['captchETAT'] == $_POST['BDC_VCID_captchaFR']) { + unset($_SESSION['captchETAT']); + return; + } + + foreach ($_POST['acf'] as $field_key => $value) { + $field = acf_get_field($field_key); + // var_dump($field); + if ($field['type'] === 'CaptchETAT') { + $form_contains_captcha = true; // we have a captcha + $valid = $this->ceService->validateCaptcha($value,$_POST['BDC_VCID_captchaFR']); + if ($valid == true) { + unset($_POST['acf']['$field_key']); + if (defined('DOING_AJAX') && DOING_AJAX) { + $_SESSION['captchETAT'] = $_POST['BDC_VCID_captchaFR']; + } + return; + } + } + } + + // Don't handle forms without the flag or any recaptcha fields. + if ( !$form_contains_captcha) { + return; + } + + + if (!$valid) { + acf_add_validation_error('', __('La valeur du captcha saisie est invalide ou expirée. Merci de rééssayer.', 'acf-recaptcha')); + } + return; + } +} diff --git a/app/admin/CaptchEtatAdmin.class.php b/app/admin/CaptchEtatAdmin.class.php new file mode 100644 index 0000000..70794ca --- /dev/null +++ b/app/admin/CaptchEtatAdmin.class.php @@ -0,0 +1,263 @@ +<?php + +namespace CaptchEtat\Admin; + +use CaptchEtat; +use CaptchEtat\WpcPlugin; +use WP_Error; + +class CaptchEtatAdmin +{ + /** + * CaptchEtatAdmin service + * + * @var CaptchEtatAdmin + */ + static private $_instance; + + private $wpcPlugin; + + /** + * @var string|array + */ + private $settings; + + /** + * Registering singleton + * + * @return CaptchEtatAdmin + */ + static public function register() + { + if (!isset(self::$_instance)) { + self::$_instance = new self(); + } + + return self::$_instance; + } + + + protected function __construct() + { + $this->wpcPlugin = WpcPlugin::register(); + $this->initSettingsActions(); + } + + + /** + * register glcapt_settings_init to the admin_init action hook + */ + private function initSettingsActions() + { + + add_action('admin_menu', function () { + $this->addOptionsPage(); + }); + + add_action('admin_init', function () { + $this->settingsAdminInit(); + }); + + } + + function settingsAdminInit() + { + if (!get_option('glcapt_settings')) { + add_option('glcapt_settings'); + } + // register a new setting for "glcapt" page + register_setting( + 'glcapt_option_group', + 'glcapt_settings' + ); + + // register a new section in the "glcapt" page + add_settings_section( + 'glcapt_settings_section', + 'Options pour les appels et l\'intégration de l\'API Captchetat', + function () { + $this->registerSettingsSection(); + }, + 'glcapt' + ); + + // register a new field in the "glcapt_settings_section" section, inside the "glcapt" page + add_settings_field( + 'glcapt_client_id', + 'Client id', + function () { + $this->setFieldHtmlClientId(); + }, + 'glcapt', + 'glcapt_settings_section' + ); + + add_settings_field( + 'glcapt_client_secret', + 'Client Secret', + function () { + $this->setFieldHtmlClientSecret(); + }, + 'glcapt', + 'glcapt_settings_section' + ); + + add_settings_field( + 'glcapt_api_url', + 'URL de l\'API', + function () { + $this->setFieldHtmlApiUrl('glcapt_api_url', 'glcapt_settings[glcapt_api_url]'); + }, + 'glcapt', + 'glcapt_settings_section' + ); + + add_settings_field( + 'glcapt_token_url', + 'URL de l\'API Oauth', + function () { + $this->setFieldHtmlApiUrl('glcapt_token_url', 'glcapt_settings[glcapt_token_url]'); + }, + 'glcapt', + 'glcapt_settings_section' + ); + + add_settings_section( + 'glcapt_forms_section', + 'Sélection des formulaires où activer le widget Captchetat', + function () { + $this->registerFormsSection(); + }, + 'glcapt' + ); + + add_settings_field( + 'glcapt_login_form', + 'formulaire de login', + function () { + $this->setFieldHtmlFormsCheckbox('glcapt_login_form','glcapt_settings[glcapt_forms][]'); + }, + 'glcapt', + 'glcapt_forms_section' + ); + + add_settings_field( + 'glcapt_comment_form', + 'formulaire de commentaire', + function () { + $this->setFieldHtmlFormsCheckbox('glcapt_comment_form','glcapt_settings[glcapt_forms][]'); + }, + 'glcapt', + 'glcapt_forms_section' + ); + + } + + /** + * callback functions + */ + + // section content cb + function registerSettingsSection() + { + echo '<p>Plugin pour intégrer la solution Captchetat du gouvernement français.</p>'; + } + + function registerFormsSection() + { + //todo + } + + // field content cb + function setFieldHtmlClientId() + { + // get the value of the setting we've registered with register_setting() + if (!empty($this->settings["glcapt_client_id"])) { + $setting = esc_attr($this->settings["glcapt_client_id"]); + } else { + $setting = ''; + } +?> + <input type="text" id="glcapt_client_id" name="glcapt_settings[glcapt_client_id]" value="<?php echo $setting; ?>"> + <?php + } + + // field content cb + function setFieldHtmlClientSecret() + { + // get the value of the setting we've registered with register_setting() + if (!empty($this->settings["glcapt_client_secret"])) { + $setting = esc_attr($this->settings["glcapt_client_secret"]); + } else { + $setting = ''; + } + ?> + <input type="text" id="glcapt_client_secret" name="glcapt_settings[glcapt_client_secret]" value="<?php echo $setting; ?>"> + <?php + } + + function setFieldHtmlApiUrl($idAttr, $nameAttr) { + if (!empty($this->settings[$idAttr])) { + $setting = sanitize_url($this->settings[$idAttr]); + } else { + $setting = ''; + } + ?> + <input type="text" id="<?php echo $idAttr; ?>" name="<?php echo $nameAttr; ?>" value="<?php echo $setting; ?>"> + <?php + } + + function setFieldHtmlFormsCheckbox($idAttr, $nameAttr) { + if (!empty($this->settings['glcapt_forms']) && in_array($idAttr,$this->settings['glcapt_forms'])) { + $checked = 'checked'; + + } else { + $checked = ''; + } + ?> + <input type="checkbox" id="<?php echo $idAttr; ?>" name="<?php echo $nameAttr; ?>" value="<?php echo $idAttr; ?>" <?php echo $checked; ?>> + <?php + } + + function generateOptionsPage() + { + // check user capabilities + if (!current_user_can('manage_options')) { + return; + } + + $this->settings = get_option('glcapt_settings'); + + ?> + <div class="wrap"> + <h1><?php echo esc_html(get_admin_page_title()); ?></h1> + <form action="options.php" method="post" enctype="multipart/form-data"> + <?php + // output security fields for the registered setting "glcapt_options" + settings_fields('glcapt_option_group'); + // output setting sections and their fields + // (sections are registered for "glcapt", each field is registered to a specific section) + do_settings_sections('glcapt'); + // output save settings button + submit_button(__('Enregister', 'textdomain')); + ?> + </form> + </div> +<?php + } + + function addOptionsPage() + { + add_submenu_page( + 'tools.php', + 'Captchetat plugin by métropole de Lyon', + 'Captchetat plugin', + 'manage_options', + 'glcapt', + function () { + $this->generateOptionsPage(); + } + ); + } + +} diff --git a/app/forms/CaptchEtatForms.class.php b/app/forms/CaptchEtatForms.class.php new file mode 100644 index 0000000..5b611ef --- /dev/null +++ b/app/forms/CaptchEtatForms.class.php @@ -0,0 +1,188 @@ +<?php + +namespace CaptchEtat\Forms; + +use CaptchEtat\Service\CaptchEtatService; +use CaptchEtat\WpcPlugin; +use WP_Error; +use WP_REST_Server; +use WP_User; + +class CaptchEtatForms +{ + /** + * CaptchEtatForms service + * + * @var CaptchEtatForms + */ + static private $_instance; + + private $wpcPlugin; + + private $wpcService; + + /** + * @var string|array + */ + private $settings; + + /** + * Registering singleton + * + * @return CaptchEtatForms + */ + static public function register(): CaptchEtatForms + { + if (!isset(self::$_instance)) { + self::$_instance = new self(); + } + + return self::$_instance; + } + + + protected function __construct() + { + $this->wpcPlugin = WpcPlugin::register(); + $this->wpcService = CaptchEtatService::register(); + $this->initFormsActions(); + } + + + /** + * register glcapt_settings_init to the admin_init action hook + */ + private function initFormsActions() + { + add_action('rest_api_init', function () { + $this->initGLCaptchetatPluginRestApi(); + }); + add_action( 'wp_footer', function () { + $this->addGLCaptchetaScripts(); + } ); + + if (!empty($this->wpcPlugin->getFormsWithCaptchetat()) && $this->wpcService->getShowCaptcha()) { + + if (in_array('glcapt_login_form', $this->wpcPlugin->getFormsWithCaptchetat())) { + $this->initCaptchaLoginForm(); + } + + if (in_array('glcapt_comment_form', $this->wpcPlugin->getFormsWithCaptchetat())) { + $this->initCaptchaCommentForm(); + } + } + } + + private function initCaptchaLoginForm() + { + add_action('login_form', function () { + $this->getGLCaptchetatHtml(); + }); + + add_action( 'login_footer', function () { + //si erreur de jsquery en page de login décommenter cette ligne + //wp_enqueue_script('jquery') ; + $this->addGLCaptchetaScripts(); + } ); + + add_filter('wp_authenticate_user', function ($user) { + return $this->validateLoginCaptcha($user); + }); + } + private function initCaptchaCommentForm() + { + add_action( 'comment_form_after_fields', function() { + $this->getGLCaptchetatHtml(); + }); + add_action( 'comment_form_logged_in_after', function() { + $this->getGLCaptchetatHtml(); + }); + add_action( 'pre_comment_on_post', function() { + $this->validateCommentCaptcha(); + }); + + } + + private function addGLCaptchetaScripts() + { + //captchetat library + wp_enqueue_script('captchetat_script', WP_CAPTCHETAT_PLUGIN_URL . 'assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js'); + //our js + wp_enqueue_script('captchetat_plugin_form_integration', WP_CAPTCHETAT_PLUGIN_URL . 'assets/scripts/gl-captchetat-form.js'); + + wp_enqueue_style('captchetat_style', WP_CAPTCHETAT_PLUGIN_URL . 'assets/styles/styles.css'); + wp_add_inline_script( + 'captchetat_plugin_form_integration', + 'const GLCAPTPARAMS = ' . + json_encode( + array( + 'backendUrl' => rest_url('glcapt/simple-captcha-endpoint') + ) + ), + 'before' + ); + } + + private function getGLCaptchetatHtml() + { + ?> + <div id="botdetect-captcha" data-captchastylename="captchaFR"></div> + <input aria-label="Code de vérification" id="captchaFormulaireExtInput" class = "captchetat-input" name="userEnteredCaptchaCode" type="text" required placeholder="Tapez le code lu ou entendu" /> + <input id="captchaId" name="captchaId" type="hidden" /> + <?php + } + + function validateLoginCaptcha( $user) + { + //only apply extra validation for the main login page + if ($GLOBALS['pagenow'] !== 'wp-login.php') { + return $user; + } + if (is_wp_error($user) && isset($user->errors['empty_username']) && isset($user->errors['empty_password'])) { + return $user; + } + if (empty($_POST['captchaId'])) { + return new WP_Error('captcha_librairy_error', __('Problème avec le système de validation captchetat')); + } elseif (empty($_POST['userEnteredCaptchaCode'])) { + return new WP_Error('captcha_empty', __('Merci de saisir le code de validation')); + } elseif (!$this->wpcService->validateCaptcha($_POST['userEnteredCaptchaCode'], $_POST['captchaId'])) { + return new WP_Error('captcha_invalide', __('Le captcha saisi est invalide')); + } + return $user; + } + + private function validateCommentCaptcha() + { + $error = new WP_ERROR; + if (empty($_POST['captchaId'])) { + $error->add('captcha_librairy_error', wp_kses_post(__('Problème avec le système de validation captchetat'))); + } elseif (empty($_POST['userEnteredCaptchaCode'])) { + $error->add('captcha_empty', wp_kses_post(__('Merci de saisir le code (captcha) de validation'))); + } elseif (!$this->wpcService->validateCaptcha($_POST['userEnteredCaptchaCode'], $_POST['captchaId'])) { + $error->add('captcha_invalide', wp_kses_post(__('Le captcha saisi est invalide'))); + } + if ($error->has_errors()) { + wp_die($error, "erreur de captcha", ["back_link" => true]); + } + } + + private function initGLCaptchetatPluginRestApi() + { + /* Custom endpoints for sessions */ + register_rest_route( + 'glcapt', + '/simple-captcha-endpoint', + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => function () { + $res = $this->wpcService->getCaptchaElement($_GET); + header('content-type:' . $res['content-type']); + echo $res['content']; + die(); + }, + 'permission_callback' => function() {return true;}, + + ] + ); + } +} diff --git a/app/index.php b/app/index.php new file mode 100644 index 0000000..3226752 --- /dev/null +++ b/app/index.php @@ -0,0 +1,2 @@ +<?php +// ssssh \ No newline at end of file diff --git a/app/service/CaptchEtatService.class.php b/app/service/CaptchEtatService.class.php new file mode 100644 index 0000000..926c3e4 --- /dev/null +++ b/app/service/CaptchEtatService.class.php @@ -0,0 +1,225 @@ +<?php + +namespace CaptchEtat\Service; + +use CaptchEtat\WpcPlugin; +use WP_Error; +use WP_Http_Curl; + +class CaptchEtatService +{ + /** + * CaptchEtatService service + * + * @var CaptchEtatService + */ + static private $_instance; + + private $wpcPlugin; + + private $wpCurl; + + /** + * Registering singleton + * + * @return CaptchEtatService + */ + static public function register() + { + if (!isset(self::$_instance)) { + self::$_instance = new self(); + } + + return self::$_instance; + } + + + protected function __construct() + { + $this->wpCurl = new WP_Http_Curl(); + $this->wpcPlugin = WpcPlugin::register(); + } + + /** + * URL Calls internal method + * + * @param string $url + * @param string $method + * @param array $options + * @return array + * @throws \Exception + */ + protected function _call($url, $method = 'GET', $options = []): array + { + + if ($method === 'GET') { + $response = wp_remote_get($url, $options); + } elseif ($method === 'POST') { + $response = wp_remote_post($url, $options); + } else { + throw new \Exception( + "only GET and POST methods are allowed", + 1 + ); + } + + if (is_wp_error($response, WP_Error::class)) { + throw new \Exception( + implode('\r ' , $response->get_error_messages()), + 1); + } + if ($response['response']['code'] === 200) { + return $response; + } else { + throw new \Exception( + 'exeption de _call, code : ' . $response['response']['code'] . ", message : " . $response['response']['message'], + $response['response']['code'] + ); + } + } + + /** + * Is CaptchEtat's API available ? + * + * @param string $token + * + * @return bool + */ + public function getShowCaptcha(): bool + { + $showCaptcha = false; + + try { + $additionalOptions = [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->getToken() + ], + ]; + + $this->_call($this->wpcPlugin->getApiURL() . '/healthcheck', 'GET', $additionalOptions); + $showCaptcha = true; + + } catch (\Exception $e) { + } + + return $showCaptcha; + } + + /** + * retrieve API Token + * + * @return string + */ + public function getToken(): string + { + + //first, verify if we have a token already registered in wp_options + $tokenData = $this->wpcPlugin->getTokenData(); + //test s'il en existe déjà un valable + if (!empty($tokenData) && time() < $tokenData['expire']) { + return $tokenData['access_token']; + } + + try { + $apiParams = [ + 'grant_type' => 'client_credentials', + 'client_id' => $this->wpcPlugin->getClientId(), + 'client_secret' => $this->wpcPlugin->getClientSecret(), + 'scope' => 'piste.captchetat' + ]; + + + $additionalOptions = [ + 'body' => $apiParams, + //'headers' =>['Content-Type' => 'application/x-www-form-urlencoded'] + ]; + + + $response = $this->_call($this->wpcPlugin->getTokenURL(), 'POST', $additionalOptions); + + if (strncmp($response['headers']['Content-Type'], 'application/json', strlen('application/json')) === 0) { + $return = json_decode($response['body']); + + if (isset($return->access_token)) { + $expiredDate = time() + $return->expires_in; + + $this->wpcPlugin + ->setTokenData(['access_token' => $return->access_token, 'expire' => $expiredDate]) + ->persist(); + + return $return->access_token; + } + } + throw new \Exception('Something gone wrong with the Token API response.', 1); + } catch (\Exception $e) { + // log, display, etc + throw new \Exception($e->getMessage(),$e->getCode()); + } + } + + /** + * Retrieve CaptchEtat HTML + * + * @param array $urlParams Get Params for the API + * + * @return array + */ + public function getCaptchaElement($urlParams = []): array + { + $additionalOptions = [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->getToken(), + ], + ]; + + + unset($urlParams['action']); + $urlParamsStr = http_build_query($urlParams); + try { + $response = $this->_call($this->wpcPlugin->getApiURL() . '/simple-captcha-endpoint?' . $urlParamsStr, 'GET', $additionalOptions); + + if (!empty($response)) { + return ['content-type' => $response['headers']['Content-Type'], 'content' => $response['body']]; + } + } catch (\Exception $e) { + // log, display, etc + return ["content" => "<div> erreur, code : " . $e->getCode() . " , message : " .$e->getMessage() . "</div>"]; + } + } + + /** + * Capcha validation + * + * @param string $input + * + * @param string $id $capcha id + * + * @return bool + * + */ + public function validateCaptcha($code, $id): bool + { + $return = false; + + if (!empty($code) && !empty($id)) { + $additionalOptions = [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->getToken(), + 'Content-Type' => 'application/json' + ], + 'body' => json_encode(['code' => $code, 'id' => $id]) + ]; + + try { + $response = $this->_call($this->wpcPlugin->getApiURL() . '/valider-captcha', 'POST', $additionalOptions); + + $return = $response['body'] == 'false' ? false : true; + } catch (\Exception $e) { + // log, display, etc + throw new \Exception('validate captcha error '. $e->getCode() . " : " . $e->getMessage(), $e->getCode()); + } + } + + return $return; + } +} diff --git a/app/wpcPlugin.class.php b/app/wpcPlugin.class.php new file mode 100644 index 0000000..c9c2ea1 --- /dev/null +++ b/app/wpcPlugin.class.php @@ -0,0 +1,212 @@ +<?php + +namespace CaptchEtat; + +if (!defined('ABSPATH')) { + exit; +} + +use CaptchEtat\Admin; +use CaptchEtat\Service; +use WP_Error; + +/** + * WordPress CaptchEtat Plugin Singleton class + * + */ +class WpcPlugin +{ + /** + * WpcPlugin singleton instance + * + * @var WpcPlugin + */ + static private $_instance; + + + /** + * Client ID + * + * @var string + */ + private $_clientId = null; + + /** + * Client Secret + * + * @var string + */ + private $_clientSecret = null; + + /** + * CaptchEtat API URL + * + * @var string + */ + private $_apiUrl = null; + + + /** + * CaptchEtat Token generation URL (?) + * + * @var string|null + */ + private $_tokenUrl = null; + + /** + * CaptchEtat Token data expire, access_token + * + * @var array|null + */ + private $_tokenData = null; + + /** + * @var string|array + */ + private $settings; + + /** + * Array of forms with CaptchEtat or not + * @var array[bool]|array[] + */ + private $_formsWithCaptchetat; + + + /** + * Registering singleton + * + * @return WpcPlugin + */ + static public function register() + { + if (!isset(self::$_instance)) { + self::$_instance = new self(); + } + + return self::$_instance; + } + + /** + * Private constructor + * + */ + protected function __construct() + { + $this->settings = get_option('glcapt_settings'); + $this->hydrateWSettings(); + } + + /** + * Get client ID + * + * @return string|null + */ + public function getClientId() + { + if ($this->_clientId === null) { + throw new \Exception("Missing configuration 'Client Id' in extension configuration"); + } + return $this->_clientId; + } + + /** + * Get client Secret + * + * @return string|null + */ + public function getClientSecret() + { + if ($this->_clientSecret === null) { + throw new \Exception("Missing configuration 'Client Secret' in extension configuration"); + } + return $this->_clientSecret; + } + + /** + * Get API URL + * + * @return string|null + */ + public function getApiURL() + { + if (!wp_http_validate_url($this->_apiUrl)) { + throw new \Exception('Bad or empty API URL : ' . $this->_apiUrl, 500); + } + return rtrim($this->_apiUrl, '/'); + } + + /** + * Get Token API URL + * + * @return string|null + */ + public function getTokenURL() + { + if (!wp_http_validate_url($this->_tokenUrl)) { + throw new \Exception('Bad or empty Token URL : ' . $this->_tokenUrl, 500); + } + return rtrim($this->_tokenUrl, '/'); + } + + /** + * Get Token + * + * @return string|null + */ + public function getTokenData() + { + return $this->_tokenData; + } + + /** + * Set Token data + * + * @return self + */ + public function setTokenData(array $tokenData) + { + $this->_tokenData = $tokenData; + $this->settings['glcapt_token_data'] = $tokenData; + + return $this; + } + + /** + * + */ + public function getFormsWithCaptchetat() { + return $this->_formsWithCaptchetat; + } + + public function hydrateWSettings() + { + if (!empty($this->settings["glcapt_client_id"])) { + $this->_clientId = esc_attr($this->settings["glcapt_client_id"]); + } + + if (!empty($this->settings["glcapt_client_secret"])) { + $this->_clientSecret = esc_attr($this->settings["glcapt_client_secret"]); + } + + if (!empty($this->settings["glcapt_api_url"])) { + $this->_apiUrl = esc_attr($this->settings["glcapt_api_url"]); + } + + if (!empty($this->settings["glcapt_token_url"])) { + $this->_tokenUrl = esc_attr($this->settings["glcapt_token_url"]); + } + + if(!empty($this->settings["glcapt_forms"]) && is_array($this->settings["glcapt_forms"])) { + $this->_formsWithCaptchetat = $this->settings["glcapt_forms"]; + } + + if(!empty($this->settings["glcapt_token_data"]) && is_array($this->settings["glcapt_token_data"])) { + $this->_tokenData = $this->settings["glcapt_token_data"]; + } + } + + public function persist() + { + update_option('glcapt_settings', $this->settings); + } +} diff --git a/assets/scripts/gl-captchetat-form.js b/assets/scripts/gl-captchetat-form.js new file mode 100644 index 0000000..9e1b4d0 --- /dev/null +++ b/assets/scripts/gl-captchetat-form.js @@ -0,0 +1,15 @@ +jQuery(document).ready(function () { + if (jQuery('div[id^="botdetect-captcha"').length > 0) { + var captcha = + jQuery('div[id^="botdetect-captcha"').captcha({ + captchaEndpoint: GLCAPTPARAMS.backendUrl + }); + var glCaptForm = jQuery('div[id^="botdetect-captcha"').closest('form'); + + jQuery(glCaptForm).submit(function (event) { + // Le code entré par l’utilisateur récupéré en backend + jQuery('input[id^="captchaId"').val(captcha.getCaptchaId()); + }); + } +}); + diff --git a/assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js b/assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js new file mode 100644 index 0000000..1159842 --- /dev/null +++ b/assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js @@ -0,0 +1,2 @@ +!function(a){"use strict";a.fn.captcha=function(b){function c(){return a.ajax({method:"GET",url:b.captchaEndpoint,data:{get:"html",c:p}})}function d(b,c){var d=j();a.ajax({method:"GET",url:d.validationUrl,data:{i:b},success:function(a){c(a)}})}function e(){var b=j();a("#"+b.options.userInputID).on("blur",function(){var c=a.trim(a(this).val());if(0!==c.length){var e=this;d(c,function(c){c||b.reloadImage(),a(e).trigger("validatecaptcha",[c])})}})}function f(b,c){return c=a.extend({dataType:"script",cache:!1,url:b},c||{}),a.ajax(c)}function g(){var c=a("#BDC_VCID_"+p).val();f(b.captchaEndpoint+"?get=script-include&c="+p+"&t="+c+"&cs=2").done(function(){setTimeout(i,200)})}function h(){var b=j();return void 0!==a("#"+b.options.userInputID).attr("data-correct-captcha")}function i(){h()&&e()}function j(){return void 0!==window.botdetect?window.botdetect.getInstanceByStyleName(p):null}function k(){var a=b.captchaEndpoint.split("/");return a[a.length-1]}function l(a){var c=b.captchaEndpoint.lastIndexOf(a);return b.captchaEndpoint.substring(0,c)}function m(a){var b=k(),c=l(b);a=a.replace(/<script.*<\/script>/g,"");for(var d,e,f,g=a.match(/(src|href)=\"([^"]+)\"/g),h=a,i=0;i<g.length;i++)d=g[i].slice(0,-1).replace(/src=\"|href=\"/,""),e=new RegExp(".*"+b),f=d.replace(e,c+b),h=h.replace(d,f);return h}function n(){c(b.captchaEndpoint,p).done(function(a){a=m(a),o.html(a),g()})}var o=this;if(0===o.length)throw new Error("Captcha html element cound not be found.");if(!b||!b.captchaEndpoint)throw new Error("The captchaEndpoint setting is required.");b.captchaEndpoint=b.captchaEndpoint.replace(/\/+$/g,"");var p=function(){var a;if(a=o.data("captchastylename"))return a;if(a=o.data("stylename"))return a;throw new Error("The data-captchastylename attribute is not found or its value is not set.")}();return o.init=function(){return n(),o},o.getCaptchaId=function(){return j().captchaId},o.getCaptchaCode=function(){return j().userInput.value},o.getUserEnteredCaptchaCode=function(){return o.getCaptchaCode()},o.reloadImage=function(){j().reloadImage()},o.validateUnsafe=function(b){var c=j();d(a.trim(a("#"+c.options.userInputID).val()),function(a){b(a),h()||a||c.reloadImage()})},o.init()}}(jQuery); +//# sourceMappingURL=jquery-captcha.min.js.map \ No newline at end of file diff --git a/assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js.map b/assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js.map new file mode 100644 index 0000000..b73f4a1 --- /dev/null +++ b/assets/scripts/vendor/jquery-capcha/jquery-captcha.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/jquery-captcha.js"],"names":["$","fn","captcha","settings","_getHtml","ajax","method","url","captchaEndpoint","data","get","c","captchaStyleName","_validateUnsafe","captchaCode","onSuccess","instance","_getInstance","validationUrl","i","success","isCorrect","_registerUserInputBlurValidation","options","userInputID","on","trim","this","val","length","reloadImage","trigger","_getScript","extend","dataType","cache","_loadScriptIncludes","captchaId","done","setTimeout","_onLoadScriptsSuccess","_useUserInputBlurValidation","undefined","attr","window","botdetect","getInstanceByStyleName","_getCaptchaEndpointHandler","splited","split","_getBackendBaseUrl","captchaEndpointHandler","lastIndex","lastIndexOf","substring","_changeRelativeToAbsoluteUrls","originCaptchaHtml","backendUrl","replace","relativeUrl","relativeUrlPrefixPattern","absoluteUrl","relativeUrls","match","changedCaptchaHtml","slice","RegExp","_displayHtml","captchaHtml","element","html","Error","styleName","init","getCaptchaId","getCaptchaCode","userInput","value","getUserEnteredCaptchaCode","validateUnsafe","callback","isHuman","jQuery"],"mappings":"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","file":"jquery-captcha.min.js","sourcesContent":["(function($) {\r\n 'use strict';\r\n \r\n $.fn.captcha = function(settings) {\r\n \r\n var element = this;\r\n \r\n if (element.length === 0) {\r\n throw new Error('Captcha html element cound not be found.');\r\n }\r\n\r\n if (!settings || !settings.captchaEndpoint) {\r\n throw new Error('The captchaEndpoint setting is required.');\r\n }\r\n \r\n // normalize captcha endpoint path\r\n settings.captchaEndpoint = settings.captchaEndpoint.replace(/\\/+$/g, '');\r\n \r\n var captchaStyleName = _getCaptchaStyleName();\r\n \r\n // get captcha style name\r\n function _getCaptchaStyleName() {\r\n var styleName;\r\n\r\n styleName = element.data('captchastylename');\r\n if (styleName) {\r\n return styleName;\r\n }\r\n\r\n // backward compatibility\r\n styleName = element.data('stylename');\r\n if (styleName) {\r\n return styleName;\r\n }\r\n\r\n throw new Error('The data-captchastylename attribute is not found or its value is not set.');\r\n };\r\n \r\n // get captcha html markup\r\n function _getHtml() {\r\n return $.ajax({\r\n method: 'GET',\r\n url: settings.captchaEndpoint,\r\n data: {\r\n get: 'html',\r\n c: captchaStyleName\r\n }\r\n });\r\n };\r\n\r\n // ajax validate captcha\r\n function _validateUnsafe(captchaCode, onSuccess) {\r\n var instance = _getInstance();\r\n\r\n $.ajax({\r\n method: 'GET',\r\n url: instance.validationUrl,\r\n data: {\r\n i: captchaCode\r\n },\r\n success: function (isCorrect) {\r\n onSuccess(isCorrect);\r\n }\r\n });\r\n };\r\n \r\n // ajax validate captcha on blur event and trigging the \r\n // custom 'validatecaptcha' event to fire the validation result\r\n function _registerUserInputBlurValidation() {\r\n var instance = _getInstance();\r\n\r\n $('#' + instance.options.userInputID).on('blur', function() {\r\n var captchaCode = $.trim($(this).val());\r\n if (captchaCode.length === 0) { return; }\r\n\r\n var userInputID = this;\r\n _validateUnsafe(captchaCode, function(isCorrect) {\r\n if (!isCorrect) {\r\n instance.reloadImage();\r\n }\r\n $(userInputID).trigger('validatecaptcha', [isCorrect]);\r\n });\r\n });\r\n };\r\n \r\n // a custom of $.getScript(), which lets changing the options\r\n function _getScript(url, options) {\r\n options = $.extend({\r\n dataType: 'script',\r\n cache: false,\r\n url: url\r\n }, options || {});\r\n return $.ajax(options);\r\n };\r\n \r\n // load botdetect scripts and execute them\r\n function _loadScriptIncludes() {\r\n var captchaId = $('#BDC_VCID_' + captchaStyleName).val();\r\n var scriptIncludeUrl = settings.captchaEndpoint + '?get=script-include&c=' + captchaStyleName + '&t=' + captchaId + '&cs=2';\r\n _getScript(scriptIncludeUrl).done(function() {\r\n setTimeout(_onLoadScriptsSuccess, 200);\r\n });\r\n };\r\n \r\n // use user input blur validation if the input element has data-correct-captcha attribute\r\n function _useUserInputBlurValidation() {\r\n var instance = _getInstance();\r\n return ($('#' + instance.options.userInputID).attr('data-correct-captcha') !== undefined);\r\n };\r\n \r\n // fire the custom event when botdetect scripts are loaded\r\n function _onLoadScriptsSuccess() {\r\n if (_useUserInputBlurValidation()) {\r\n _registerUserInputBlurValidation();\r\n }\r\n }\r\n \r\n // get botdetect captcha client-side instance\r\n function _getInstance() {\r\n var instance = null;\r\n if (typeof window.botdetect !== 'undefined') {\r\n return window.botdetect.getInstanceByStyleName(captchaStyleName);\r\n }\r\n return instance;\r\n };\r\n\r\n // get captcha endpoint handler from configued captchaEndpoint value,\r\n // the result can be \"simple-captcha-endpoint.ashx\", \"botdetectcaptcha\",\r\n // or \"simple-botdetect.php\"\r\n function _getCaptchaEndpointHandler() {\r\n var splited = settings.captchaEndpoint.split('/');\r\n return splited[splited.length - 1];\r\n };\r\n\r\n // get backend base url from configued captchaEndpoint value\r\n function _getBackendBaseUrl(captchaEndpointHandler) {\r\n var lastIndex = settings.captchaEndpoint.lastIndexOf(captchaEndpointHandler);\r\n return settings.captchaEndpoint.substring(0, lastIndex);\r\n };\r\n\r\n // change relative to absolute urls in captcha html markup\r\n function _changeRelativeToAbsoluteUrls(originCaptchaHtml) {\r\n var captchaEndpointHandler = _getCaptchaEndpointHandler();\r\n var backendUrl = _getBackendBaseUrl(captchaEndpointHandler);\r\n\r\n originCaptchaHtml = originCaptchaHtml.replace(/<script.*<\\/script>/g, '');\r\n var relativeUrls = originCaptchaHtml.match(/(src|href)=\\\"([^\"]+)\\\"/g);\r\n \r\n var relativeUrl, relativeUrlPrefixPattern, absoluteUrl,\r\n changedCaptchaHtml = originCaptchaHtml;\r\n\r\n for (var i = 0; i < relativeUrls.length; i++) {\r\n relativeUrl = relativeUrls[i].slice(0, -1).replace(/src=\\\"|href=\\\"/, '');\r\n relativeUrlPrefixPattern = new RegExp(\".*\" + captchaEndpointHandler);\r\n absoluteUrl = relativeUrl.replace(relativeUrlPrefixPattern, backendUrl + captchaEndpointHandler);\r\n changedCaptchaHtml = changedCaptchaHtml.replace(relativeUrl, absoluteUrl);\r\n }\r\n\r\n return changedCaptchaHtml;\r\n };\r\n \r\n // display captcha html markup in view\r\n function _displayHtml() {\r\n _getHtml(settings.captchaEndpoint, captchaStyleName).done(function(captchaHtml) {\r\n captchaHtml = _changeRelativeToAbsoluteUrls(captchaHtml) ;\r\n element.html(captchaHtml);\r\n _loadScriptIncludes();\r\n });\r\n }\r\n \r\n // plugin initialization - we display the captcha html markup in view\r\n element.init = function() {\r\n _displayHtml();\r\n return element;\r\n };\r\n \r\n // captcha id for validating captcha at server-side\r\n element.getCaptchaId = function() {\r\n var instance = _getInstance();\r\n return instance.captchaId;\r\n };\r\n\r\n // get the user entered captcha code\r\n // keep this function for backward compatibility\r\n element.getCaptchaCode = function() {\r\n var instance = _getInstance();\r\n return instance.userInput.value;\r\n };\r\n\r\n element.getUserEnteredCaptchaCode = function() {\r\n return element.getCaptchaCode();\r\n };\r\n \r\n // reload new captcha image\r\n element.reloadImage = function() {\r\n var instance = _getInstance();\r\n instance.reloadImage();\r\n };\r\n\r\n // validate captcha on client-side and execute user callback function on ajax success\r\n element.validateUnsafe = function(callback) {\r\n var instance = _getInstance();\r\n var captchaCode = $.trim($('#' + instance.options.userInputID).val());\r\n _validateUnsafe(captchaCode, function(isHuman) {\r\n callback(isHuman);\r\n if (!_useUserInputBlurValidation() && !isHuman) {\r\n instance.reloadImage();\r\n }\r\n });\r\n };\r\n\r\n return element.init();\r\n };\r\n \r\n}(jQuery));\r\n"]} \ No newline at end of file diff --git a/assets/styles/acf.css b/assets/styles/acf.css new file mode 100644 index 0000000..02f00ed --- /dev/null +++ b/assets/styles/acf.css @@ -0,0 +1,9 @@ +.acf-field input[type=text].captchetat-acf { + width:250px; + margin-top:1em; + text-transform: uppercase; +} + +input.captchetat-acf::placeholder { + opacity:0.8; +} \ No newline at end of file diff --git a/assets/styles/styles.css b/assets/styles/styles.css new file mode 100644 index 0000000..1996b10 --- /dev/null +++ b/assets/styles/styles.css @@ -0,0 +1,11 @@ +.captchetat-input { + width: 250px; + margin-top: 1rem; + outline: none; +} + +.captchetat-input::placeholder { + opacity: 0.8; + font-size: 1rem; + vertical-align: text-top; +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..01f77cf --- /dev/null +++ b/composer.json @@ -0,0 +1,13 @@ +{ + "name": "grandlyon/wp-captchetat", + "type": "wordpress-plugin", + "licence": "GPL-3.0+", + "keywords": [ + "wordpress", + "captcha", + "captchetat" + ], + "require": { + "php":">=5.6.0" + } +} diff --git a/index.php b/index.php new file mode 100644 index 0000000..0e76c7b --- /dev/null +++ b/index.php @@ -0,0 +1,2 @@ +<?php +/** nothing here */ \ No newline at end of file diff --git a/languages/wp-capchetat.pot b/languages/wp-capchetat.pot new file mode 100644 index 0000000..e69de29 diff --git a/wp-captchetat.php b/wp-captchetat.php new file mode 100644 index 0000000..313f698 --- /dev/null +++ b/wp-captchetat.php @@ -0,0 +1,71 @@ +<?php +/** + * CAPTCHETAT for WordPress by Metropole de Lyon + * + * @copyright Copyright (C) 2023 - Métropole de Lyon + * @license ? + * + * @wordpress-plugin + * Plugin Name: CAPTCHETAT for WordPress by Metropole de Lyon + * Version: 0.1 + * Description: Implementation of the CaptchEtat API for WordPress (see https://api.gouv.fr/les-api/api-captchetat) + * Author: Yohan RICCI, Matthieu BENOIST, AUSY + * Author URI: + * License: ? + * Requires at least: 5.2 + * Requires PHP: 7.0 + * + * WC requires at least: 5.2 + * WC tested up to: 6.2 + */ + + namespace CaptchEtat; + +use CaptchEtat\Admin\CaptchEtatAdmin; +use CaptchEtat\Forms\CaptchEtatForms; +use CaptchEtat\AcfField\GLCaptchEtatField; + + // definition + + if ( ! defined( 'ABSPATH' ) ) { + exit; +} + + +if (!defined('WP_CAPTCHETAT_VERSION')) { + define ('WP_CAPTCHETAT_VERSION', '0?1'); +} + +if (!defined('WP_CAPTCHETAT_PATH')) { + define ('WP_CAPTCHETAT_PATH', plugin_dir_path(__FILE__)); +} + +if (!defined('WP_CAPTCHETAT_PLUGIN_URL')) { + define ('WP_CAPTCHETAT_PLUGIN_URL', plugin_dir_url(__FILE__)); +} + +require WP_CAPTCHETAT_PATH . 'app/wpcPlugin.class.php'; +require WP_CAPTCHETAT_PATH . 'app/service/CaptchEtatService.class.php'; +require WP_CAPTCHETAT_PATH . 'app/admin/CaptchEtatAdmin.class.php'; +require WP_CAPTCHETAT_PATH . 'app/forms/CaptchEtatForms.class.php'; + + +if (function_exists('acf_register_field_type') ) { + require WP_CAPTCHETAT_PATH . 'app/acfField/GLCaptchEtatField.class.php'; + add_action('init', function(){ + acf_register_field_type(GLCaptchEtatField::class); + }); +} + +if (class_exists('WpcPlugin') || true) { + //$plugin = WpcPlugin::register(); + //admin page + $admin = CaptchEtatAdmin::register(); + //forms action for displayin and validating captchas with the capchetat library + $forms = CaptchEtatForms::register(); +} + + + + + -- GitLab