diff --git a/config/vufind/config.ini b/config/vufind/config.ini index 53e74537f5a16151b948e74c0a500d35057f86fb..a225928723159ad19b2befb5f3814c1e92b7a02c 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -1459,7 +1459,7 @@ treeSearchLimit = 100 ;secretKey = "https://www.google.com/recaptcha/admin/create" ; Valid theme values: dark, light ;theme = light -; Valid forms values: changePassword, email, feedback, newAccount, passwordRecovery, sms +; Valid forms values: changePassword, email, feedback, newAccount, passwordRecovery, sms, userComments ; Use * for all supported forms ;forms = changePassword, email, newAccount, passwordRecovery, sms diff --git a/module/VuFind/src/VuFind/Controller/AjaxController.php b/module/VuFind/src/VuFind/Controller/AjaxController.php index 6cfe77954e7c8f008257ec8b35e3564c377f55ef..98c7b7f50d4406259e076cbc6c1bb023b725b8b2 100644 --- a/module/VuFind/src/VuFind/Controller/AjaxController.php +++ b/module/VuFind/src/VuFind/Controller/AjaxController.php @@ -1086,6 +1086,16 @@ class AjaxController extends AbstractBase ); } + $useCaptcha = $this->recaptcha()->active('userComments'); + $this->recaptcha()->setErrorMode('none'); + if (!$this->formWasSubmitted('comment', $useCaptcha)) { + return $this->output( + $this->translate('recaptcha_not_passed'), + self::STATUS_ERROR, + 403 + ); + } + $table = $this->getTable('Resource'); $resource = $table->findResource( $id, $this->params()->fromPost('source', DEFAULT_SEARCH_BACKEND) diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Recaptcha.php b/module/VuFind/src/VuFind/Controller/Plugin/Recaptcha.php index 01474bdedadcee5ab1a58a7fce7f8457b0c726f7..bd3591b8fa3a7b4bc862cce43cd4bc79e48250f3 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/Recaptcha.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/Recaptcha.php @@ -90,7 +90,7 @@ class Recaptcha extends AbstractPlugin */ public function setErrorMode($mode) { - if (in_array($mode, ['flash', 'throw'])) { + if (in_array($mode, ['flash', 'throw', 'none'])) { $this->errorMode = $mode; return true; } @@ -125,7 +125,7 @@ class Recaptcha extends AbstractPlugin $response = false; } $captchaPassed = $response && $response->isValid(); - if (!$captchaPassed) { + if (!$captchaPassed && $this->errorMode != 'none') { if ($this->errorMode == 'flash') { $this->getController()->flashMessenger() ->addMessage('recaptcha_not_passed', 'error'); diff --git a/module/VuFind/src/VuFind/RecordTab/Factory.php b/module/VuFind/src/VuFind/RecordTab/Factory.php index 94fd9107b49e6d5a3503275527b08e3845110f84..82e6fa835cc4deae6d1a31ccc2f15ecf1680599d 100644 --- a/module/VuFind/src/VuFind/RecordTab/Factory.php +++ b/module/VuFind/src/VuFind/RecordTab/Factory.php @@ -262,6 +262,13 @@ class Factory public static function getUserComments(ServiceManager $sm) { $capabilities = $sm->getServiceLocator()->get('VuFind\AccountCapabilities'); - return new UserComments('enabled' === $capabilities->getCommentSetting()); + $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $useRecaptcha = isset($config->Captcha) && isset($config->Captcha->forms) + && (trim($config->Captcha->forms) === '*' + || strpos($config->Captcha->forms, 'userComments')); + return new UserComments( + 'enabled' === $capabilities->getCommentSetting(), + $useRecaptcha + ); } } diff --git a/module/VuFind/src/VuFind/RecordTab/UserComments.php b/module/VuFind/src/VuFind/RecordTab/UserComments.php index af5d219352d6f121e1741f7b0f937ebe6e64c9da..9bbede39072f0d4a43b49e81d51829a7c7873086 100644 --- a/module/VuFind/src/VuFind/RecordTab/UserComments.php +++ b/module/VuFind/src/VuFind/RecordTab/UserComments.php @@ -45,14 +45,33 @@ class UserComments extends AbstractBase */ protected $enabled; + /** + * Is this tab enabled? + * + * @var bool + */ + protected $useRecaptcha; + /** * Constructor * * @param bool $enabled is this tab enabled? + * @param bool $urc use recaptcha? */ - public function __construct($enabled = true) + public function __construct($enabled = true, $urc = false) { $this->enabled = $enabled; + $this->useRecaptcha = $urc; + } + + /** + * Is Recaptcha active? + * + * @return bool + */ + public function isRecaptchaActive() + { + return $this->useRecaptcha; } /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Recaptcha.php b/module/VuFind/src/VuFind/View/Helper/Root/Recaptcha.php index ee5e6848dad196b34ba23c0f509f4e2bae863a9e..b919aa7e877ffa57ae2869e998c28112d920e1dd 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Recaptcha.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Recaptcha.php @@ -93,14 +93,18 @@ class Recaptcha extends AbstractHelper * Generate <div> with ReCaptcha from render. * * @param boolean $useRecaptcha Boolean of active state, for compact templating + * @param boolean $wrapHtml Include prefix and suffix? * * @return string $html */ - public function html($useRecaptcha = true) + public function html($useRecaptcha = true, $wrapHtml = true) { if (!isset($useRecaptcha) || !$useRecaptcha) { return false; } + if (!$wrapHtml) { + return $this->recaptcha->getHtml(); + } return $this->prefixHtml . $this->recaptcha->getHtml() . $this->suffixHtml; } diff --git a/themes/bootstrap3/js/lightbox.js b/themes/bootstrap3/js/lightbox.js index c55eca08277bd0136228e485f4f79d3afdfe2ced..c09c436a436d4b0834337cf37bf593675c35f106 100644 --- a/themes/bootstrap3/js/lightbox.js +++ b/themes/bootstrap3/js/lightbox.js @@ -1,4 +1,4 @@ -/*global VuFind */ +/*global grecaptcha, VuFind */ VuFind.register('lightbox', function Lightbox() { // State var _originalUrl = false; @@ -101,6 +101,11 @@ VuFind.register('lightbox', function Lightbox() { $('#modal').find('.checkbox-select-item').change(function lbSelectAllDisable() { $(this).closest('.modal-body').find('.checkbox-select-all').prop('checked', false); }); + // Recaptcha + var modalCaptcha = $('#modal .g-recaptcha'); + if (modalCaptcha.length && typeof grecaptcha !== 'undefined' && modalCaptcha.is(':empty')) { + grecaptcha.render(modalCaptcha[0], modalCaptcha[0].dataset); + } } var _xhr = false; diff --git a/themes/bootstrap3/js/record.js b/themes/bootstrap3/js/record.js index 5dd139dc00e492b20134c6feb4ea080ef00c5efd..863968bc504eef6184135ee680cd6af5988c498e 100644 --- a/themes/bootstrap3/js/record.js +++ b/themes/bootstrap3/js/record.js @@ -1,4 +1,4 @@ -/*global deparam, syn_get_widget, userIsLoggedIn, VuFind */ +/*global deparam, grecaptcha, syn_get_widget, userIsLoggedIn, VuFind */ /*exported ajaxTagUpdate, recordDocReady */ /** @@ -78,6 +78,9 @@ function refreshCommentList($target, recordId, recordSource) { return false; }); $target.find('.comment-form input[type="submit"]').button('reset'); + if (typeof grecaptcha !== 'undefined') { + grecaptcha.reset(); + } }); } @@ -93,6 +96,14 @@ function registerAjaxCommentRecord() { id: id, source: recordSource }; + if (typeof grecaptcha !== 'undefined') { + try { + data['g-recaptcha-response'] = grecaptcha.getResponse(0); + } catch (e) { + console.error('Expected errors: placeholder element full and Invalid client ID'); + console.error(e); + } + } $.ajax({ type: 'POST', url: url, @@ -107,7 +118,7 @@ function registerAjaxCommentRecord() { }) .fail(function addCommentFail(response, textStatus) { if (textStatus === 'abort' || typeof response.responseJSON === 'undefined') { return; } - VuFind.lightbox.update(response.responseJSON.data); + VuFind.lightbox.alert(response.responseJSON.data, 'danger'); }); return false; }); diff --git a/themes/bootstrap3/templates/RecordTab/usercomments.phtml b/themes/bootstrap3/templates/RecordTab/usercomments.phtml index 2236461e4e7eaebee54926dfbea1574f04e56314..4bd7cb6e88b426ba9feab25c73d9265f456ed44f 100644 --- a/themes/bootstrap3/templates/RecordTab/usercomments.phtml +++ b/themes/bootstrap3/templates/RecordTab/usercomments.phtml @@ -6,7 +6,7 @@ <div class="comment-list"> <?=$this->render('record/comments-list.phtml')?> </div> -<form class="comment-form row" name="commentRecord" action="<?=$this->recordLink()->getActionUrl($this->driver, 'AddComment')?>" method="post"> +<form class="comment-form" name="commentRecord" action="<?=$this->recordLink()->getActionUrl($this->driver, 'AddComment')?>" method="post"> <div class="row"> <div class="col-sm-3 name"> <input type="hidden" name="id" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>"/> @@ -17,6 +17,9 @@ <? $user = $this->auth()->isLoggedIn() ?> <? if($user): ?> <textarea name="comment" class="form-control" rows="3" required></textarea><br/> + <? if ($this->tab->isRecaptchaActive()): ?> + <?=$this->recaptcha()->html(true, false) ?><br/> + <? endif; ?> <input class="btn btn-primary" data-loading-text="<?=$this->transEsc('Submitting') ?>..." type="submit" value="<?=$this->transEsc("Add your comment")?>"/> <? else: ?> <a href="<?=$this->url('myresearch-userlogin') ?>" class="btn btn-primary" data-lightbox title="Login"><i class="fa fa-sign-in" aria-hidden="true"></i> <?=$this->transEsc("You must be logged in first") ?></a>