first commit
This commit is contained in:
@@ -0,0 +1,528 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup form Form
|
||||
* Implements a toolkit for the server-side implementation of forms, including
|
||||
* initializing forms with presets, reading submitted content, validating
|
||||
* content, and saving the results.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/form/Form.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Form
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @brief Class defining basic operations for handling HTML forms.
|
||||
*/
|
||||
|
||||
namespace PKP\form;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\notification\NotificationManager;
|
||||
use APP\template\TemplateManager;
|
||||
use PKP\core\PKPRequest;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\form\validation\FormValidator;
|
||||
use PKP\notification\PKPNotification;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\session\SessionManager;
|
||||
|
||||
class Form
|
||||
{
|
||||
/** @var string The template file containing the HTML form */
|
||||
public $_template;
|
||||
|
||||
/** @var array Associative array containing form data */
|
||||
public $_data;
|
||||
|
||||
/** @var array Validation checks for this form */
|
||||
public $_checks;
|
||||
|
||||
/** @var array Errors occurring in form validation */
|
||||
public $_errors;
|
||||
|
||||
/** @var array Array of field names where an error occurred and the associated error message */
|
||||
public $errorsArray;
|
||||
|
||||
/** @var array Array of field names where an error occurred */
|
||||
public $errorFields;
|
||||
|
||||
/** @var array Array of errors for the form section currently being processed */
|
||||
public $formSectionErrors;
|
||||
|
||||
/** @var array Client-side validation rules */
|
||||
public $cssValidation;
|
||||
|
||||
/** @var string Symbolic name of required locale */
|
||||
public $requiredLocale;
|
||||
|
||||
/** @var array Set of supported locales */
|
||||
public $supportedLocales;
|
||||
|
||||
/** @var string Default form locale */
|
||||
public $defaultLocale;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $template the path to the form template file
|
||||
* @param null|mixed $requiredLocale
|
||||
* @param null|mixed $supportedLocales
|
||||
*/
|
||||
public function __construct($template = null, $callHooks = true, $requiredLocale = null, $supportedLocales = null)
|
||||
{
|
||||
if ($requiredLocale === null) {
|
||||
$requiredLocale = Locale::getPrimaryLocale();
|
||||
}
|
||||
$this->requiredLocale = $requiredLocale;
|
||||
if ($supportedLocales === null) {
|
||||
$supportedLocales = Locale::getSupportedFormLocales();
|
||||
}
|
||||
$this->supportedLocales = $supportedLocales;
|
||||
|
||||
$this->defaultLocale = Locale::getLocale();
|
||||
|
||||
$this->_template = $template;
|
||||
$this->_data = [];
|
||||
$this->_checks = [];
|
||||
$this->_errors = [];
|
||||
$this->errorsArray = [];
|
||||
$this->errorFields = [];
|
||||
$this->formSectionErrors = [];
|
||||
|
||||
if ($callHooks === true) {
|
||||
// Call hooks based on the calling entity, assuming
|
||||
// this method is only called by a subclass. Results
|
||||
// in hook calls named e.g. "papergalleyform::Constructor"
|
||||
// Note that class names are always lower case.
|
||||
$classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
|
||||
Hook::call(strtolower_codesafe(end($classNameParts)) . '::Constructor', [$this, &$template]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Setters and Getters
|
||||
//
|
||||
/**
|
||||
* Set the template
|
||||
*
|
||||
* @param string $template
|
||||
*/
|
||||
public function setTemplate($template)
|
||||
{
|
||||
$this->_template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplate()
|
||||
{
|
||||
return $this->_template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required locale for this form (i.e. the locale for which
|
||||
* required fields must be set, all others being optional)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequiredLocale()
|
||||
{
|
||||
return $this->requiredLocale;
|
||||
}
|
||||
|
||||
//
|
||||
// Public Methods
|
||||
//
|
||||
/**
|
||||
* Display the form.
|
||||
*
|
||||
* @param PKPRequest $request
|
||||
* @param string $template the template to be rendered, mandatory
|
||||
* if no template has been specified on class instantiation.
|
||||
*/
|
||||
public function display($request = null, $template = null)
|
||||
{
|
||||
$this->fetch($request, $template, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of the rendered form
|
||||
*
|
||||
* @param PKPRequest $request
|
||||
* @param string $template the template to be rendered, mandatory
|
||||
* if no template has been specified on class instantiation.
|
||||
* @param bool $display
|
||||
*
|
||||
* @return string the rendered form
|
||||
*/
|
||||
public function fetch($request, $template = null, $display = false)
|
||||
{
|
||||
// Set custom template.
|
||||
if (!is_null($template)) {
|
||||
$this->_template = $template;
|
||||
}
|
||||
|
||||
// Call hooks based on the calling entity, assuming
|
||||
// this method is only called by a subclass. Results
|
||||
// in hook calls named e.g. "papergalleyform::display"
|
||||
// Note that class names are always lower case.
|
||||
$returner = null;
|
||||
$classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
|
||||
if (Hook::call(strtolower_codesafe(end($classNameParts)) . '::display', [$this, &$returner])) {
|
||||
return $returner;
|
||||
}
|
||||
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$templateMgr->setCacheability(TemplateManager::CACHEABILITY_NO_STORE);
|
||||
|
||||
$context = $request->getContext();
|
||||
|
||||
// Attach this form object to the Form Builder Vocabulary for validation to work
|
||||
$fbv = $templateMgr->getFBV();
|
||||
$fbv->setForm($this);
|
||||
|
||||
$templateMgr->assign(array_merge(
|
||||
$this->_data,
|
||||
[
|
||||
'isError' => !$this->isValid(),
|
||||
'errors' => $this->getErrorsArray(),
|
||||
'formLocales' => $this->supportedLocales,
|
||||
'formLocale' => $this->getDefaultFormLocale(),
|
||||
]
|
||||
));
|
||||
|
||||
if (!$templateMgr->getTemplateVars('primaryLocale')) {
|
||||
$templateMgr->assign([
|
||||
'primaryLocale' => $context
|
||||
? $context->getPrimaryLocale()
|
||||
: (Application::isInstalled() ? $request->getSite()->getPrimaryLocale() : null),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($display) {
|
||||
$templateMgr->display($this->_template);
|
||||
$returner = null;
|
||||
} else {
|
||||
$returner = $templateMgr->fetch($this->_template);
|
||||
}
|
||||
|
||||
// Reset the FBV's form in case template manager fetches another template not within a form.
|
||||
$fbv->setForm(null);
|
||||
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a form field.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData($key)
|
||||
{
|
||||
return $this->_data[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of one or several form fields.
|
||||
*
|
||||
* @param string|array $key If a string, then set a single field. If an associative array, then set many.
|
||||
* @param null|mixed $value
|
||||
*/
|
||||
public function setData($key, $value = null)
|
||||
{
|
||||
if (is_array($key)) {
|
||||
foreach ($key as $aKey => $aValue) {
|
||||
$this->setData($aKey, $aValue);
|
||||
}
|
||||
} else {
|
||||
$this->_data[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize form data for a new form.
|
||||
*/
|
||||
public function initData()
|
||||
{
|
||||
// Call hooks based on the calling entity, assuming
|
||||
// this method is only called by a subclass. Results
|
||||
// in hook calls named e.g. "papergalleyform::initData"
|
||||
// Note that class and function names are always lower
|
||||
// case.
|
||||
$classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
|
||||
Hook::call(strtolower_codesafe(end($classNameParts) . '::initData'), [$this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign form data to user-submitted data.
|
||||
* Can be overridden from subclasses.
|
||||
*/
|
||||
public function readInputData()
|
||||
{
|
||||
// Default implementation does nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate form data.
|
||||
*
|
||||
* @param bool $callHooks True (default) iff hooks are to be called.
|
||||
*/
|
||||
public function validate($callHooks = true)
|
||||
{
|
||||
if (!isset($this->errorsArray)) {
|
||||
$this->getErrorsArray();
|
||||
}
|
||||
|
||||
foreach ($this->_checks as $check) {
|
||||
if (!isset($this->errorsArray[$check->getField()]) && !$check->isValid()) {
|
||||
if (method_exists($check, 'getErrorFields') && method_exists($check, 'isArray') && call_user_func([&$check, 'isArray'])) {
|
||||
$errorFields = call_user_func([&$check, 'getErrorFields']);
|
||||
for ($i = 0, $count = count($errorFields); $i < $count; $i++) {
|
||||
$this->addError($errorFields[$i], $check->getMessage());
|
||||
$this->errorFields[$errorFields[$i]] = 1;
|
||||
}
|
||||
} else {
|
||||
$this->addError($check->getField(), $check->getMessage());
|
||||
$this->errorFields[$check->getField()] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($callHooks === true) {
|
||||
// Call hooks based on the calling entity, assuming
|
||||
// this method is only called by a subclass. Results
|
||||
// in hook calls named e.g. "papergalleyform::validate"
|
||||
// Note that class and function names are always lower
|
||||
// case.
|
||||
$value = null;
|
||||
$classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
|
||||
if (Hook::call(strtolower_codesafe(end($classNameParts) . '::validate'), [$this, &$value])) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!SessionManager::isDisabled()) {
|
||||
$request = Application::get()->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
if (!$this->isValid() && $user) {
|
||||
// Create a form error notification.
|
||||
$notificationManager = new NotificationManager();
|
||||
$notificationManager->createTrivialNotification(
|
||||
$user->getId(),
|
||||
PKPNotification::NOTIFICATION_TYPE_FORM_ERROR,
|
||||
['contents' => $this->getErrorsArray()]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the form's action.
|
||||
* (Note that it is assumed that the form has already been validated.)
|
||||
*
|
||||
* @return mixed Result from the consumer to be passed to the caller. Send a true-ish result if you want the caller to do something with the return value.
|
||||
*/
|
||||
public function execute(...$functionArgs)
|
||||
{
|
||||
// Call hooks based on the calling entity, assuming
|
||||
// this method is only called by a subclass. Results
|
||||
// in hook calls named e.g. "papergalleyform::execute"
|
||||
// Note that class and function names are always lower
|
||||
// case.
|
||||
$returner = null;
|
||||
$classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
|
||||
Hook::call(strtolower_codesafe(end($classNameParts) . '::execute'), array_merge([$this], $functionArgs, [&$returner]));
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of field names that need to support multiple locales
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocaleFieldNames()
|
||||
{
|
||||
// Call hooks based on the calling entity, assuming
|
||||
// this method is only called by a subclass. Results
|
||||
// in hook calls named e.g. "papergalleyform::getLocaleFieldNames"
|
||||
// Note that class and function names are always lower
|
||||
// case.
|
||||
$returner = [];
|
||||
$classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
|
||||
Hook::call(strtolower_codesafe(end($classNameParts) . '::getLocaleFieldNames'), [$this, &$returner]);
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default form locale.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultFormLocale()
|
||||
{
|
||||
$formLocale = $this->defaultLocale;
|
||||
if (!isset($this->supportedLocales[$formLocale])) {
|
||||
$formLocale = $this->requiredLocale;
|
||||
}
|
||||
return $formLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default form locale.
|
||||
*
|
||||
* @param string $defaultLocale
|
||||
*/
|
||||
public function setDefaultFormLocale($defaultLocale)
|
||||
{
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a supported locale.
|
||||
*
|
||||
* @param string $supportedLocale
|
||||
*/
|
||||
public function addSupportedFormLocale($supportedLocale)
|
||||
{
|
||||
if (!in_array($supportedLocale, $this->supportedLocales)) {
|
||||
$site = Application::get()->getRequest()->getSite();
|
||||
$siteSupportedLocales = $site->getSupportedLocaleNames();
|
||||
if (array_key_exists($supportedLocale, $siteSupportedLocales)) {
|
||||
$this->supportedLocales[$supportedLocale] = $siteSupportedLocales[$supportedLocale];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds specified user variables to input data.
|
||||
*
|
||||
* @param array $vars the names of the variables to read
|
||||
*/
|
||||
public function readUserVars($vars)
|
||||
{
|
||||
// Call hooks based on the calling entity, assuming
|
||||
// this method is only called by a subclass. Results
|
||||
// in hook calls named e.g. "papergalleyform::readUserVars"
|
||||
// Note that class and function names are always lower
|
||||
// case.
|
||||
$classNameParts = explode('\\', get_class($this)); // Separate namespace info from class name
|
||||
Hook::call(strtolower_codesafe(end($classNameParts) . '::readUserVars'), [$this, &$vars]);
|
||||
$request = Application::get()->getRequest();
|
||||
foreach ($vars as $k) {
|
||||
$this->setData($k, $request->getUserVar($k));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a validation check to the form.
|
||||
*
|
||||
* @param FormValidator $formValidator
|
||||
*/
|
||||
public function addCheck($formValidator)
|
||||
{
|
||||
$this->_checks[] = & $formValidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error to the form.
|
||||
* Errors are typically assigned as the form is validated.
|
||||
*
|
||||
* @param string $field the name of the field where the error occurred
|
||||
*/
|
||||
public function addError($field, $message)
|
||||
{
|
||||
$this->_errors[] = new FormError($field, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error field for highlighting on form
|
||||
*
|
||||
* @param string $field the name of the field where the error occurred
|
||||
*/
|
||||
public function addErrorField($field)
|
||||
{
|
||||
$this->errorFields[$field] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if form passes all validation checks.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return empty($this->_errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return set of errors that occurred in form validation.
|
||||
* If multiple errors occurred processing a single field, only the first error is included.
|
||||
*
|
||||
* @return array erroneous fields and associated error messages
|
||||
*/
|
||||
public function getErrorsArray()
|
||||
{
|
||||
$this->errorsArray = [];
|
||||
foreach ($this->_errors as $error) {
|
||||
if (!isset($this->errorsArray[$error->getField()])) {
|
||||
$this->errorsArray[$error->getField()] = $error->getMessage();
|
||||
}
|
||||
}
|
||||
return $this->errorsArray;
|
||||
}
|
||||
|
||||
//
|
||||
// Private helper methods
|
||||
//
|
||||
/**
|
||||
* Convert PHP variable (literals or arrays) into HTML containing
|
||||
* hidden input fields.
|
||||
*
|
||||
* @param string $name Name of variable
|
||||
* @param mixed $value Value of variable
|
||||
* @param array $stack Names of array keys (for recursive calling)
|
||||
*
|
||||
* @return string HTML hidden form elements describing the parameters.
|
||||
*/
|
||||
public function _decomposeArray($name, $value, $stack)
|
||||
{
|
||||
$returner = '';
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $key => $subValue) {
|
||||
$newStack = $stack;
|
||||
$newStack[] = $key;
|
||||
$returner .= $this->_decomposeArray($name, $subValue, $newStack);
|
||||
}
|
||||
} else {
|
||||
$name = htmlentities($name, ENT_COMPAT);
|
||||
$value = htmlentities($value, ENT_COMPAT);
|
||||
$returner .= '<input type="hidden" name="' . $name;
|
||||
while (($item = array_shift($stack)) !== null) {
|
||||
$item = htmlentities($item, ENT_COMPAT);
|
||||
$returner .= '[' . $item . ']';
|
||||
}
|
||||
$returner .= '" value="' . $value . "\" />\n";
|
||||
}
|
||||
return $returner;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\Form', '\Form');
|
||||
}
|
||||
@@ -0,0 +1,989 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup FormBuilderVocabulary Form Builder Vocabulary
|
||||
* Implements a form construction toolkit for generating standard form markup.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/form/FormBuilderVocabulary.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormBuilderVocabulary
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @brief Class defining Form Builder Vocabulary methods.
|
||||
*
|
||||
* Form Builder Vocabulary - FBV
|
||||
* Generates form markup in templates using {fbvX} calls.
|
||||
* Group form areas with the {fbvFormArea} call. These sections mark off groups of semantically
|
||||
* related form sections.
|
||||
* Parameters:
|
||||
* id: The form area ID
|
||||
* class (optional): Any additional classes
|
||||
* title (optional): Title of the area
|
||||
* Group form sections with the {fbvFormSection} call. These sections organize directly related form elements.
|
||||
* Parameters:
|
||||
* id: The section ID
|
||||
* class (optional): Any additional classes
|
||||
* title (optional): Title of the area
|
||||
* Form submit/cancel buttons should be created with {fbvFormButtons}
|
||||
* Parameters:
|
||||
* submitText (optional): Text to display for the submit link (default is 'Ok')
|
||||
* submitDisabled (optional): Whether the submit button should be disabled
|
||||
* confirmSubmit (optional): Text to display in a confirmation dialog that must be okayed
|
||||
* before the form is submitted
|
||||
* cancelText (optional): Text to display for the cancel link (default is 'Cancel')
|
||||
* hideCancel (optional): Whether the submit button should be disabled
|
||||
* cancelAction (optional): A LinkAction object to execute when cancel is clicked
|
||||
* cancelUrl (optional): URL to redirect to when cancel is clicked
|
||||
* Form elements are created with {fbvElement type="type"} plus any additional parameters.
|
||||
* Each specific element type may have other additional attributes (see their method comments)
|
||||
* Parameters:
|
||||
* type: The form element type (one of the cases in the smartyFBVElement method)
|
||||
* id: The element ID
|
||||
* class (optional): Any additional classes
|
||||
* required (optional) whether the section should have a 'required' label (adds span.required)
|
||||
* for (optional): What the section's label is for
|
||||
* inline: Adds .inline to the element's parent container and causes it to display inline with other elements
|
||||
* size: One of $fbvStyles.size.SMALL (adds .quarter to element's parent container) or $fbvStyles.size.MEDIUM (adds
|
||||
* .half to element's parentcontainer)
|
||||
* required: Adds an asterisk and a .required class to the element's label
|
||||
*/
|
||||
|
||||
namespace PKP\form;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FormBuilderVocabulary
|
||||
{
|
||||
/** @var Form associated with this object, if any. Will inform smarty which forms to label as required */
|
||||
public $_form;
|
||||
|
||||
/** @var array Styles organized by parameter name */
|
||||
public $_fbvStyles;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param object $form Form associated with this object
|
||||
*/
|
||||
public function __construct($form = null)
|
||||
{
|
||||
$this->_fbvStyles = [
|
||||
'size' => ['SMALL' => 'SMALL', 'MEDIUM' => 'MEDIUM', 'LARGE' => 'LARGE'],
|
||||
'height' => ['SHORT' => 'SHORT', 'MEDIUM' => 'MEDIUM', 'TALL' => 'TALL']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Setters and Getters
|
||||
//
|
||||
/**
|
||||
* Set the form
|
||||
*
|
||||
* @param object $form
|
||||
*/
|
||||
public function setForm($form)
|
||||
{
|
||||
if ($form) {
|
||||
assert($form instanceof \PKP\form\Form);
|
||||
}
|
||||
$this->_form = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
return $this->_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form style constants
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStyles()
|
||||
{
|
||||
return $this->_fbvStyles;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public Methods
|
||||
//
|
||||
/**
|
||||
* A form area that contains form sections.
|
||||
*
|
||||
* @param array $params
|
||||
* @param string $content
|
||||
* @param object $smarty
|
||||
* @param bool $repeat
|
||||
*/
|
||||
public function smartyFBVFormArea($params, $content, $smarty, &$repeat)
|
||||
{
|
||||
assert(isset($params['id']));
|
||||
if (!$repeat) {
|
||||
$smarty->assign([
|
||||
'FBV_class' => $params['class'] ?? null,
|
||||
'FBV_id' => $params['id'],
|
||||
'FBV_content' => $content ?? null,
|
||||
'FBV_translate' => $params['translate'] ?? true,
|
||||
'FBV_title' => $params['title'] ?? null,
|
||||
]);
|
||||
return $smarty->fetch('form/formArea.tpl');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* A form section that contains controls in a variety of layout possibilities.
|
||||
*
|
||||
* @param array $params
|
||||
* @param string $content
|
||||
* @param object $smarty
|
||||
* @param bool $repeat
|
||||
*/
|
||||
public function smartyFBVFormSection($params, $content, $smarty, &$repeat)
|
||||
{
|
||||
$form = $this->getForm();
|
||||
if (!$repeat) {
|
||||
$smarty->assign('FBV_required', $params['required'] ?? false);
|
||||
$smarty->assign('FBV_id', $params['id'] ?? null);
|
||||
|
||||
// Since $content will contain input fields that may have unique Ids appended, the 'for'
|
||||
// attribute on the form section's label needs to include this. Look for the assigned
|
||||
// form element within $content and extract the full id. Default to the passed in param
|
||||
// otherwise.
|
||||
if (!empty($params['for'])) {
|
||||
if (preg_match('/id="(' . preg_quote($params['for'], '/') . '\-[^"]+)"/', $content, $matches)) {
|
||||
$smarty->assign('FBV_labelFor', $matches[1]);
|
||||
} else {
|
||||
$smarty->assign('FBV_labelFor', $params['for']);
|
||||
}
|
||||
} else {
|
||||
$smarty->assign('FBV_labelFor', null);
|
||||
}
|
||||
$smarty->assign([
|
||||
'FBV_title' => $params['title'] ?? null,
|
||||
'FBV_label' => $params['label'] ?? null,
|
||||
'FBV_layoutInfo' => $this->_getLayoutInfo($params),
|
||||
'FBV_description' => $params['description'] ?? null,
|
||||
'FBV_content' => $content ?? null,
|
||||
'FBV_translate' => $params['translate'] ?? true,
|
||||
]);
|
||||
|
||||
$class = $params['class'] ?? null;
|
||||
|
||||
// Check if we are using the Form class and if there are any errors
|
||||
if (isset($form) && !empty($form->formSectionErrors)) {
|
||||
$class = $class . (empty($class) ? '' : ' ') . 'error';
|
||||
$smarty->assign('FBV_sectionErrors', $form->formSectionErrors);
|
||||
$form->formSectionErrors = [];
|
||||
} else {
|
||||
$smarty->assign('FBV_sectionErrors', null);
|
||||
}
|
||||
|
||||
// If we are displaying checkboxes or radio options, we'll need to use a
|
||||
// list to organize our elements -- Otherwise we use divs and spans
|
||||
if (isset($params['list']) && $params['list'] != false) {
|
||||
$smarty->assign('FBV_listSection', true);
|
||||
} else {
|
||||
// Double check that we don't have lists in the content.
|
||||
// This is a kludge but the only way to make sure we've
|
||||
// set the list parameter when we're using lists
|
||||
if (substr(trim($content), 0, 4) == '<li>') {
|
||||
throw new Exception('FBV: list attribute not set on form section containing lists');
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_listSection', false);
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_class', $class);
|
||||
$smarty->assign('FBV_layoutColumns', empty($params['layout']) ? false : true);
|
||||
|
||||
return $smarty->fetch('form/formSection.tpl');
|
||||
} else {
|
||||
if (isset($form)) {
|
||||
$form->formSectionErrors = [];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit and (optional) cancel button for a form.
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function smartyFBVFormButtons($params, $smarty)
|
||||
{
|
||||
$smarty->assign([
|
||||
'FBV_submitText' => $params['submitText'] ?? 'common.ok',
|
||||
'FBV_submitDisabled' => isset($params['submitDisabled']) ? (bool)$params['submitDisabled'] : false,
|
||||
'FBV_confirmSubmit' => $params['confirmSubmit'] ?? null,
|
||||
'FBV_cancelText' => $params['cancelText'] ?? 'common.cancel',
|
||||
'FBV_hideCancel' => isset($params['hideCancel']) ? (bool)$params['hideCancel'] : false,
|
||||
'FBV_cancelAction' => $params['cancelAction'] ?? null,
|
||||
'FBV_cancelUrl' => $params['cancelUrl'] ?? null,
|
||||
'FBV_cancelUrlTarget' => $params['cancelUrlTarget'] ?? '',
|
||||
'FBV_translate' => $params['translate'] ?? true,
|
||||
'FBV_saveText' => $params['saveText'] ?? null,
|
||||
]);
|
||||
return $smarty->fetch('form/formButtons.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Base form element.
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
* @param null|mixed $content
|
||||
*/
|
||||
public function smartyFBVElement($params, $smarty, $content = null)
|
||||
{
|
||||
if (!isset($params['type'])) {
|
||||
throw new Exception('FBV: Element type not set');
|
||||
}
|
||||
if (!isset($params['id'])) {
|
||||
throw new Exception('FBV: Element ID not set');
|
||||
}
|
||||
|
||||
// Set up the element template
|
||||
$smarty->assign([
|
||||
'FBV_id' => $params['id'],
|
||||
'FBV_class' => $params['class'] ?? null,
|
||||
'FBV_required' => $params['required'] ?? false,
|
||||
'FBV_layoutInfo' => $this->_getLayoutInfo($params),
|
||||
'FBV_label' => $params['label'] ?? null,
|
||||
'FBV_for' => $params['for'] ?? null,
|
||||
'FBV_tabIndex' => $params['tabIndex'] ?? null,
|
||||
'FBV_translate' => $params['translate'] ?? true,
|
||||
'FBV_keepLabelHtml' => $params['keepLabelHtml'] ?? false,
|
||||
]);
|
||||
|
||||
// Unset these parameters so they don't get assigned twice
|
||||
unset($params['class']);
|
||||
|
||||
// Find fields that the form class has marked as required and add the 'required' class to them
|
||||
$params = $this->_addClientSideValidation($params);
|
||||
$smarty->assign('FBV_validation', $params['validation'] ?? null);
|
||||
|
||||
// Set up the specific field's template
|
||||
switch (strtolower_codesafe($params['type'])) {
|
||||
case 'autocomplete':
|
||||
$content = $this->_smartyFBVAutocompleteInput($params, $smarty);
|
||||
break;
|
||||
case 'button':
|
||||
case 'submit':
|
||||
$content = $this->_smartyFBVButton($params, $smarty);
|
||||
break;
|
||||
case 'checkbox':
|
||||
$content = $this->_smartyFBVCheckbox($params, $smarty);
|
||||
unset($params['label']);
|
||||
break;
|
||||
case 'checkboxgroup':
|
||||
$content = $this->_smartyFBVCheckboxGroup($params, $smarty);
|
||||
unset($params['label']);
|
||||
break;
|
||||
case 'file':
|
||||
$content = $this->_smartyFBVFileInput($params, $smarty);
|
||||
break;
|
||||
case 'hidden':
|
||||
$content = $this->_smartyFBVHiddenInput($params, $smarty);
|
||||
break;
|
||||
case 'keyword':
|
||||
$content = $this->_smartyFBVKeywordInput($params, $smarty);
|
||||
break;
|
||||
case 'interests':
|
||||
$content = $this->_smartyFBVInterestsInput($params, $smarty);
|
||||
break;
|
||||
case 'radio':
|
||||
$content = $this->_smartyFBVRadioButton($params, $smarty);
|
||||
unset($params['label']);
|
||||
break;
|
||||
case 'email':
|
||||
case 'search':
|
||||
case 'tel':
|
||||
case 'text':
|
||||
case 'url':
|
||||
$content = $this->_smartyFBVTextInput($params, $smarty);
|
||||
break;
|
||||
case 'select':
|
||||
$content = $this->_smartyFBVSelect($params, $smarty);
|
||||
break;
|
||||
case 'textarea':
|
||||
$content = $this->_smartyFBVTextArea($params, $smarty);
|
||||
break;
|
||||
default: assert(false);
|
||||
}
|
||||
|
||||
unset($params['type']);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
//
|
||||
// Private methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Form button.
|
||||
* parameters: label (or value), disabled (optional), type (optional)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVButton($params, $smarty)
|
||||
{
|
||||
// the type of this button. the default value is 'button' (but could be 'submit')
|
||||
|
||||
$buttonParams = '';
|
||||
$smarty->assign(['FBV_label' => null, 'FBV_disabled' => false]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'inline':
|
||||
case 'translate':
|
||||
break;
|
||||
case 'label':
|
||||
case 'type':
|
||||
case 'disabled':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
default: $buttonParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_buttonParams', $buttonParams);
|
||||
|
||||
return $smarty->fetch('form/button.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form Autocomplete text input. (actually two inputs, label and value)
|
||||
* parameters: disabled (optional), name (optional - assigned value of 'id' by default)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVAutocompleteInput($params, $smarty)
|
||||
{
|
||||
assert(isset($params['autocompleteUrl']) && isset($params['id']));
|
||||
|
||||
// This id will be used for the hidden input that should be read by the Form.
|
||||
$autocompleteId = $params['id'];
|
||||
|
||||
// We then override the id parameter to differentiate it from the hidden element
|
||||
// and make sure that the text input is not read by the Form class.
|
||||
$params['id'] = $autocompleteId . '_input';
|
||||
|
||||
// We set this now, so that we unset the param for the text input.
|
||||
$smarty->assign([
|
||||
'FBV_autocompleteUrl' => $params['autocompleteUrl'],
|
||||
'FBV_autocompleteValue' => $params['autocompleteValue'] ?? null,
|
||||
'FBV_disableSync' => isset($params['disableSync']) ? true : null,
|
||||
]);
|
||||
|
||||
unset($params['autocompleteUrl'], $params['autocompleteValue']);
|
||||
|
||||
$smarty->assign([
|
||||
'FBV_textInput' => $this->_smartyFBVTextInput($params, $smarty),
|
||||
'FBV_id' => $autocompleteId,
|
||||
]);
|
||||
|
||||
return $smarty->fetch('form/autocompleteInput.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form text input.
|
||||
* parameters: disabled (optional), name (optional - assigned value of 'id' by default), multilingual (optional)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVTextInput($params, $smarty)
|
||||
{
|
||||
$params['name'] = $params['name'] ?? $params['id'];
|
||||
$params['subLabelTranslate'] = isset($params['subLabelTranslate']) ? (bool) $params['subLabelTranslate'] : true;
|
||||
$params['uniqId'] = uniqid();
|
||||
|
||||
$textInputParams = '';
|
||||
$smarty->assign([
|
||||
'FBV_isPassword' => isset($params['password']) ? true : false,
|
||||
'FBV_isTypeURL' => $params['type'] === 'url' ? true : false,
|
||||
'FBV_isTypeSearch' => $params['type'] === 'search' ? true : false,
|
||||
'FBV_isTypeEmail' => $params['type'] === 'email' ? true : false,
|
||||
'FBV_isTypeTel' => $params['type'] === 'tel' ? true : false,
|
||||
'FBV_disabled' => false,
|
||||
'FBV_readonly' => false,
|
||||
'FBV_multilingual' => false,
|
||||
'FBV_name' => null,
|
||||
'FBV_value' => null,
|
||||
'FBV_label_content' => null,
|
||||
'FBV_uniqId' => null,
|
||||
'FBV_urlValidationErrorMessage' => null,
|
||||
]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'label':
|
||||
$smarty->assign('FBV_label_content', $this->_smartyFBVSubLabel($params, $smarty));
|
||||
break;
|
||||
case 'type':
|
||||
case 'size':
|
||||
case 'inline':
|
||||
case 'subLabelTranslate':
|
||||
break;
|
||||
case 'urlValidationErrorMsg':
|
||||
if ($params['type'] === 'url') {
|
||||
$smarty->assign('FBV_urlValidationErrorMessage', __($value));
|
||||
}
|
||||
break;
|
||||
case 'placeholder':
|
||||
$textInputParams .= 'placeholder="' . htmlspecialchars(__($value), ENT_QUOTES) . '" ';
|
||||
break;
|
||||
case 'disabled':
|
||||
case 'readonly':
|
||||
case 'multilingual':
|
||||
case 'name':
|
||||
case 'id':
|
||||
case 'value':
|
||||
case 'uniqId':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'required':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'validation':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
// no break
|
||||
default:
|
||||
$textInputParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_textInputParams', $textInputParams);
|
||||
|
||||
return $smarty->fetch('form/textInput.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form text area.
|
||||
* parameters:
|
||||
* - value: string for single language inputs, array (xx_YY => language_value) for multilingual
|
||||
* - name: string (optional - assigned value based on ID by default)
|
||||
* - disabled: boolean (default false)
|
||||
* - multilingual: boolean (default false)
|
||||
* - rich: false (default), true, or 'extended'
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVTextArea($params, $smarty)
|
||||
{
|
||||
$params['name'] = $params['name'] ?? $params['id'];
|
||||
$params['rows'] = $params['rows'] ?? 10;
|
||||
$params['cols'] = $params['cols'] ?? 80;
|
||||
$params['subLabelTranslate'] = isset($params['subLabelTranslate']) ? (bool) $params['subLabelTranslate'] : true;
|
||||
$params['uniqId'] = uniqid();
|
||||
|
||||
$textAreaParams = '';
|
||||
$smarty->assign([
|
||||
'FBV_label_content' => null,
|
||||
'FBV_disabled' => false,
|
||||
'FBV_readonly' => false,
|
||||
'FBV_multilingual' => false,
|
||||
'FBV_value' => null,
|
||||
'FBV_height' => null,
|
||||
'FBV_rich' => false,
|
||||
'FBV_variables' => null,
|
||||
'FBV_variablesType' => null,
|
||||
]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'name':
|
||||
case 'value':
|
||||
case 'rows':
|
||||
case 'cols':
|
||||
case 'rich':
|
||||
case 'disabled':
|
||||
case 'readonly':
|
||||
case 'multilingual':
|
||||
case 'uniqId':
|
||||
case 'variablesType':
|
||||
case 'variables':
|
||||
case 'required':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'label': $smarty->assign('FBV_label_content', $this->_smartyFBVSubLabel($params, $smarty));
|
||||
break;
|
||||
case 'type': break;
|
||||
case 'size': break;
|
||||
case 'inline': break;
|
||||
case 'subLabelTranslate': break;
|
||||
case 'height':
|
||||
$styles = $this->getStyles();
|
||||
switch ($value) {
|
||||
case $styles['height']['SHORT']: $smarty->assign('FBV_height', 'short');
|
||||
break;
|
||||
case $styles['height']['MEDIUM']: $smarty->assign('FBV_height', 'medium');
|
||||
break;
|
||||
case $styles['height']['TALL']: $smarty->assign('FBV_height', 'tall');
|
||||
break;
|
||||
default:
|
||||
throw new Exception('FBV: invalid height specified for textarea.');
|
||||
}
|
||||
break;
|
||||
case 'id': break; // if we don't do this, the textarea ends up with two id attributes because FBV_id is also set.
|
||||
default: $textAreaParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars((string) $value, ENT_QUOTES) . '" ';
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_textAreaParams', $textAreaParams);
|
||||
|
||||
return $smarty->fetch('form/textarea.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hidden input element.
|
||||
* parameters: value, id, name (optional - assigned value of 'id' by default), disabled (optional), multilingual (optional)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVHiddenInput($params, $smarty)
|
||||
{
|
||||
$params['name'] = $params['name'] ?? $params['id'];
|
||||
|
||||
$hiddenInputParams = '';
|
||||
$smarty->assign(['FBV_id' => null, 'FBV_value' => null]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'name':
|
||||
case 'id':
|
||||
case 'value':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'label': break;
|
||||
case 'type': break;
|
||||
default: $hiddenInputParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_hiddenInputParams', $hiddenInputParams);
|
||||
|
||||
return $smarty->fetch('form/hiddenInput.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form select control.
|
||||
* parameters: from [array], selected [array index], defaultLabel (optional), defaultValue (optional), disabled (optional),
|
||||
* translate (optional), name (optional - value of 'id' by default)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVSelect($params, $smarty)
|
||||
{
|
||||
$params['name'] = $params['name'] ?? $params['id'];
|
||||
$params['translate'] = $params['translate'] ?? true;
|
||||
$params['subLabelTranslate'] = isset($params['subLabelTranslate']) ? (bool) $params['subLabelTranslate'] : true;
|
||||
|
||||
$selectParams = '';
|
||||
|
||||
$smarty->assign([
|
||||
'FBV_label' => null,
|
||||
'FBV_from' => null,
|
||||
'FBV_selected' => null,
|
||||
'FBV_label_content' => null,
|
||||
'FBV_defaultValue' => null,
|
||||
'FBV_defaultLabel' => null,
|
||||
'FBV_required' => false,
|
||||
'FBV_disabled' => false,
|
||||
]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'from':
|
||||
case 'selected':
|
||||
case 'translate':
|
||||
case 'defaultValue':
|
||||
case 'defaultLabel':
|
||||
case 'disabled':
|
||||
case 'required':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'type':
|
||||
case 'inline':
|
||||
case 'size':
|
||||
break;
|
||||
case 'subLabelTranslate': break;
|
||||
case 'label': $smarty->assign('FBV_label_content', $this->_smartyFBVSubLabel($params, $smarty));
|
||||
break;
|
||||
default: $selectParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value ?? '', ENT_QUOTES) . '" ';
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_selectParams', $selectParams);
|
||||
|
||||
return $smarty->fetch('form/select.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form checkbox group control.
|
||||
* parameters: from [array], selected [array index], defaultLabel (optional), defaultValue (optional), disabled (optional),
|
||||
* translate (optional), name (optional - value of 'id' by default)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVCheckboxGroup($params, $smarty)
|
||||
{
|
||||
$params['name'] = $params['name'] ?? $params['id'];
|
||||
$params['translate'] = isset($params['translate']) ? (bool)$params['translate'] : true;
|
||||
$params['subLabelTranslate'] = isset($params['subLabelTranslate']) ? (bool) $params['subLabelTranslate'] : true;
|
||||
$checkboxParams = '';
|
||||
|
||||
$smarty->assign(['FBV_from' => null, 'FBV_selected' => false, 'FBV_label_content' => null, 'FBV_defaultValue' => null, 'FBV_defaultLabel' => null]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'from':
|
||||
case 'selected':
|
||||
case 'defaultValue':
|
||||
case 'defaultLabel':
|
||||
case 'translate':
|
||||
case 'name':
|
||||
case 'validation':
|
||||
case 'disabled':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'type': break;
|
||||
case 'inline': break;
|
||||
case 'subLabelTranslate': break;
|
||||
default: $checkboxParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_checkboxParams', $checkboxParams);
|
||||
|
||||
return $smarty->fetch('form/checkboxGroup.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkbox input control.
|
||||
* parameters: label, disabled (optional), translate (optional), name (optional - value of 'id' by default)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVCheckbox($params, $smarty)
|
||||
{
|
||||
$params['name'] = $params['name'] ?? $params['id'];
|
||||
$params['translate'] = isset($params['translate']) ? (bool)$params['translate'] : true;
|
||||
|
||||
$checkboxParams = '';
|
||||
$smarty->assign(['FBV_id' => null, 'FBV_label' => null, 'FBV_checked' => false, 'FBV_disabled' => false]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'type': break;
|
||||
case 'id':
|
||||
case 'label':
|
||||
case 'translate':
|
||||
case 'checked':
|
||||
case 'disabled':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
default: $checkboxParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_checkboxParams', $checkboxParams);
|
||||
|
||||
return $smarty->fetch('form/checkbox.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio input control.
|
||||
* parameters: label, disabled (optional), translate (optional), name (optional - value of 'id' by default)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVRadioButton($params, $smarty)
|
||||
{
|
||||
$params['name'] = $params['name'] ?? $params['id'];
|
||||
$params['translate'] = $params['translate'] ?? true;
|
||||
|
||||
if (isset($params['label']) && isset($params['content'])) {
|
||||
throw new Exception('FBV: radio button cannot have both a content and a label parameter. Label has precedence.');
|
||||
}
|
||||
|
||||
$radioParams = '';
|
||||
$smarty->assign(['FBV_id' => null, 'FBV_label' => null, 'FBV_content' => null, 'FBV_checked' => false, 'FBV_disabled' => false]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'type': break;
|
||||
case 'id':
|
||||
case 'label':
|
||||
case 'translate':
|
||||
case 'checked':
|
||||
case 'disabled':
|
||||
case 'content':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
default: $radioParams .= htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '" ';
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('FBV_radioParams', $radioParams);
|
||||
|
||||
return $smarty->fetch('form/radioButton.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload input.
|
||||
* parameters: submit (optional - name of submit button to include), disabled (optional), name (optional - value of 'id' by default)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVFileInput($params, $smarty)
|
||||
{
|
||||
$params['name'] = $params['name'] ?? $params['id'];
|
||||
$params['translate'] = $params['translate'] ?? true;
|
||||
|
||||
$smarty->assign(['FBV_id' => null, 'FBV_label_content' => null, 'FBV_checked' => false, 'FBV_disabled' => false, 'FBV_submit' => null]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'type': break;
|
||||
case 'id':
|
||||
case 'submit':
|
||||
case 'name':
|
||||
case 'disabled':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'label': $smarty->assign('FBV_label_content', $this->_smartyFBVSubLabel($params, $smarty));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $smarty->fetch('form/fileInput.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Keyword input.
|
||||
* parameters: available - all available keywords (for autosuggest); current - user's current keywords
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVKeywordInput($params, $smarty)
|
||||
{
|
||||
$params['uniqId'] = uniqid();
|
||||
|
||||
$smarty->assign(['FBV_id' => null, 'FBV_label' => null, 'FBV_label_content' => null, 'FBV_currentKeywords' => null, 'FBV_multilingual' => false, 'FBV_sourceUrl' => null, 'FBV_disabled' => false]);
|
||||
$emptyFormLocaleMap = array_fill_keys(array_keys($smarty->getTemplateVars('formLocales')), []);
|
||||
$smarty->assign('FBV_availableKeywords', $emptyFormLocaleMap);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'type': break;
|
||||
case 'id':
|
||||
case 'uniqId':
|
||||
case 'disabled':
|
||||
case 'multilingual':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'label': $smarty->assign('FBV_label_content', $this->_smartyFBVSubLabel($params, $smarty));
|
||||
break;
|
||||
case 'available': $smarty->assign(
|
||||
'FBV_availableKeywords',
|
||||
$thing = array_merge(
|
||||
$emptyFormLocaleMap,
|
||||
$value
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'current': $smarty->assign('FBV_currentKeywords', $value);
|
||||
break;
|
||||
case 'sourceUrl': $smarty->assign('FBV_sourceUrl', $value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $smarty->fetch('form/keywordInput.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reviewing interests input.
|
||||
* parameters: interests - current users's keywords (array)
|
||||
*
|
||||
* @param array $params
|
||||
* @param object $smarty
|
||||
*/
|
||||
public function _smartyFBVInterestsInput($params, $smarty)
|
||||
{
|
||||
$smarty->assign(['FBV_id' => null, 'FBV_label' => null, 'FBV_label_content' => null, 'FBV_interests' => null]);
|
||||
$params['subLabelTranslate'] = isset($params['subLabelTranslate']) ? (bool) $params['subLabelTranslate'] : true;
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'type': break;
|
||||
case 'id':
|
||||
case 'interests':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
case 'label': $smarty->assign('FBV_label_content', $this->_smartyFBVSubLabel($params, $smarty));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $smarty->fetch('form/interestsInput.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Smarty function for labelling/highlighting of form fields.
|
||||
*
|
||||
* @param array $params can contain 'name' (field name/ID), 'required' (required field), 'key' (localization key), 'label' (non-localized label string), 'suppressId' (boolean)
|
||||
* @param \Smarty $smarty
|
||||
*/
|
||||
public function _smartyFBVSubLabel($params, $smarty)
|
||||
{
|
||||
assert(isset($params['label']));
|
||||
|
||||
$returner = '';
|
||||
|
||||
$smarty->assign(['FBV_suppressId' => null, 'FBV_label' => null, 'FBV_required' => false, 'FBV_uniqId' => null, 'FBV_multilingual' => false]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'subLabelTranslate': $smarty->assign('FBV_subLabelTranslate', $value);
|
||||
break;
|
||||
case 'label':
|
||||
case 'uniqId':
|
||||
case 'multilingual':
|
||||
case 'suppressId':
|
||||
case 'required':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$form = $this->getForm();
|
||||
if (isset($form) && isset($form->errorFields[$params['name']])) {
|
||||
$smarty->assign('FBV_error', true);
|
||||
$errors = $form->getErrorsArray();
|
||||
$smarty->assign('FBV_subLabelTranslate', false);
|
||||
$smarty->assign('FBV_label', $errors[$params['name']]);
|
||||
} else {
|
||||
$smarty->assign('FBV_error', false);
|
||||
}
|
||||
|
||||
$returner = $smarty->fetch('form/subLabel.tpl');
|
||||
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the appropriate class name to the element for client-side validation
|
||||
*
|
||||
* @param array $params
|
||||
* return array
|
||||
*/
|
||||
public function _addClientSideValidation($params)
|
||||
{
|
||||
$form = $this->getForm();
|
||||
if (isset($form)) {
|
||||
// Assign the appropriate class name to the element for client-side validation
|
||||
$fieldId = $params['id'];
|
||||
if (isset($form->cssValidation[$fieldId])) {
|
||||
$params['validation'] = implode(' ', $form->cssValidation[$fieldId]);
|
||||
}
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle through layout parameters to add the appropriate classes to the element's parent container
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function _getLayoutInfo($params)
|
||||
{
|
||||
$classes = [];
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'size':
|
||||
switch ($value) {
|
||||
case 'SMALL': $classes[] = 'pkp_helpers_quarter';
|
||||
break;
|
||||
case 'MEDIUM': $classes[] = 'pkp_helpers_half';
|
||||
break;
|
||||
case 'LARGE': $classes[] = 'pkp_helpers_threeQuarter';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'inline':
|
||||
if ($value) {
|
||||
$classes[] = 'inline';
|
||||
} break;
|
||||
}
|
||||
}
|
||||
if (!empty($classes)) {
|
||||
return implode(' ', $classes);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Custom Smarty function for labelling/highlighting of form fields.
|
||||
*
|
||||
* @param array $params can contain 'name' (field name/ID), 'required' (required field), 'key' (localization key), 'label' (non-localized label string), 'suppressId' (boolean)
|
||||
* @param \Smarty $smarty
|
||||
*/
|
||||
public function smartyFieldLabel($params, $smarty)
|
||||
{
|
||||
$returner = '';
|
||||
if (isset($params) && !empty($params)) {
|
||||
if (isset($params['key'])) {
|
||||
$params['label'] = __($params['key'], $params ?? []);
|
||||
}
|
||||
|
||||
$form = $this->getForm();
|
||||
if (isset($form) && isset($form->errorFields[$params['name']])) {
|
||||
$smarty->assign('FBV_class', 'error ' . $params['class']);
|
||||
} else {
|
||||
$smarty->assign('FBV_class', $params['class']);
|
||||
}
|
||||
|
||||
$smarty->assign(['FBV_suppressId' => null, 'FBV_label' => null, 'FBV_required' => false, 'FBV_disabled' => false, 'FBV_name' => null]);
|
||||
foreach ($params as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'label':
|
||||
case 'required':
|
||||
case 'suppressId':
|
||||
case 'disabled':
|
||||
case 'name':
|
||||
$smarty->assign('FBV_' . $key, $value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$returner = $smarty->fetch('form/fieldLabel.tpl');
|
||||
}
|
||||
return $returner;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\FormBuilderVocabulary', '\FormBuilderVocabulary');
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/FormError.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormError
|
||||
*
|
||||
* @ingroup form
|
||||
*
|
||||
* @brief Class to represent a form validation error.
|
||||
*/
|
||||
|
||||
namespace PKP\form;
|
||||
|
||||
class FormError
|
||||
{
|
||||
/** @var string The name of the field */
|
||||
public $field;
|
||||
|
||||
/** @var string The error message */
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $field the name of the field
|
||||
* @param string $message the error message (i18n key)
|
||||
*/
|
||||
public function __construct($field, $message)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field associated with the error.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error message (i18n key).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\FormError', '\FormError');
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup form_validation Form Validation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidator.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidator
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Class to represent a form validation check.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
use PKP\form\Form;
|
||||
use PKP\validation\Validator;
|
||||
|
||||
class FormValidator
|
||||
{
|
||||
// The two allowed states for the type field
|
||||
public const FORM_VALIDATOR_OPTIONAL_VALUE = 'optional';
|
||||
public const FORM_VALIDATOR_REQUIRED_VALUE = 'required';
|
||||
|
||||
/** @var Form The Form associated with the check */
|
||||
public $_form;
|
||||
|
||||
/** @var string The name of the field */
|
||||
public $_field;
|
||||
|
||||
/** @var string The type of check ("required" or "optional") */
|
||||
public $_type;
|
||||
|
||||
/** @var string The error message associated with a validation failure */
|
||||
public $_message;
|
||||
|
||||
/** @var Validator The validator used to validate the field */
|
||||
public $_validator;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param Validator $validator the validator used to validate this form field (optional)
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $validator = null)
|
||||
{
|
||||
$this->_form = & $form;
|
||||
$this->_field = $field;
|
||||
$this->_type = $type;
|
||||
$this->_message = $message;
|
||||
$this->_validator = & $validator;
|
||||
|
||||
$form->cssValidation[$field] = [];
|
||||
if ($type == self::FORM_VALIDATOR_REQUIRED_VALUE) {
|
||||
array_push($form->cssValidation[$field], 'required');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Setters and Getters
|
||||
//
|
||||
/**
|
||||
* Get the field associated with the check.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->_field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error message associated with a failed validation check.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return __($this->_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form associated with the check
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function &getForm()
|
||||
{
|
||||
return $this->_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validator associated with the check
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
public function &getValidator()
|
||||
{
|
||||
return $this->_validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the validated field ('optional' or 'required')
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->_type;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* Check if field value is valid.
|
||||
* Default check is that field is either optional or not empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
if ($this->isEmptyAndOptional()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$validator = & $this->getValidator();
|
||||
if (is_null($validator)) {
|
||||
// Default check: field must not be empty.
|
||||
$fieldValue = $this->getFieldValue();
|
||||
if (is_scalar($fieldValue)) {
|
||||
return $fieldValue !== '';
|
||||
} else {
|
||||
return $fieldValue !== [];
|
||||
}
|
||||
} else {
|
||||
// Delegate to the validator for the field value check.
|
||||
return $validator->isValid($this->getFieldValue());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Protected helper methods
|
||||
//
|
||||
/**
|
||||
* Get field value
|
||||
*/
|
||||
public function getFieldValue()
|
||||
{
|
||||
$form = & $this->getForm();
|
||||
$fieldValue = $form->getData($this->getField());
|
||||
if (is_null($fieldValue) || is_scalar($fieldValue)) {
|
||||
$fieldValue = trim((string)$fieldValue);
|
||||
}
|
||||
return $fieldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if field value is empty and optional.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmptyAndOptional()
|
||||
{
|
||||
if ($this->getType() != self::FORM_VALIDATOR_OPTIONAL_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fieldValue = $this->getFieldValue();
|
||||
if (is_scalar($fieldValue)) {
|
||||
return $fieldValue == '';
|
||||
} else {
|
||||
return empty($fieldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidator', '\FormValidator');
|
||||
define('FORM_VALIDATOR_OPTIONAL_VALUE', FormValidator::FORM_VALIDATOR_OPTIONAL_VALUE);
|
||||
define('FORM_VALIDATOR_REQUIRED_VALUE', FormValidator::FORM_VALIDATOR_REQUIRED_VALUE);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorArray.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorArray
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check that checks an array of fields.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
class FormValidatorArray extends FormValidator
|
||||
{
|
||||
/** @var array Array of fields to check */
|
||||
public $_fields;
|
||||
|
||||
/** @var array Array of field names where an error occurred */
|
||||
public $_errorFields;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param array $fields all subfields for each item in the array, i.e. name[][foo]. If empty it is assumed that name[] is a data field
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $fields = [])
|
||||
{
|
||||
parent::__construct($form, $field, $type, $message);
|
||||
$this->_fields = $fields;
|
||||
$this->_errorFields = [];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Setters and Getters
|
||||
//
|
||||
/**
|
||||
* Get array of fields where an error occurred.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorFields()
|
||||
{
|
||||
return $this->_errorFields;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* @see FormValidator::isValid()
|
||||
* Value is valid if it is empty and optional or all field values are set.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
if ($this->getType() == FormValidator::FORM_VALIDATOR_OPTIONAL_VALUE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$data = $this->getFieldValue();
|
||||
if (!is_array($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$isValid = true;
|
||||
foreach ($data as $key => $value) {
|
||||
if (count($this->_fields) == 0) {
|
||||
// We expect all fields to contain values.
|
||||
if (is_null($value) || trim((string)$value) == '') {
|
||||
$isValid = false;
|
||||
array_push($this->_errorFields, $this->getField() . "[{$key}]");
|
||||
}
|
||||
} else {
|
||||
// In the two-dimensional case we always expect a value array.
|
||||
if (!is_array($value)) {
|
||||
$isValid = false;
|
||||
array_push($this->_errorFields, $this->getField() . "[{$key}]");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Go through all sub-sub-fields and check them explicitly
|
||||
foreach ($this->_fields as $field) {
|
||||
if (!isset($value[$field]) || trim((string)$value[$field]) == '') {
|
||||
$isValid = false;
|
||||
array_push($this->_errorFields, $this->getField() . "[{$key}][{$field}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorArray', '\FormValidatorArray');
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorArrayCustom.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorArrayCustom
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check with a custom user function performing the validation check of an array of fields.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
class FormValidatorArrayCustom extends FormValidator
|
||||
{
|
||||
/** @var array Array of fields to check */
|
||||
public $_fields;
|
||||
|
||||
/** @var array Array of field names where an error occurred */
|
||||
public $_errorFields;
|
||||
|
||||
/** @var bool is the field a multilingual-capable field */
|
||||
public $_isLocaleField;
|
||||
|
||||
/** @var callable Custom validation function */
|
||||
public $_userFunction;
|
||||
|
||||
/** @var array Additional arguments to pass to $userFunction */
|
||||
public $_additionalArguments;
|
||||
|
||||
/** @var bool If true, field is considered valid if user function returns false instead of true */
|
||||
public $_complementReturn;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param callable $userFunction the user function to use for validation
|
||||
* @param array $additionalArguments optional, a list of additional arguments to pass to $userFunction
|
||||
* @param bool $complementReturn optional, complement the value returned by $userFunction
|
||||
* @param array $fields all subfields for each item in the array, i.e. name[][foo]. If empty it is assumed that name[] is a data field
|
||||
* @param bool $isLocaleField
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $userFunction, $additionalArguments = [], $complementReturn = false, $fields = [], $isLocaleField = false)
|
||||
{
|
||||
parent::__construct($form, $field, $type, $message);
|
||||
$this->_fields = $fields;
|
||||
$this->_errorFields = [];
|
||||
$this->_isLocaleField = $isLocaleField;
|
||||
$this->_userFunction = $userFunction;
|
||||
$this->_additionalArguments = $additionalArguments;
|
||||
$this->_complementReturn = $complementReturn;
|
||||
}
|
||||
|
||||
//
|
||||
// Setters and Getters
|
||||
//
|
||||
/**
|
||||
* Get array of fields where an error occurred.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorFields()
|
||||
{
|
||||
return $this->_errorFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is it a multilingual-capable field.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocaleField()
|
||||
{
|
||||
return $this->_isLocaleField;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* @see FormValidator::isValid()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
if ($this->isEmptyAndOptional()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$data = $this->getFieldValue();
|
||||
if (!is_array($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$isValid = true;
|
||||
foreach ($data as $key => $value) {
|
||||
// Bypass check for empty sub-fields if validation type is "optional"
|
||||
if ($this->getType() == FormValidator::FORM_VALIDATOR_OPTIONAL_VALUE && ($value == [] || $value == '')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($this->_fields) == 0) {
|
||||
if ($this->isLocaleField()) {
|
||||
$ret = call_user_func_array($this->_userFunction, array_merge([$value, $key], $this->_additionalArguments));
|
||||
} else {
|
||||
$ret = call_user_func_array($this->_userFunction, array_merge([$value], $this->_additionalArguments));
|
||||
}
|
||||
$ret = $this->_complementReturn ? !$ret : $ret;
|
||||
if (!$ret) {
|
||||
$isValid = false;
|
||||
if ($this->isLocaleField()) {
|
||||
$this->_errorFields[$key] = $this->getField() . "[{$key}]";
|
||||
} else {
|
||||
array_push($this->_errorFields, $this->getField() . "[{$key}]");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In the two-dimensional case we always expect a value array.
|
||||
if (!is_array($value)) {
|
||||
$isValid = false;
|
||||
if ($this->isLocaleField()) {
|
||||
$this->_errorFields[$key] = $this->getField() . "[{$key}]";
|
||||
} else {
|
||||
array_push($this->_errorFields, $this->getField() . "[{$key}]");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->_fields as $field) {
|
||||
// Bypass check for empty sub-sub-fields if validation type is "optional"
|
||||
if ($this->getType() == FormValidator::FORM_VALIDATOR_OPTIONAL_VALUE) {
|
||||
if (!isset($value[$field]) || $value[$field] == [] or $value[$field] == '') {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Make sure that we pass in 'null' to the user function
|
||||
// if the expected field doesn't exist in the value array.
|
||||
if (!array_key_exists($field, $value)) {
|
||||
$value[$field] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isLocaleField()) {
|
||||
$ret = call_user_func_array($this->_userFunction, array_merge([$value[$field], $key], $this->_additionalArguments));
|
||||
} else {
|
||||
$ret = call_user_func_array($this->_userFunction, array_merge([$value[$field]], $this->_additionalArguments));
|
||||
}
|
||||
$ret = $this->_complementReturn ? !$ret : $ret;
|
||||
if (!$ret) {
|
||||
$isValid = false;
|
||||
if ($this->isLocaleField()) {
|
||||
if (!isset($this->_errorFields[$key])) {
|
||||
$this->_errorFields[$key] = [];
|
||||
}
|
||||
array_push($this->_errorFields[$key], $this->getField() . "[{$key}][{$field}]");
|
||||
} else {
|
||||
array_push($this->_errorFields, $this->getField() . "[{$key}][{$field}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the field an array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray()
|
||||
{
|
||||
return is_array($this->getFieldValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorArrayCustom', '\FormValidatorArrayCustom');
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorBoolean.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorBoolean
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check that checks if the value can be
|
||||
* interpreted as a boolean value. An empty field is considered
|
||||
* 'false', a value of '1' is considered 'true'.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
class FormValidatorBoolean extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
*/
|
||||
public function __construct(&$form, $field, $message)
|
||||
{
|
||||
parent::__construct($form, $field, FormValidator::FORM_VALIDATOR_OPTIONAL_VALUE, $message);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* Value is valid if it is empty (false) or has
|
||||
* value '1' (true). This assumes checkbox
|
||||
* behavior in the form.
|
||||
*
|
||||
* @see FormValidator::isValid()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
$value = $this->getFieldValue();
|
||||
$form = & $this->getForm();
|
||||
if (empty($value) || $value == 'on') {
|
||||
// Make sure that the form will contain a real
|
||||
// boolean value after validation.
|
||||
$value = ($value == 'on' ? true : false);
|
||||
$form->setData($this->getField(), $value);
|
||||
return true;
|
||||
} elseif ($value === '1' || $value === '0') {
|
||||
$value = ($value === '1' ? true : false);
|
||||
$form->setData($this->getField(), $value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorBoolean', '\FormValidatorBoolean');
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorCSRF.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorCSRF
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check to make sure the CSRF token is correct.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use APP\core\Application;
|
||||
|
||||
class FormValidatorCSRF extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form
|
||||
* @param string $message the locale key to use (optional)
|
||||
*/
|
||||
public function __construct(&$form, $message = 'form.csrfInvalid')
|
||||
{
|
||||
parent::__construct($form, 'dummy', FormValidator::FORM_VALIDATOR_REQUIRED_VALUE, $message);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* Check if the CSRF token is correct.
|
||||
* overrides FormValidator::isValid()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
$request = Application::get()->getRequest();
|
||||
return $request->checkCSRF();
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorCSRF', '\FormValidatorCSRF');
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorControlledVocab.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorControlledVocab
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check that checks if value is within a certain set.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use PKP\validation\ValidatorControlledVocab;
|
||||
|
||||
class FormValidatorControlledVocab extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param string $symbolic
|
||||
* @param int $assocType
|
||||
* @param int $assocId
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $symbolic, $assocType, $assocId)
|
||||
{
|
||||
$validator = new ValidatorControlledVocab($symbolic, $assocType, $assocId);
|
||||
parent::__construct($form, $field, $type, $message, $validator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorControlledVocab', '\FormValidatorControlledVocab');
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorCustom.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorCustom
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check with a custom user function performing the validation check.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
class FormValidatorCustom extends FormValidator
|
||||
{
|
||||
/** @var callable Custom validation function */
|
||||
public $_userFunction;
|
||||
|
||||
/** @var array Additional arguments to pass to $userFunction */
|
||||
public $_additionalArguments;
|
||||
|
||||
/** @var bool If true, field is considered valid if user function returns false instead of true */
|
||||
public $_complementReturn;
|
||||
|
||||
/** @var mixed[] Arguments to pass to getMessage() */
|
||||
public $_messageArgs = [];
|
||||
|
||||
/** @var array If present, additional arguments to pass to the getMessage translation function
|
||||
* The user function is passed the form data as its first argument and $additionalArguments, if set, as the remaining arguments. This function must return a boolean value.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param callable $userFunction function the user function to use for validation
|
||||
* @param array $additionalArguments optional, a list of additional arguments to pass to $userFunction
|
||||
* @param bool $complementReturn optional, complement the value returned by $userFunction
|
||||
* @param array $messageArgs optional, arguments to pass to getMessage()
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $userFunction, $additionalArguments = [], $complementReturn = false, $messageArgs = [])
|
||||
{
|
||||
parent::__construct($form, $field, $type, $message);
|
||||
$this->_userFunction = $userFunction;
|
||||
$this->_additionalArguments = $additionalArguments;
|
||||
$this->_complementReturn = $complementReturn;
|
||||
$this->_messageArgs = $messageArgs;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Setters and Getters
|
||||
//
|
||||
/**
|
||||
* @see FormValidator::getMessage()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return __($this->_message, $this->_messageArgs);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* @see FormValidator::isValid()
|
||||
* Value is valid if it is empty and optional or validated by user-supplied function.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
if ($this->isEmptyAndOptional()) {
|
||||
return true;
|
||||
} else {
|
||||
$ret = call_user_func_array($this->_userFunction, array_merge([$this->getFieldValue()], $this->_additionalArguments));
|
||||
return $this->_complementReturn ? !$ret : $ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorCustom', '\FormValidatorCustom');
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorEmail.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorEmail
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @see FormValidator
|
||||
*
|
||||
* @brief Form validation check for email addresses.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use PKP\validation\ValidatorEmail;
|
||||
|
||||
class FormValidatorEmail extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
*/
|
||||
public function __construct(&$form, $field, $type = 'optional', $message = 'email.invalid')
|
||||
{
|
||||
$validator = new ValidatorEmail();
|
||||
parent::__construct($form, $field, $type, $message, $validator);
|
||||
array_push($form->cssValidation[$field], 'email');
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return __($this->_message, ['email' => $this->getFieldValue()]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorEmail', '\FormValidatorEmail');
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorISSN.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorISSN
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check for ISSNs.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use PKP\validation\ValidatorISSN;
|
||||
|
||||
class FormValidatorISSN extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
*/
|
||||
public function __construct($form, $field, $type, $message)
|
||||
{
|
||||
$validator = new ValidatorISSN();
|
||||
parent::__construct($form, $field, $type, $message, $validator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorISSN', '\FormValidatorISSN');
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorInSet.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorInSet
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check that checks if value is within a certain set.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
class FormValidatorInSet extends FormValidator
|
||||
{
|
||||
/** @var array of all values accepted as valid */
|
||||
public $_acceptedValues;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param array $acceptedValues all possible accepted values
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $acceptedValues)
|
||||
{
|
||||
parent::__construct($form, $field, $type, $message);
|
||||
$this->_acceptedValues = $acceptedValues;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* Value is valid if it is empty and optional or is in the set of accepted values.
|
||||
*
|
||||
* @see FormValidator::isValid()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return $this->isEmptyAndOptional() || in_array($this->getFieldValue(), $this->_acceptedValues);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorInSet', '\FormValidatorInSet');
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorLength.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorLength
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check that checks if a field's length meets certain requirements.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use PKP\core\PKPString;
|
||||
|
||||
class FormValidatorLength extends FormValidator
|
||||
{
|
||||
/** @var string comparator to use (== | != | < | > | <= | >= ) */
|
||||
public $_comparator;
|
||||
|
||||
/** @var int length to compare with */
|
||||
public $_length;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param string $comparator
|
||||
* @param int $length
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $comparator, $length)
|
||||
{
|
||||
parent::__construct($form, $field, $type, $message);
|
||||
$this->_comparator = $comparator;
|
||||
$this->_length = $length;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Setters and Getters
|
||||
//
|
||||
/**
|
||||
* @see FormValidator::getMessage()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return __($this->_message, ['length' => $this->_length]);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* @see FormValidator::isValid()
|
||||
* Value is valid if it is empty and optional or meets the specified length requirements.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
if ($this->isEmptyAndOptional()) {
|
||||
return true;
|
||||
} else {
|
||||
$length = PKPString::strlen($this->getFieldValue());
|
||||
switch ($this->_comparator) {
|
||||
case '==':
|
||||
return $length == $this->_length;
|
||||
case '!=':
|
||||
return $length != $this->_length;
|
||||
case '<':
|
||||
return $length < $this->_length;
|
||||
case '>':
|
||||
return $length > $this->_length;
|
||||
case '<=':
|
||||
return $length <= $this->_length;
|
||||
case '>=':
|
||||
return $length >= $this->_length;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorLength', '\FormValidatorLength');
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorLocale.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorLocale
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Class to represent a form validation check for localized fields.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use PKP\facades\Locale;
|
||||
|
||||
class FormValidatorLocale extends FormValidator
|
||||
{
|
||||
/** @var string Symbolic name of the locale to require */
|
||||
public $_requiredLocale;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param \PKP\validation\Validator $validator the validator used to validate this form field (optional)
|
||||
* @param string $requiredLocale The name of the required locale, i.e. en
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $requiredLocale = null, $validator = null)
|
||||
{
|
||||
parent::__construct($form, $field, $type, $message, $validator);
|
||||
if ($requiredLocale === null) {
|
||||
$requiredLocale = Locale::getPrimaryLocale();
|
||||
}
|
||||
$this->_requiredLocale = $requiredLocale;
|
||||
}
|
||||
|
||||
//
|
||||
// Getters and Setters
|
||||
//
|
||||
/**
|
||||
* Get the error message associated with a failed validation check.
|
||||
*
|
||||
* @see FormValidator::getMessage()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return parent::getMessage() . ' (' . Locale::getMetadata($this->_requiredLocale)->getDisplayName() . ')';
|
||||
}
|
||||
|
||||
//
|
||||
// Protected helper methods
|
||||
//
|
||||
/**
|
||||
* @see FormValidator::getFieldValue()
|
||||
*/
|
||||
public function getFieldValue()
|
||||
{
|
||||
$form = & $this->getForm();
|
||||
$data = $form->getData($this->getField());
|
||||
|
||||
$fieldValue = '';
|
||||
if (is_array($data) && isset($data[$this->_requiredLocale])) {
|
||||
$fieldValue = $data[$this->_requiredLocale];
|
||||
if (is_scalar($fieldValue)) {
|
||||
$fieldValue = trim((string)$fieldValue);
|
||||
}
|
||||
}
|
||||
return $fieldValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorLocale', '\FormValidatorLocale');
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorLocaleEmail.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorLocaleEmail
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @see FormValidatorLocale
|
||||
*
|
||||
* @brief Form validation check for email addresses.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use PKP\validation\ValidatorEmail;
|
||||
|
||||
class FormValidatorLocaleEmail extends FormValidatorLocale
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param string $requiredLocale The symbolic name of the required locale
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $requiredLocale = null)
|
||||
{
|
||||
$validator = new ValidatorEmail();
|
||||
parent::__construct($form, $field, $type, $message, $requiredLocale, $validator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorLocaleEmail', '\FormValidatorLocaleEmail');
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorLocaleUrl.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorLocaleUrl
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @see FormValidatorLocale
|
||||
*
|
||||
* @brief Form validation check for URL addresses.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
;
|
||||
use PKP\validation\ValidatorUrl;
|
||||
|
||||
class FormValidatorLocaleUrl extends FormValidatorLocale
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param string $requiredLocale The symbolic name of the required locale
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $requiredLocale = null)
|
||||
{
|
||||
$validator = new ValidatorUrl();
|
||||
parent::__construct($form, $field, $type, $message, $requiredLocale, $validator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorLocaleUrl', '\FormValidatorLocaleUrl');
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorORCID.php
|
||||
*
|
||||
* Copyright (c) 2013-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorORCID
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check for ORCID iDs.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
;
|
||||
use PKP\validation\ValidatorORCID;
|
||||
|
||||
class FormValidatorORCID extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
*/
|
||||
public function __construct($form, $field, $type, $message)
|
||||
{
|
||||
$validator = new ValidatorORCID();
|
||||
parent::__construct($form, $field, $type, $message, $validator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorORCID', '\FormValidatorORCID');
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorPost.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorPost
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check to make sure the form is POSTed.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use APP\core\Application;
|
||||
|
||||
class FormValidatorPost extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form
|
||||
* @param string $message the locale key to use (optional)
|
||||
*/
|
||||
public function __construct(&$form, $message = 'form.postRequired')
|
||||
{
|
||||
parent::__construct($form, 'dummy', FormValidator::FORM_VALIDATOR_REQUIRED_VALUE, $message);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* Check if form was posted.
|
||||
* overrides FormValidator::isValid()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
$request = Application::get()->getRequest();
|
||||
return $request->isPost();
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorPost', '\FormValidatorPost');
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorReCaptcha.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorReCaptcha
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check reCaptcha values.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
use APP\core\Application;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use PKP\config\Config;
|
||||
use PKP\form\Form;
|
||||
|
||||
class FormValidatorReCaptcha extends FormValidator
|
||||
{
|
||||
/** @var string The response field containing the reCaptcha response */
|
||||
private const RECAPTCHA_RESPONSE_FIELD = 'g-recaptcha-response';
|
||||
/** @var string The request URL */
|
||||
private const RECAPTCHA_URL = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
/** @var string The initiating IP address of the user */
|
||||
private $_userIp;
|
||||
/** @var string The hostname to expect in the validation response */
|
||||
private $_hostname;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form
|
||||
* @param string $userIp IP address of user request
|
||||
* @param string $message Key of message to display on mismatch
|
||||
* @param string|null $hostname Hostname to expect in validation response
|
||||
*/
|
||||
public function __construct(Form $form, string $userIp, string $message, ?string $hostname = null)
|
||||
{
|
||||
parent::__construct($form, self::RECAPTCHA_RESPONSE_FIELD, FormValidator::FORM_VALIDATOR_REQUIRED_VALUE, $message);
|
||||
$this->_userIp = $userIp;
|
||||
$this->_hostname = $hostname;
|
||||
}
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* @see FormValidator::isValid()
|
||||
* Determine whether or not the form meets this ReCaptcha constraint.
|
||||
*
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
$form = $this->getForm();
|
||||
try {
|
||||
$this->validateResponse($form->getData(self::RECAPTCHA_RESPONSE_FIELD), $this->_userIp, $this->_hostname);
|
||||
return true;
|
||||
} catch (Exception $exception) {
|
||||
$this->_message = 'common.captcha.error.missing-input-response';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the reCaptcha response
|
||||
*
|
||||
* @param string|null $response The reCaptcha response
|
||||
* @param string|null $ip The user IP address (defaults to null)
|
||||
* @param string|null $hostname The application hostname (defaults to null)
|
||||
*
|
||||
* @throws Exception Throws in case the validation fails
|
||||
*/
|
||||
public static function validateResponse(?string $response, ?string $ip = null, ?string $hostname = null): void
|
||||
{
|
||||
if (!empty($ip) && !filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
throw new InvalidArgumentException('Invalid IP address.');
|
||||
}
|
||||
|
||||
if (empty($response)) {
|
||||
throw new InvalidArgumentException('The reCaptcha user response is required.');
|
||||
}
|
||||
|
||||
$privateKey = Config::getVar('captcha', 'recaptcha_private_key');
|
||||
if (empty($privateKey)) {
|
||||
throw new Exception('The reCaptcha is not configured correctly, the secret key is missing.');
|
||||
}
|
||||
|
||||
$httpClient = Application::get()->getHttpClient();
|
||||
$response = $httpClient->request(
|
||||
'POST',
|
||||
self::RECAPTCHA_URL,
|
||||
[
|
||||
'multipart' => [
|
||||
['name' => 'secret', 'contents' => $privateKey],
|
||||
['name' => 'response', 'contents' => $response],
|
||||
['name' => 'remoteip', 'contents' => $ip]
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
$response = json_decode($response->getBody(), true);
|
||||
if (Config::getVar('captcha', 'recaptcha_enforce_hostname') && ($response['hostname'] ?? null) != $hostname) {
|
||||
throw new Exception('The hostname validation of the reCaptcha response failed.');
|
||||
}
|
||||
|
||||
$errorMap = [
|
||||
'missing-input-secret' => 'The secret parameter is missing.',
|
||||
'invalid-input-secret' => 'The secret parameter is invalid or malformed.',
|
||||
'missing-input-response' => 'The response parameter is missing.',
|
||||
'invalid-input-response' => 'The response parameter is invalid or malformed.',
|
||||
'invalid-keys' => 'The configured keys are invalid.',
|
||||
'bad-request' => 'The request is invalid or malformed.',
|
||||
'timeout-or-duplicate' => 'The response is no longer valid: either is too old or has been used previously.'
|
||||
];
|
||||
|
||||
if (!($response['success'] ?? false)) {
|
||||
$errors = [];
|
||||
foreach ($response['error-codes'] ?? [] as $error) {
|
||||
$errors[] = $errorMap[$error] ?? $error;
|
||||
}
|
||||
throw new Exception(implode("\n", $errors) ?: 'The reCaptcha validation failed.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorReCaptcha', '\FormValidatorReCaptcha');
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorRegExp.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorRegExp
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @brief Form validation check using a regular expression.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
;
|
||||
use PKP\validation\ValidatorRegExp;
|
||||
|
||||
class FormValidatorRegExp extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
* @param string $regExp the regular expression (PCRE form)
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message, $regExp)
|
||||
{
|
||||
$validator = new ValidatorRegExp($regExp);
|
||||
parent::__construct($form, $field, $type, $message, $validator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorRegExp', '\FormValidatorRegExp');
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorUrl.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorUrl
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @see FormValidator
|
||||
*
|
||||
* @brief Form validation check for URLs.
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
;
|
||||
use PKP\validation\ValidatorUrl;
|
||||
|
||||
class FormValidatorUrl extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message)
|
||||
{
|
||||
$validator = new ValidatorUrl();
|
||||
parent::__construct($form, $field, $type, $message, $validator);
|
||||
array_push($form->cssValidation[$field], 'url');
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorUrl', '\FormValidatorUrl');
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/form/validation/FormValidatorUsername.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class FormValidatorUsername
|
||||
*
|
||||
* @ingroup form_validation
|
||||
*
|
||||
* @see FormValidator
|
||||
*
|
||||
* @brief Form validation check for usernames (lowercase alphanumeric with interior dash/underscore
|
||||
*/
|
||||
|
||||
namespace PKP\form\validation;
|
||||
|
||||
;
|
||||
use PKP\validation\ValidatorRegExp;
|
||||
|
||||
class FormValidatorUsername extends FormValidator
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \PKP\form\Form $form the associated form
|
||||
* @param string $field the name of the associated field
|
||||
* @param string $type the type of check, either "required" or "optional"
|
||||
* @param string $message the error message for validation failures (i18n key)
|
||||
*/
|
||||
public function __construct(&$form, $field, $type, $message)
|
||||
{
|
||||
parent::__construct(
|
||||
$form,
|
||||
$field,
|
||||
$type,
|
||||
$message,
|
||||
new ValidatorRegExp('/^[a-z0-9]+([\-_][a-z0-9]+)*$/')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\form\validation\FormValidatorUsername', '\FormValidatorUsername');
|
||||
}
|
||||
Reference in New Issue
Block a user