aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichaela Brückner <brueckner@data-quest.de>2024-01-02 08:55:04 +0000
committerMichaela Brückner <brueckner@data-quest.de>2024-01-02 08:55:04 +0000
commit502e6a104f200acc2f8ff81ba09257a6035406e5 (patch)
tree6c10c6662c822475c92814ec32d60951eda9cad8
parentb6bdba58a8e090cb144bae382457c2e439d8a72a (diff)
new registry formular, re #1559
Closes #3533 Merge request studip/studip!1141
-rw-r--r--app/controllers/registration.php133
-rw-r--r--app/views/registration/index.php1
-rw-r--r--app/views/registration/save.php (renamed from templates/register/success.php)0
-rw-r--r--db/migrations/5.5.19_update_username_regular_expression.php27
-rw-r--r--lib/classes/forms/ConfirmInput.php18
-rw-r--r--lib/classes/forms/DatalistInput.php21
-rw-r--r--lib/classes/forms/Form.php71
-rw-r--r--lib/classes/forms/Input.php30
-rw-r--r--lib/classes/forms/Part.php4
-rw-r--r--lib/classes/forms/PasswordInput.php18
-rw-r--r--lib/classes/forms/RadioInput.php22
-rw-r--r--lib/classes/forms/RangeInput.php3
-rw-r--r--lib/navigation/LoginNavigation.php4
-rw-r--r--public/register1.php59
-rw-r--r--public/register2.php49
-rw-r--r--resources/assets/javascripts/bootstrap/forms.js154
-rw-r--r--resources/assets/stylesheets/scss/forms.scss8
-rw-r--r--templates/forms/confirm_password_input.php16
-rw-r--r--templates/forms/datalist_input.php22
-rw-r--r--templates/forms/form.php12
-rw-r--r--templates/forms/password_input.php16
-rw-r--r--templates/forms/radio_input.php16
-rw-r--r--templates/forms/range_input.php5
-rw-r--r--templates/forms/text_input.php13
-rw-r--r--templates/register/form.php175
-rw-r--r--templates/register/step1.php6
26 files changed, 548 insertions, 355 deletions
diff --git a/app/controllers/registration.php b/app/controllers/registration.php
new file mode 100644
index 0000000..edb6b42
--- /dev/null
+++ b/app/controllers/registration.php
@@ -0,0 +1,133 @@
+<?php
+class RegistrationController extends AuthenticatedController
+{
+ protected $allow_nobody = true;
+
+ public function before_filter(&$action, &$args)
+ {
+ parent::before_filter($action, $args);
+
+ PageLayout::setTitle(_('Registrierung'));
+ }
+
+ public function index_action()
+ {
+ $new_user = new User();
+
+ $this->registrationform = \Studip\Forms\Form::fromSORM(
+ $new_user,
+ [
+ 'legend' => _('Herzlich willkommen!'),
+ 'fields' => [
+ 'username' => [
+ 'label' => _('Benutzername'),
+ 'required' => true,
+ 'maxlength' => '63',
+ 'validate' => function ($value, $input) {
+ if (!preg_match(Config::get()->USERNAME_REGULAR_EXPRESSION, $value)) {
+ return Config::get()->getMetadata('USERNAME_REGULAR_EXPRESSION')['comment'] ?:
+ _('Benutzername muss mindestens 4 Zeichen lang sein und darf nur aus Buchstaben, '
+ . 'Ziffern, Unterstrich, @, Punkt und Minus bestehen.');
+ }
+ $user = User::findByUsername($value);
+ $context = $input->getContextObject();
+ if ($user && ($user->id !== $context->getId())) {
+ return _('Benutzername ist schon vergeben.');
+ }
+ return true;
+ }
+ ],
+ 'password' => [
+ 'label' => _('Passwort'),
+ 'type' => 'password',
+ 'required' => true,
+ 'maxlength' => '31',
+ 'minlength' => '8',
+ 'mapper' => function($value) {
+ $hasher = UserManagement::getPwdHasher();
+ return $hasher->HashPassword($value);
+ }
+ ],
+ 'confirm_password' => [
+ 'label' => _('Passwortbestätigung'),
+ 'type' => 'password',
+ 'required' => true,
+ 'maxlength' => '31',
+ 'minlength' => '8',
+ ':pattern' => "password.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&')", //mask special chars
+ 'data-validation_requirement' => _('Passwörter stimmen nicht überein.'),
+ 'store' => function() {}
+ ],
+ 'title_front' => [
+ 'label' => _('Titel'),
+ 'type' => 'datalist',
+ 'options' => $GLOBALS['TITLE_FRONT_TEMPLATE']
+ ],
+ 'title_rear' => [
+ 'label' => _('Titel nachgestellt'),
+ 'type' => 'datalist',
+ 'options' => $GLOBALS['TITLE_REAR_TEMPLATE'],
+ ],
+ 'vorname' => [
+ 'label' => _('Vorname'),
+ 'required' => true
+ ],
+ 'nachname' => [
+ 'label' => _('Nachname'),
+ 'required' => true
+ ],
+ 'geschlecht' => [
+ 'name' => 'geschlecht',
+ 'label' => _('Geschlecht'),
+ 'type' => 'radio',
+ 'orientation' => 'horizontal',
+ 'options' => [
+ '0' => _('keine Angabe'),
+ '1' => _('männlich'),
+ '2' => _('weiblich'),
+ '3' => _('divers'),
+ ],
+ ],
+ 'email' => [
+ 'label' => _('E-Mail'),
+ 'required' => true,
+ 'validate' => function ($value, $input) {
+ $user = User::findOneByEmail($value);
+ $context = $input->getContextObject();
+ if ($user && ($user->id !== $context->getId())) {
+ return _('Diese Emailadresse ist bereits registriert.');
+ }
+ return true;
+ }
+ ],
+ ]
+ ]
+ );
+ $this->registrationform->setSaveButtonText(_('Registrierung abschließen'));
+ $this->registrationform->setCancelButtonText(_('Abbrechen'));
+ $this->registrationform->setCancelButtonName(URLHelper::getURL('index.php?cancel_login=1'));
+
+ $this->registrationform->addStoreCallback(
+ function ($form) {
+ $new_user = $form->getLastPart()->getContextObject();
+
+ $GLOBALS['sess']->regenerate_session_id(['auth']);
+ $GLOBALS['auth']->unauth();
+ $GLOBALS['auth']->auth['jscript'] = true;
+ $GLOBALS['auth']->auth['perm'] = $new_user['perms'];
+ $GLOBALS['auth']->auth['uname'] = $new_user['username'];
+ $GLOBALS['auth']->auth['auth_plugin'] = $new_user['auth_plugin'];
+ $GLOBALS['auth']->auth_set_user_settings($new_user->user_id);
+ $GLOBALS['auth']->auth['uid'] = $new_user['user_id'];
+ $GLOBALS['auth']->auth['exp'] = time() + (60 * $GLOBALS['auth']->lifetime);
+ $GLOBALS['auth']->auth['refresh'] = time() + (60 * $GLOBALS['auth']->refresh);
+
+ Seminar_Register_Auth::sendValidationMail($new_user);
+
+ return 1;
+ }
+ );
+
+ $this->registrationform->autoStore()->setURL(URLHelper::getURL('dispatch.php/start'));
+ }
+}
diff --git a/app/views/registration/index.php b/app/views/registration/index.php
new file mode 100644
index 0000000..b253987
--- /dev/null
+++ b/app/views/registration/index.php
@@ -0,0 +1 @@
+<?= $registrationform->render() ?>
diff --git a/templates/register/success.php b/app/views/registration/save.php
index 92eca8e..92eca8e 100644
--- a/templates/register/success.php
+++ b/app/views/registration/save.php
diff --git a/db/migrations/5.5.19_update_username_regular_expression.php b/db/migrations/5.5.19_update_username_regular_expression.php
new file mode 100644
index 0000000..cdcbc9d
--- /dev/null
+++ b/db/migrations/5.5.19_update_username_regular_expression.php
@@ -0,0 +1,27 @@
+<?php
+class UpdateUsernameRegularExpression extends Migration
+{
+ public function description()
+ {
+ return 'Alters configuration description to make clearer that it is used for creating new users';
+ }
+
+ public function up()
+ {
+ $db = DBManager::get();
+ $stmt = $db->prepare("UPDATE `config`
+ SET `description` = 'Regulärer Ausdruck für erlaubte Zeichen in Benutzernamen. Das Kommentarfeld kann genutzt werden, um eine Fehlermeldung anzugeben, die zum Beispiel im Registrierungsformular ausgegeben wird, wenn der Ausdruck nicht erfüllt wird.'
+ WHERE `field` = 'USERNAME_REGULAR_EXPRESSION'");
+ $stmt->execute();
+ }
+
+ public function down()
+ {
+ $db = DBManager::get();
+ $stmt = $db->prepare("UPDATE `config`
+ SET `description` = 'Regex for allowed characters in usernames'
+ WHERE `field` = 'USERNAME_REGULAR_EXPRESSION'");
+ $stmt->execute();
+
+ }
+}
diff --git a/lib/classes/forms/ConfirmInput.php b/lib/classes/forms/ConfirmInput.php
new file mode 100644
index 0000000..13e3634
--- /dev/null
+++ b/lib/classes/forms/ConfirmInput.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Studip\Forms;
+
+class ConfirmInput extends Input
+{
+ public function render()
+ {
+ $template = $GLOBALS['template_factory']->open('forms/confirm_password_input');
+ $template->title = $this->title;
+ $template->name = $this->name;
+ $template->value = $this->value;
+ $template->id = md5(uniqid());
+ $template->required = $this->required;
+ $template->attributes = arrayToHtmlAttributes($this->attributes);
+ return $template->render();
+ }
+}
diff --git a/lib/classes/forms/DatalistInput.php b/lib/classes/forms/DatalistInput.php
new file mode 100644
index 0000000..06cc9e0
--- /dev/null
+++ b/lib/classes/forms/DatalistInput.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Studip\Forms;
+
+class DatalistInput extends Input
+{
+ public function render()
+ {
+ $options = $this->extractOptionsFromAttributes($this->attributes);
+
+ $template = $GLOBALS['template_factory']->open('forms/datalist_input');
+ $template->title = $this->title;
+ $template->name = $this->name;
+ $template->value = $this->value;
+ $template->id = md5(uniqid());
+ $template->required = $this->required;
+ $template->attributes = arrayToHtmlAttributes($this->attributes);
+ $template->options = $options;
+ return $template->render();
+ }
+}
diff --git a/lib/classes/forms/Form.php b/lib/classes/forms/Form.php
index 49713b7..3b29552 100644
--- a/lib/classes/forms/Form.php
+++ b/lib/classes/forms/Form.php
@@ -17,6 +17,8 @@ class Form extends Part
protected $save_button_text = '';
protected $save_button_name = '';
+ protected $cancel_button_text = '';
+ protected $cancel_button_name = '';
protected $autoStore = false;
protected $debugmode = false;
protected $success_message = '';
@@ -207,6 +209,31 @@ class Form extends Part
return $this->save_button_name ?: $this->getSaveButtonText();
}
+ public function setCancelButtonText(string $text): Form
+ {
+ $this->cancel_button_text = $text;
+ return $this;
+ }
+
+ /**
+ * @return string The text for the "save" button in the form.
+ */
+ public function getCancelButtonText() : string
+ {
+ return $this->cancel_button_text ?: _('Abbrechen');
+ }
+
+ public function setCancelButtonName(string $name): Form
+ {
+ $this->cancel_button_name = $name;
+ return $this;
+ }
+
+ public function getCancelButtonName() : string
+ {
+ return $this->cancel_button_name ?: $this->getCancelButtonText();
+ }
+
public function setSuccessMessage(string $success_message): Form
{
$this->success_message = $success_message;
@@ -250,12 +277,35 @@ class Form extends Part
{
$this->autoStore = true;
if (\Request::isPost() && \Request::isAjax() && !\Request::isDialog()) {
- $this->store();
- if ($this->success_message) {
- \PageLayout::postSuccess($this->success_message);
+ if (\Request::submitted('STUDIPFORM_SERVERVALIDATION')) {
+ //verify the user input:
+ $output = [];
+ foreach ($this->getAllInputs() as $input) {
+ if ($input->validate) {
+ $callback = $input->getValidationCallback();
+ $value = $this->getStorableValueFromRequest($input);
+ $valid = $callback($value, $input);
+ if ($valid !== true) {
+ $output[$input->getName()] = [
+ 'name' => $input->getName(),
+ 'label' => $input->getTitle(),
+ 'error' => $callback($value, $input)
+ ];
+ }
+ }
+ }
+ echo json_encode($output);
+ page_close();
+ die();
+ } else {
+ //storing the input
+ $this->store();
+ if ($this->success_message) {
+ \PageLayout::postSuccess($this->success_message);
+ }
+ page_close();
+ die();
}
- page_close();
- die();
}
return $this;
}
@@ -325,6 +375,17 @@ class Form extends Part
$stored = 0;
+ foreach ($this->getAllInputs() as $input) {
+ if ($input->validate) {
+ $callback = $input->getValidationCallback();
+ $value = $this->getStorableValueFromRequest($input);
+ $valid = $callback($value, $input);
+ if ($valid !== true) {
+ return $stored;
+ }
+ }
+ }
+
//store by each input
$all_values = [];
foreach ($this->getAllInputs() as $input) {
diff --git a/lib/classes/forms/Input.php b/lib/classes/forms/Input.php
index aa3069b..9d2ad32 100644
--- a/lib/classes/forms/Input.php
+++ b/lib/classes/forms/Input.php
@@ -11,6 +11,7 @@ abstract class Input
protected $parent = null;
public $mapper = null;
public $store = null;
+ public $validate = null;
public $if = null;
public $permission = true;
public $required = false;
@@ -133,6 +134,21 @@ abstract class Input
return $this->name;
}
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ public function hasValidation()
+ {
+ return $this->validate !== null;
+ }
+
+ public function getValidationCallback()
+ {
+ return $this->validate;
+ }
+
/**
* Returns the value of this input.
* @return null
@@ -216,6 +232,18 @@ abstract class Input
}
/**
+ * Sets the server-side verify function of this input. The callable returns true if the given value is okay, or
+ * false or a textstring representing the error.
+ * @param callable $verify
+ * @return $this
+ */
+ public function setValidationFunction(Callable $validate)
+ {
+ $this->validate = $validate;
+ return $this;
+ }
+
+ /**
* Sets a condition to display this input. The condition is a javascript condition which is used by vue to
* hide the input if the condition is not satisfies.
* @param string $if
@@ -261,7 +289,7 @@ abstract class Input
protected function extractOptionsFromAttributes(array &$attributes)
{
- $options = null;
+ $options = [];
if (isset($attributes['options'])) {
$options = $attributes['options'];
unset($attributes['options']);
diff --git a/lib/classes/forms/Part.php b/lib/classes/forms/Part.php
index 3609eb4..fdca8f5 100644
--- a/lib/classes/forms/Part.php
+++ b/lib/classes/forms/Part.php
@@ -235,6 +235,7 @@ abstract class Part
$attributes['type'],
$attributes['mapper'],
$attributes['store'],
+ $attributes['validate'],
$attributes['if'],
$attributes['permission'],
$attributes['required'],
@@ -257,6 +258,9 @@ abstract class Part
if (isset($data['store']) && is_callable($data['store'])) {
$input->store = $data['store'];
}
+ if (isset($data['validate']) && is_callable($data['validate'])) {
+ $input->validate = $data['validate'];
+ }
if (!empty($data['if'])) {
$input->if = $data['if'];
}
diff --git a/lib/classes/forms/PasswordInput.php b/lib/classes/forms/PasswordInput.php
new file mode 100644
index 0000000..68e4718
--- /dev/null
+++ b/lib/classes/forms/PasswordInput.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Studip\Forms;
+
+class PasswordInput extends Input
+{
+ public function render()
+ {
+ $template = $GLOBALS['template_factory']->open('forms/password_input');
+ $template->title = $this->title;
+ $template->name = $this->name;
+ $template->value = $this->value;
+ $template->id = md5(uniqid());
+ $template->required = $this->required;
+ $template->attributes = arrayToHtmlAttributes($this->attributes);
+ return $template->render();
+ }
+}
diff --git a/lib/classes/forms/RadioInput.php b/lib/classes/forms/RadioInput.php
new file mode 100644
index 0000000..1945d87
--- /dev/null
+++ b/lib/classes/forms/RadioInput.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Studip\Forms;
+
+class RadioInput extends Input
+{
+ public function render()
+ {
+ $options = $this->extractOptionsFromAttributes($this->attributes);
+ $template = $GLOBALS['template_factory']->open('forms/radio_input');
+ $template->title = $this->title;
+ $template->name = $this->name;
+ $template->value = $this->value;
+ $template->id = md5(uniqid());
+ $template->required = $this->required;
+ $template->options = $options;
+ $template->attributes = arrayToHtmlAttributes($this->attributes);
+ $template->orientation = $this->attributes['orientation'];
+ return $template->render();
+
+ }
+}
diff --git a/lib/classes/forms/RangeInput.php b/lib/classes/forms/RangeInput.php
index 9f99d59..7a69e9e 100644
--- a/lib/classes/forms/RangeInput.php
+++ b/lib/classes/forms/RangeInput.php
@@ -11,6 +11,9 @@ class RangeInput extends Input
$template->name = $this->name;
$template->value = $this->value;
$template->id = md5(uniqid());
+ $template->min = $this->attributes['min'];
+ $template->max = $this->attributes['max'];
+ $template->step = $this->attributes['step'];
$template->required = $this->required;
$template->attributes = arrayToHtmlAttributes($this->attributes);
return $template->render();
diff --git a/lib/navigation/LoginNavigation.php b/lib/navigation/LoginNavigation.php
index 96ec90e..bb63a03 100644
--- a/lib/navigation/LoginNavigation.php
+++ b/lib/navigation/LoginNavigation.php
@@ -43,9 +43,9 @@ class LoginNavigation extends Navigation
}
if (Config::get()->ENABLE_SELF_REGISTRATION) {
- $navigation = new Navigation(_('Registrieren'), 'register1.php');
+ $navigation = new Navigation(_('Registrieren'), 'dispatch.php/registration');
$navigation->setDescription(_('um das System erstmalig zu nutzen'));
- $this->addSubNavigation('register', $navigation);
+ $this->addSubNavigation('registration', $navigation);
}
if (Config::get()->ENABLE_FREE_ACCESS) {
diff --git a/public/register1.php b/public/register1.php
deleted file mode 100644
index 7b4740f..0000000
--- a/public/register1.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-/**
- * register1.php - Benutzerregistrierung in Stud.IP, Part I
- *
- * @author Stefan Suchi <suchi@gmx.de>
- * @author Oliver Brakel <obrakel@gwdg.de>
- * @copyright 2000 authors
- * @license GPL2 or any later version
- */
-
-require '../lib/bootstrap.php';
-
-page_open([
- 'sess' => 'Seminar_Session',
- 'auth' => 'Seminar_Default_Auth',
- 'perm' => 'Seminar_Perm',
- 'user' => 'Seminar_User',
-]);
-
-include 'lib/seminar_open.php'; // initialise Stud.IP-Session
-
-if (!Config::get()->ENABLE_SELF_REGISTRATION) {
- PageLayout::postError(_('Registrierung ausgeschaltet'), [
- _('In dieser Installation ist die Möglichkeit zur Registrierung ausgeschaltet.'),
- sprintf(
- '<a href="%s">%s</a>',
- URLHelper::getLink('index.php'),
- _('Hier geht es zur Startseite.')
- )
- ]);
-
- echo $GLOBALS['template_factory']->render('layouts/base.php', [
- 'content_for_layout' => '',
- ]);
-} elseif (Config::get()->SHOW_TERMS_ON_FIRST_LOGIN) {
- header('Location: ' . URLHelper::getURL('register2.php'));
-} elseif ($GLOBALS['auth']->is_authenticated() && $GLOBALS['user']->id !== 'nobody') {
- PageLayout::postError(_('Sie sind schon als BenutzerIn am System angemeldet!'), [
- sprintf(
- '<a href="%s">%s</a>',
- URLHelper::getLink('index.php'),
- _('Hier geht es zur Startseite.')
- )
- ]);
- echo $GLOBALS['template_factory']->render('layouts/base.php', [
- 'content_for_layout' => '',
- ]);
-} else {
- PageLayout::setHelpKeyword('Basis.AnmeldungRegistrierung');
- PageLayout::setTitle(_('Nutzungsbedingungen'));
- echo $GLOBALS['template_factory']->render(
- 'register/step1.php',
- [],
- $GLOBALS['template_factory']->open('layouts/base.php')
- );
- $auth->logout();
-}
-
-page_close();
diff --git a/public/register2.php b/public/register2.php
deleted file mode 100644
index 8d73c81..0000000
--- a/public/register2.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-/**
- * register2.php - Benutzerregistrierung in Stud.IP, Part II
- *
- * @author Stefan Suchi <suchi@gmx.de>
- * @author Oliver Brakel <obrakel@gwdg.de>
- * @copyright 2000 authors
- * @license GPL2 or any later version
- */
-
-require '../lib/bootstrap.php';
-
-page_open([
- 'sess' => 'Seminar_Session',
- 'auth' => Config::get()->ENABLE_SELF_REGISTRATION ? 'Seminar_Register_Auth' : 'Seminar_Default_Auth',
- 'perm' => 'Seminar_Perm',
- 'user' => 'Seminar_User',
-]);
-
-if (!Config::get()->ENABLE_SELF_REGISTRATION){
- PageLayout::postError(_('Registrierung ausgeschaltet'), [
- _('In dieser Installation ist die Möglichkeit zur Registrierung ausgeschaltet.'),
- sprintf(
- '<a href="%s">%s</a>',
- URLHelper::getLink('index.php'),
- _('Hier geht es zur Startseite.')
- )
- ]);
-
- echo $GLOBALS['template_factory']->render('layouts/base.php', [
- 'content_for_layout' => '',
- ]);
-} elseif ($GLOBALS['auth']->auth['uid'] === 'nobody') {
- $GLOBALS['auth']->logout();
- header('Location: ' . URLHelper::getURL('register2.php'));
-} else {
- include 'lib/seminar_open.php'; // initialise Stud.IP-Session
-
- PageLayout::setHelpKeyword('Basis.AnmeldungRegistrierung');
- PageLayout::setTitle(_('Registrierung erfolgreich'));
- echo $GLOBALS['template_factory']->render(
- 'register/success.php',
- [],
- $GLOBALS['template_factory']->open('layouts/base.php')
- );
- $GLOBALS['auth']->logout();
-}
-
-page_close();
diff --git a/resources/assets/javascripts/bootstrap/forms.js b/resources/assets/javascripts/bootstrap/forms.js
index b0e66d3..2cbac9e 100644
--- a/resources/assets/javascripts/bootstrap/forms.js
+++ b/resources/assets/javascripts/bootstrap/forms.js
@@ -250,12 +250,15 @@ STUDIP.ready(function () {
data() {
let params = JSON.parse(f.dataset.inputs);
params.STUDIPFORM_REQUIRED = f.dataset.required ? JSON.parse(f.dataset.required) : [];
+ params.STUDIPFORM_SERVERVALIDATION = f.dataset.server_validation > 0;
params.STUDIPFORM_DISPLAYVALIDATION = false;
params.STUDIPFORM_VALIDATIONNOTES = [];
params.STUDIPFORM_AUTOSAVEURL = f.dataset.autosave;
params.STUDIPFORM_REDIRECTURL = f.dataset.url;
- params.STUDIPFORM_SELECTEDLANGUAGES = {};
- params.STUDIPFORM_DEBUGMODE = JSON.parse(f.dataset.debugmode);
+ params.STUDIPFORM_INPUTS_ORDER = [];
+ for (let i in JSON.parse(f.dataset.inputs)) {
+ params.STUDIPFORM_INPUTS_ORDER.push(i);
+ }
return params;
},
methods: {
@@ -265,31 +268,31 @@ STUDIP.ready(function () {
this.STUDIPFORM_DISPLAYVALIDATION = true;
//validation:
- let validated = this.validate();
-
- if (!validated) {
- e.preventDefault();
- v.$el.scrollIntoView({
- "behavior": "smooth"
- });
- return;
- }
-
- if (this.STUDIPFORM_AUTOSAVEURL) {
- let params = this.getFormValues();
+ let validation_promise = this.validate();
+ validation_promise.then(function (validated) {
+ if (!validated) {
+ v.$el.scrollIntoView({
+ behavior: 'smooth'
+ });
+ return;
+ }
- $.ajax({
- url: this.STUDIPFORM_AUTOSAVEURL,
- data: params,
- type: 'post',
- success() {
- if (v.STUDIPFORM_REDIRECTURL && !v.STUDIPFORM_DEBUGMODE) {
- window.location.href = v.STUDIPFORM_REDIRECTURL;
+ if (v.STUDIPFORM_AUTOSAVEURL) {
+ let params = v.getFormValues();
+
+ $.ajax({
+ url: v.STUDIPFORM_AUTOSAVEURL,
+ data: params,
+ type: 'post',
+ success() {
+ if (v.STUDIPFORM_REDIRECTURL) {
+ window.location.href = v.STUDIPFORM_REDIRECTURL
+ }
}
- }
- });
- e.preventDefault();
- }
+ });
+ }
+ });
+ e.preventDefault();
},
getFormValues() {
let v = this;
@@ -311,32 +314,74 @@ STUDIP.ready(function () {
let v = this;
this.STUDIPFORM_VALIDATIONNOTES = [];
- let validated = this.$el.checkValidity();
-
- $(this.$el).find('input, select, textarea').each(function () {
- if (!this.validity.valid) {
- let note = {
- name: $(this.labels[0]).find('.textlabel').text(),
- description: $gettext('Fehler!'),
- describedby: this.id
- };
- if (this.validity.tooShort) {
- note.description = $gettextInterpolate(
- $gettext('Geben Sie mindestens %{min} Zeichen ein.'),
- {min: this.minLength}
- );
- }
- if (this.validity.valueMissing) {
- if (this.type === 'checkbox') {
- note.description = $gettext('Dieses Feld muss ausgewählt sein.');
- } else {
- note.description = $gettext('Hier muss ein Wert eingetragen werden.');
+ let validation_promise = new Promise(function (resolve, reject) {
+ let validated = v.$el.checkValidity();
+
+ $(v.$el).find('input, select, textarea').each(function () {
+ let name = $(this).attr('name');
+ if (!this.validity.valid) {
+ let note = {
+ name: this.name,
+ label: $(this.labels[0]).find('.textlabel').text(),
+ description: $gettext('Fehler!'),
+ describedby: this.id
+ };
+ if ($(this).data('validation_requirement')) {
+ note.description = $(this).data('validation_requirement');
+ }
+ if (this.validity.tooShort) {
+ note.description = $gettextInterpolate(
+ $gettext('Geben Sie mindestens %{min} Zeichen ein.'),
+ {min: this.minLength}
+ );
+ }
+ if (this.validity.valueMissing) {
+ if (this.type === 'checkbox') {
+ note.description = $gettext('Dieses Feld muss ausgewählt sein.');
+ } else {
+ if (this.minLength > 0) {
+ note.description = $gettextInterpolate(
+ $gettext('Hier muss ein Wert mit mindestens %{min} Zeichen eingetragen werden.'),
+ {min: this.minLength}
+ );
+ } else {
+ note.description = $gettext('Hier muss ein Wert eingetragen werden.');
+ }
+
+ }
}
+ v.STUDIPFORM_VALIDATIONNOTES.push(note);
}
- v.STUDIPFORM_VALIDATIONNOTES.push(note);
+ });
+ if (v.STUDIPFORM_SERVERVALIDATION) {
+
+ let params = v.getFormValues();
+ params.STUDIPFORM_SERVERVALIDATION = 1;
+
+ $.ajax({
+ url: v.STUDIPFORM_AUTOSAVEURL,
+ data: params,
+ type: 'post',
+ dataType: 'json',
+ success(output) {
+ for (let i in output) {
+ let note = {
+ name: output[i].name,
+ label: output[i].label,
+ description: output[i].error,
+ describedby: null
+ };
+ v.STUDIPFORM_VALIDATIONNOTES.push(note);
+ }
+ validated = v.STUDIPFORM_VALIDATIONNOTES.length < 1;
+ resolve(validated);
+ }
+ });
+ } else {
+ resolve(validated);
}
});
- return validated;
+ return validation_promise;
},
setInputs(inputs) {
for (const [key, value] of Object.entries(inputs)) {
@@ -353,6 +398,19 @@ STUDIP.ready(function () {
this.STUDIPFORM_SELECTEDLANGUAGES = languages;
}
},
+ computed: {
+ ordererValidationNotes: function () {
+ let orderedNotes = [];
+ for (let i in this.STUDIPFORM_INPUTS_ORDER) {
+ for (let k in this.STUDIPFORM_VALIDATIONNOTES) {
+ if (this.STUDIPFORM_VALIDATIONNOTES[k].name === this.STUDIPFORM_INPUTS_ORDER[i]) {
+ orderedNotes.push(this.STUDIPFORM_VALIDATIONNOTES[k]);
+ }
+ }
+ }
+ return orderedNotes;
+ }
+ },
mounted () {
$(this.$el).addClass("vueified");
}
@@ -362,7 +420,7 @@ STUDIP.ready(function () {
}
// Well, this is really nasty: Select2 can't determine the select
- // element's width if it is hidden (by itself or by it's parent).
+ // element's width if it is hidden (by itself or by its parent).
// This is due to the fact that elements are not rendered when hidden
// (which seems pretty obvious when you think about it) but elements
// only have a width when they are rendered (pretty obvious as well).
diff --git a/resources/assets/stylesheets/scss/forms.scss b/resources/assets/stylesheets/scss/forms.scss
index 72e2d26..0b43f04 100644
--- a/resources/assets/stylesheets/scss/forms.scss
+++ b/resources/assets/stylesheets/scss/forms.scss
@@ -70,6 +70,14 @@ form.default {
}
}
+ input[list] {
+ @include background-icon(arr_1down, clickable);
+
+ background-repeat: no-repeat;
+ background-position: center right 4px;
+ padding-right: 24px
+ }
+
textarea:not(.size-l) + .ck-editor {
max-width: $max-width-m;
}
diff --git a/templates/forms/confirm_password_input.php b/templates/forms/confirm_password_input.php
new file mode 100644
index 0000000..e2e2583
--- /dev/null
+++ b/templates/forms/confirm_password_input.php
@@ -0,0 +1,16 @@
+<div class="formpart">
+ <label <?= $this->required ? 'class="studiprequired"' : '' ?> for="<?= $id ?>">
+ <span class="textlabel">
+ <?= htmlReady($this->title) ?>
+ </span>
+ <? if ($this->required) : ?>
+ <span class="asterisk" title="<?= _('Dies ist ein Pflichtfeld') ?>" aria-hidden="true">*</span>
+ <? endif ?>
+ <input type="password"
+ v-model="<?= htmlReady($this->name) ?>"
+ name="<?= htmlReady($this->name) ?>"
+ value="<?= htmlReady($this->value) ?>"
+ id="<?= $id ?>" <?= $this->required ? 'required aria-required="true"' : '' ?>
+ <?= $attributes ?>>
+ </label>
+</div>
diff --git a/templates/forms/datalist_input.php b/templates/forms/datalist_input.php
new file mode 100644
index 0000000..7958c33
--- /dev/null
+++ b/templates/forms/datalist_input.php
@@ -0,0 +1,22 @@
+<div class="formpart">
+ <label <?= $this->required ? 'class="studiprequired"' : '' ?> for="<?= $id ?>">
+ <span class="textlabel">
+ <?= htmlReady($this->title) ?>
+ </span>
+ <? if ($this->required) : ?>
+ <span class="asterisk" title="<?= _('Dies ist ein Pflichtfeld') ?>" aria-hidden="true">*</span>
+ <? endif ?>
+
+ <input type="text" list="<?= $this->title ?>" id="" v-model="<?= htmlReady($this->name) ?>" <?= $this->required ? 'required aria-required="true"' : '' ?> />
+
+ <datalist class="" id="<?= $this->title ?>" <?= $attributes ?>>
+ <? foreach ($options as $key => $option) : ?>
+ <option value="<?= htmlReady($option) ?>"<?= ($option == $value ? " selected" : "") ?>>
+ </option>
+ <? endforeach ?>
+ </datalist>
+ </label>
+
+</div>
+
+
diff --git a/templates/forms/form.php b/templates/forms/form.php
index 05b0b30..96cd2c0 100644
--- a/templates/forms/form.php
+++ b/templates/forms/form.php
@@ -2,14 +2,17 @@
$inputs = [];
$allinputs = $form->getAllInputs();
$required_inputs = [];
+$server_validation = false;
foreach ($allinputs as $input) {
foreach ($input->getAllInputNames() as $name) {
$inputs[$name] = $input->getValue();
}
-
if ($input->required) {
$required_inputs[] = $input->getName();
}
+ if ($input->hasValidation()) {
+ $server_validation = true;
+ }
}
$form_id = md5(uniqid());
?><form v-cloak
@@ -21,12 +24,14 @@ $form_id = md5(uniqid());
data-url="<?= htmlReady($form->getURL()) ?>"
<? endif ?>
@submit="submit"
+ @cancel=""
novalidate
<?= $form->getDataSecure() ? 'data-secure' : '' ?>
id="<?= htmlReady($form_id) ?>"
data-inputs="<?= htmlReady(json_encode($inputs)) ?>"
data-debugmode="<?= htmlReady(json_encode($form->getDebugMode())) ?>"
data-required="<?= htmlReady(json_encode($required_inputs)) ?>"
+ data-server_validation="<?= $server_validation ? 1 : 0?>"
class="default studipform<?= $form->isCollapsable() ? ' collapsable' : '' ?>">
<?= CSRFProtection::tokenTag(['ref' => 'securityToken']) ?>
@@ -52,7 +57,7 @@ $form_id = md5(uniqid());
<div v-if="STUDIPFORM_DISPLAYVALIDATION && (STUDIPFORM_VALIDATIONNOTES.length > 0)">
<?= _('Folgende Angaben müssen korrigiert werden, um das Formular abschicken zu können:') ?>
<ul>
- <li v-for="note in STUDIPFORM_VALIDATIONNOTES" :aria-describedby="note.describedby">{{ note.name + ": " + note.description }}</li>
+ <li v-for="note in ordererValidationNotes" :aria-describedby="note.describedby">{{ note.label.trim() + ": " + note.description }}</li>
</ul>
</div>
</article>
@@ -64,7 +69,8 @@ $form_id = md5(uniqid());
</div>
<? if (!Request::isDialog()) : ?>
<footer>
- <?= \Studip\Button::create($form->getSaveButtonText(), $form->getSaveButtonName(), ['form' => $form_id]) ?>
+ <?= \Studip\Button::createAccept($form->getSaveButtonText(), $form->getSaveButtonName(), ['form' => $form_id]) ?>
+ <?= \Studip\LinkButton::createCancel($form->getCancelButtonText(), $form->getCancelButtonName()) ?>
</footer>
<? endif ?>
</form>
diff --git a/templates/forms/password_input.php b/templates/forms/password_input.php
new file mode 100644
index 0000000..e2e2583
--- /dev/null
+++ b/templates/forms/password_input.php
@@ -0,0 +1,16 @@
+<div class="formpart">
+ <label <?= $this->required ? 'class="studiprequired"' : '' ?> for="<?= $id ?>">
+ <span class="textlabel">
+ <?= htmlReady($this->title) ?>
+ </span>
+ <? if ($this->required) : ?>
+ <span class="asterisk" title="<?= _('Dies ist ein Pflichtfeld') ?>" aria-hidden="true">*</span>
+ <? endif ?>
+ <input type="password"
+ v-model="<?= htmlReady($this->name) ?>"
+ name="<?= htmlReady($this->name) ?>"
+ value="<?= htmlReady($this->value) ?>"
+ id="<?= $id ?>" <?= $this->required ? 'required aria-required="true"' : '' ?>
+ <?= $attributes ?>>
+ </label>
+</div>
diff --git a/templates/forms/radio_input.php b/templates/forms/radio_input.php
new file mode 100644
index 0000000..37170c9
--- /dev/null
+++ b/templates/forms/radio_input.php
@@ -0,0 +1,16 @@
+<div class="formpart">
+ <section <?= $this->orientation == 'horizontal' ? 'class="hgroup"' : '' ?> for="<?= $id ?>">
+ <span class="textlabel">
+ <?= htmlReady($this->title) ?>
+ </span>
+
+ <? foreach ($options as $key => $option) : ?>
+ <label class="" <?= $attributes ?>>
+ <input type="radio"
+ v-model="<?= htmlReady($this->name) ?>"
+ value="<?= htmlReady($key) ?>" <?= $key == $value ? 'checked' : '' ?>>
+ <?= htmlReady($option) ?>
+ </label>
+ <? endforeach ?>
+</section>
+</div>
diff --git a/templates/forms/range_input.php b/templates/forms/range_input.php
index c85b02d..07bd934 100644
--- a/templates/forms/range_input.php
+++ b/templates/forms/range_input.php
@@ -10,4 +10,7 @@
name="<?= htmlReady($name) ?>"
value="<?= htmlReady($value) ?>"
id="<?= $id ?>"
- <?= $attributes ?>></range-input>
+ min="<?= $min ?>"
+ max="<?= $max ?>"
+ step="<?= $step ?>"
+ <?= $attributes ?>></range-input>
diff --git a/templates/forms/text_input.php b/templates/forms/text_input.php
index ae93758..3104ee8 100644
--- a/templates/forms/text_input.php
+++ b/templates/forms/text_input.php
@@ -6,11 +6,12 @@
<? if ($this->required) : ?>
<span class="asterisk" title="<?= _('Dies ist ein Pflichtfeld') ?>" aria-hidden="true">*</span>
<? endif ?>
+ <input type="text"
+ v-model="<?= htmlReady($this->name) ?>"
+ name="<?= htmlReady($this->name) ?>"
+ value="<?= htmlReady($this->value) ?>"
+ id="<?= $id ?>" <?= ($this->required ? 'required aria-required="true"' : '') ?>
+ <?= $attributes ?>>
</label>
- <input type="text"
- v-model="<?= htmlReady($this->name) ?>"
- name="<?= htmlReady($this->name) ?>"
- value="<?= htmlReady($this->value) ?>"
- id="<?= $id ?>" <?= ($this->required ? 'required aria-required="true"' : '') ?>
- <?= $attributes ?>>
+
</div>
diff --git a/templates/register/form.php b/templates/register/form.php
deleted file mode 100644
index 137cd05..0000000
--- a/templates/register/form.php
+++ /dev/null
@@ -1,175 +0,0 @@
-<?php
-use Studip\Button, Studip\LinkButton;
-
-$email_restriction = Config::get()->EMAIL_DOMAIN_RESTRICTION;
-?>
-<script type="text/javascript" language="javaScript">
-jQuery(document).ready(function() {
- STUDIP.register.re_username = <?= $validator->username_regular_expression ?>;
- STUDIP.register.re_name = <?= $validator->name_regular_expression ?>;
-
- $('form[name=login]').submit(function () {
- return STUDIP.register.checkdata();
- });
-});
-</script>
-
-<? if (isset($username)): ?>
- <?= MessageBox::error(_("Bei der Registrierung ist ein Fehler aufgetreten!"), [$error_msg, _("Bitte korrigieren Sie Ihre Eingaben und versuchen Sie es erneut")]) ?>
-<? endif; ?>
-
-<h1><?= _('Stud.IP - Registrierung') ?></h1>
-
-<form name="login" action="<?= URLHelper::getLink() ?>" method="post" class="default">
- <?= CSRFProtection::tokenTag() ?>
- <input type="hidden" name="login_ticket" value="<?= Seminar_Session::get_ticket() ?>">
-
- <fieldset>
- <legend><?= _('Herzlich willkommen!') ?></legend>
-
- <p><?= _('Bitte füllen Sie zur Anmeldung das Formular aus:') ?></p>
-
- <label for="username">
- <em class="required"><?= _('Benutzername') ?></em>
- <input type="text" name="username" id="username"
- onchange="STUDIP.register.checkusername()"
- value="<?= htmlReady($username) ?>"
- autofocus
- required maxlength="63"
- autocapitalize="off" autocorrect="off">
- </label>
-
- <label for="password">
- <em class="required"><?= _('Passwort') ?></em>
- <input type="password" name="password" id="password"
- onchange="STUDIP.register.checkpassword()"
- required maxlength="31">
- </label>
-
- <label for="password2">
- <em class="required"><?= _('Passwortbestätigung') ?></em>
- <input type="password" name="password2" id="password2"
- onchange="STUDIP.register.checkpassword2()"
- required maxlength="31">
- </label>
-
- <label for="title_front">
- <?= _('Titel') ?>
- </label>
- <section class="hgroup size-m">
- <select name="title_chooser_front" data-copy-to="#title_front" class="size-s">
- <? foreach ($GLOBALS['TITLE_FRONT_TEMPLATE'] as $template): ?>
- <option <? if ($template === $title_front) echo 'selected'; ?>>
- <?= htmlReady($template) ?>
- </option>
- <? endforeach; ?>
- </select>
-
- <input type="text" name="title_front" id="title_front"
- value="<?= htmlReady($title_front) ?>"
- maxlength="63" class="no-hint">
- </section>
-
- <label for="title_rear">
- <?= _('Titel nachgestellt') ?>
- </label>
- <section class="hgroup size-m">
- <select name="title_chooser_rear" data-copy-to="#title_rear" class="size-s">
- <? foreach ($GLOBALS['TITLE_REAR_TEMPLATE'] as $template): ?>
- <option <? if ($template === $title_rear) echo 'selected'; ?>>
- <?= htmlReady($template) ?>
- </option>
- <? endforeach; ?>
- </select>
-
- <input type="text" name="title_rear" id="title_rear"
- value="<?= htmlReady($title_rear) ?>"
- maxlength="63" class="no-hint">
- </section>
-
- <label for="first_name">
- <em class="required"><?= _('Vorname') ?></em>
-
- <input type="text" name="Vorname" id="first_name"
- onchange="STUDIP.register.checkVorname()"
- value="<?= htmlReady($Vorname) ?>"
- required maxlength="63">
- </label>
-
- <label for="last_name">
- <em class="required"><?= _('Nachname') ?></em>
-
- <input type="text" name="Nachname" id="last_name"
- onchange="STUDIP.register.checkNachname()"
- value="<?= htmlReady($Nachname) ?>"
- required maxlength="63">
- </label>
-
- <div>
- <?= _('Geschlecht') ?>
- </div>
-
- <section class="hgroup" id="gender">
- <label>
- <input type="radio" name="geschlecht" value="0"
- <? if (!$geschlecht) echo 'checked' ?>>
- <?= _('unbekannt') ?>
- </label>
-
- <label>
- <input type="radio" name="geschlecht" value="1"
- <? if ($geschlecht == 1) echo "checked" ?>>
- <?= _('männlich') ?>
- </label>
-
- <label>
- <input type="radio" name="geschlecht" value="2"
- <? if ($geschlecht == 2) echo "checked" ?>>
- <?= _('weiblich') ?>
- </label>
-
- <label>
- <input type="radio" name="geschlecht" value="3"
- <? if ($geschlecht == 3) echo "checked" ?>>
- <?= _('divers') ?>
- </label>
- </section>
-
-
- <label for="email">
- <em class="required"><?= _('E-Mail') ?></em>
- <? if (!trim($email_restriction)): ?>
- <input type="email" name="Email" id="email"
- onchange="STUDIP.register.checkEmail()"
- value="<?= htmlReady(trim($Email)) ?>"
- required maxlength="63">
- <? endif; ?>
- </label>
-
- <? if (trim($email_restriction)): ?>
- <section class="hgroup size-m">
- <input type="text" name="Email" id="email"
- onchange="STUDIP.register.checkEmail()"
- value="<?= htmlReady(preg_replace('/@.*$/', '', trim($Email ?: ''))) ?>"
- required maxlength="63"
- class="no-hint">
- <select name="emaildomain">
- <? foreach (explode(',', $email_restriction) as $domain): ?>
- <option value="<?= trim($domain) ?>"
- <? if (trim($domain) == Request::get('emaildomain')) echo 'selected'; ?>>
- @<?= trim($domain) ?>
- </option>
- <? endforeach; ?>
- </select>
- </section>
- <? endif; ?>
- </fieldset>
-
- <footer>
- <?= Button::createAccept(_('Registrieren'))?>
- <?= LinkButton::createCancel(
- _('Registrierung abbrechen'),
- URLHelper::getURL('index.php?cancel_login=1')
- ) ?>
- </footer>
-</form>
diff --git a/templates/register/step1.php b/templates/register/step1.php
deleted file mode 100644
index b67116e..0000000
--- a/templates/register/step1.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?= $GLOBALS['template_factory']->render('terms.php') ?>
-
-<footer style="text-align: center">
- <?= Studip\LinkButton::createAccept(_('Ich erkenne die Nutzungsbedingungen an'), URLHelper::getLink('register2.php')) ?>
- <?= Studip\LinkButton::createCancel(_('Registrierung abbrechen'), URLHelper::getLink('index.php')) ?>
-</footer>