Compare commits

16 Commits

Author SHA1 Message Date
dd83e0de95 upd: docker
update docker-compose and readme with updated docker image
2025-10-10 17:15:00 -04:00
f3b91ac1d7 docker: install
Update docker info
2025-10-08 21:13:17 -04:00
205fa5f3a1 {type}: {component}
{summary}
2025-10-08 21:11:48 -04:00
5b284cc107 {type}: {component}
{summary}
2025-10-08 21:11:10 -04:00
9c682ea58d upd: registration
- added dd return if fail to register
2025-08-11 13:02:34 -04:00
24a0892160 fix: index
- fixed problem with files not pulling from server correct with MIME types
2025-08-11 13:00:55 -04:00
50f62535e6 fix: registration
- fixed account registration problem
2025-08-11 12:59:57 -04:00
abd2d93c90 upd: index
- simplified link to return home if logged in
2025-08-11 12:59:21 -04:00
3fdb7d4e09 upd: home
- update to pull sorted speaker and series
2025-08-11 12:58:11 -04:00
c822de468c upd: DefaultController
- add get for speakers and series sorted by name
2025-08-11 12:57:34 -04:00
f1fceff25a fix: dirty text
- add check for backspace key
2025-08-11 12:53:42 -04:00
f6b4aeea83 upd: scripts
- add check for escape key and input validation for new speaker and series
2025-08-11 12:53:03 -04:00
44a8a72219 upd: home
Add input-error style for validation feedback
2025-06-03 13:01:47 -04:00
c933e6b91b upd: registration
Add CSRF protection to registration form
2025-06-03 13:00:34 -04:00
573b8b1d26 upd: notes
add validation feedback if user doesn't fill out all required fields.
2025-06-03 12:58:42 -04:00
1aed314ae3 fix: registration
Fix a couple typos in registration JS
2025-06-03 12:44:03 -04:00
16 changed files with 141 additions and 106 deletions

3
.gitmessage Normal file
View File

@@ -0,0 +1,3 @@
{type}: {component}
{summary}

View File

@@ -49,7 +49,7 @@ COPY data/data.db /data/data.db
RUN mkdir /var/www/html/var/cache RUN mkdir /var/www/html/var/cache
RUN mkdir /var/www/html/var/log RUN mkdir /var/www/html/var/log
RUN symfony console asset-map:compile #RUN symfony console asset-map:compile
RUN chown -R 33:33 /var/www/html /data RUN chown -R 33:33 /var/www/html /data
RUN chmod -R 755 /var/www/html /data RUN chmod -R 755 /var/www/html /data

View File

@@ -19,7 +19,7 @@ This was my first publicly available docker container so I did not realize what
1. Make a directory in your desired docker storage folder (e.g. `~/docker/sermon-notes`), then `cd` into it. 1. Make a directory in your desired docker storage folder (e.g. `~/docker/sermon-notes`), then `cd` into it.
2. Create a file called `.env` in that folder, no need to add anything to it right now. 2. Create a file called `.env` in that folder, no need to add anything to it right now.
3. Run `docker run -d --name sermon-notes -p 80:80 -v $PWD/data:/data -v $PWD/.env:/var/www/html/.env ryanprather/sermon-notes:latest`, this will download and start the container and keep it running in the background. If you already have something on port 80 change the first `80` to whatever open port you'd like. 3. Run `docker run -d --name sermon-notes -p 80:80 -v $PWD/data:/data -v $PWD/.env:/var/www/html/.env gitea.rkprather.com/ryan/sermon-notes:latest`, this will download and start the container and keep it running in the background. If you already have something on port 80 change the first `80` to whatever open port you'd like.
4. Run `docker exec -it sermon-notes bash install.sh` This will run an install script to create an .env file specific to your install, populate with the beginning factors, and then run a `composer` command to download the necessary package dependancies. 4. Run `docker exec -it sermon-notes bash install.sh` This will run an install script to create an .env file specific to your install, populate with the beginning factors, and then run a `composer` command to download the necessary package dependancies.
5. Once complete you have a running system that you can navigate to in your browser with `http://{ip}:{port}`|`http://{hostname}:{port}`. Then you just need to register for an account. The first account that is created is made an admin so that you can access the `Reference Editor` and update any reference material if necessary. 5. Once complete you have a running system that you can navigate to in your browser with `http://{ip}:{port}`|`http://{hostname}:{port}`. Then you just need to register for an account. The first account that is created is made an admin so that you can access the `Reference Editor` and update any reference material if necessary.

View File

@@ -1,8 +1,7 @@
// Get references to the form elements // Get references to the form elements
const nameInput = document.getElementById("name"); const nameInput = document.getElementById("registration_form_name");
const emailInput = document.getElementById("emailAddress"); const emailInput = document.getElementById("registration_form_email");
const passwordInput = document.getElementById("password"); const passwordInput = document.getElementById("registration_form_plainPassword");
const confirmPasswordInput = document.getElementById("confirmPassword");
const csrfToken = document.getElementById("registration_form__token").value; const csrfToken = document.getElementById("registration_form__token").value;
// Add event listeners to the form // Add event listeners to the form
@@ -18,25 +17,18 @@ function handleSubmit(event) {
const name = nameInput.value; const name = nameInput.value;
const email = emailInput.value; const email = emailInput.value;
const password = passwordInput.value; const password = passwordInput.value;
const confirmPassword = confirmPasswordInput.value;
if (name === "" || email === "" || password === "") { if (name === "" || email === "" || password === "") {
alert("Please fill in all fields."); alert("Please fill in all fields.");
return; return;
} }
if (password !== confirmPassword) {
alert("Passwords do not match.");
return;
}
// Send data to server for processing // Send data to server for processing
const data = { const data = {
"name": name, "name": name,
"email": email, "email": email,
"password": password,
"plainPassword": password, "plainPassword": password,
"csrf_token": csrfToken "_token": csrfToken
}; };
fetch("/register", { fetch("/register", {

View File

@@ -95,6 +95,13 @@ function saveNote(event) {
event.preventDefault(); event.preventDefault();
} }
document.querySelector('#noteTitle').classList.remove('input-error');
document.querySelector('#noteDate').classList.remove('input-error');
document.querySelector('#speaker').classList.remove('input-error');
document.querySelector('#series').classList.remove('input-error');
document.querySelector('#passage').classList.remove('input-error');
document.querySelector('#notes').classList.remove('input-error');
if (!textDirty || !validateNote()) { if (!textDirty || !validateNote()) {
clearTimeout(to); clearTimeout(to);
to = setTimeout(saveNote, saveInterval); to = setTimeout(saveNote, saveInterval);
@@ -174,14 +181,20 @@ function validateNote() {
const title = document.querySelector('#noteTitle'); const title = document.querySelector('#noteTitle');
const psg = document.querySelector('#passage'); const psg = document.querySelector('#passage');
if (!title.value.length) { return false; } let ret = true;
if (!date.value) { return false; }
if (!parseInt(speaker.value)) { return false; }
if (!parseInt(series.value)) { return false; }
if (!psg.value) { return false; }
if (!note.value.length) { return false; }
return true; if (!title.value.length) { title.classList.add('input-error'); ret = false; }
if (!date.value) { date.classList.add('input-error'); ret = false; }
if (!parseInt(speaker.value)) { speaker.classList.add('input-error'); ret = false; }
if (!parseInt(series.value)) { series.classList.add('input-error'); ret = false; }
if (!psg.value) { psg.classList.add('input-error'); ret = false; }
if (!note.value.length) { note.classList.add('input-error'); ret = false; }
if (!ret) {
toggleFields(true);
}
return ret;
} }
/** /**
@@ -292,7 +305,12 @@ function newSpeaker() {
* @return {Promise} A Promise that resolves with the results of the fetch request. * @return {Promise} A Promise that resolves with the results of the fetch request.
*/ */
function saveSpeaker(event) { function saveSpeaker(event) {
if (event.keyCode == 13) { if (event.keyCode == 27) {
document.querySelector('#newSpeaker').style.display = 'none';
document.querySelector('#speaker').style.display = 'inline-block';
document.querySelector('#speaker').value = 0;
}
if (event.keyCode == 13 && document.querySelector('#newSpeaker').value != '') {
fetch('/save-speaker', { fetch('/save-speaker', {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -339,7 +357,12 @@ function newSeries() {
* @return {Promise} A Promise that resolves with the response from the server. * @return {Promise} A Promise that resolves with the response from the server.
*/ */
function saveSeries(event) { function saveSeries(event) {
if (event.keyCode == 13) { if (event.keyCode == 27) {
document.querySelector('#newSeries').style.display = 'none';
document.querySelector('#series').style.display = 'inline-block';
document.querySelector('#series').value = 0;
}
if (event.keyCode == 13 && document.querySelector('#newSeries').value != '') {
fetch('/save-series', { fetch('/save-series', {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -506,20 +529,21 @@ function removeActiveRef() {
/** /**
* Toggles the visibility of the fields container and updates the active state of the show/hide button. * Toggles the visibility of the fields container and updates the active state of the show/hide button.
* *
* @param boolean show
* @return {void} * @return {void}
*/ */
function toggleFields() { function toggleFields(show = false) {
const fieldsContainer = document.getElementById('fields-container'); const fieldsContainer = document.getElementById('fields-container');
const showHideBtn = document.getElementById('show-hide-btn'); const showHideBtn = document.getElementById('show-hide-btn');
if (fieldsContainer.classList.contains('show')) { if (show || !fieldsContainer.classList.contains('show')) {
fieldsContainer.classList.remove('show');
fieldsContainer.style.display = 'none';
showHideBtn.classList.remove('active');
} else {
fieldsContainer.classList.add('show'); fieldsContainer.classList.add('show');
fieldsContainer.style.display = 'block'; fieldsContainer.style.display = 'block';
showHideBtn.classList.add('active'); showHideBtn.classList.add('active');
} else {
fieldsContainer.classList.remove('show');
fieldsContainer.style.display = 'none';
showHideBtn.classList.remove('active');
} }
setHeight(); setHeight();

View File

@@ -58,7 +58,7 @@ export function setEventListeners() {
document.querySelector('#notes').addEventListener('keyup', function (event) { document.querySelector('#notes').addEventListener('keyup', function (event) {
let key = event.keyCode; let key = event.keyCode;
if (key >= 48 && key <= 90 || key >= 96 && key <= 111 || key >= 186 && key <= 222) { if (key == 8 || key >= 48 && key <= 90 || key >= 96 && key <= 111 || key >= 186 && key <= 222) {
textDirty = true; textDirty = true;
document.querySelector('#note-header-left h2').classList.add('dirty'); document.querySelector('#note-header-left h2').classList.add('dirty');
} }

View File

@@ -15,37 +15,37 @@
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"phpdocumentor/reflection-docblock": "^5.4", "phpdocumentor/reflection-docblock": "^5.4",
"phpstan/phpdoc-parser": "^1.28", "phpstan/phpdoc-parser": "^1.28",
"symfony/asset": "7.2.*", "symfony/asset": "7.3.*",
"symfony/asset-mapper": "7.2.*", "symfony/asset-mapper": "7.3.*",
"symfony/console": "7.2.*", "symfony/console": "7.3.*",
"symfony/debug-bundle": "7.2.*", "symfony/debug-bundle": "7.3.*",
"symfony/doctrine-messenger": "7.2.*", "symfony/doctrine-messenger": "7.3.*",
"symfony/dotenv": "7.2.*", "symfony/dotenv": "7.3.*",
"symfony/expression-language": "7.2.*", "symfony/expression-language": "7.3.*",
"symfony/flex": "^2", "symfony/flex": "^2",
"symfony/form": "7.2.*", "symfony/form": "7.3.*",
"symfony/framework-bundle": "7.2.*", "symfony/framework-bundle": "7.3.*",
"symfony/http-client": "7.2.*", "symfony/http-client": "7.3.*",
"symfony/intl": "7.2.*", "symfony/intl": "7.3.*",
"symfony/mailer": "7.2.*", "symfony/mailer": "7.3.*",
"symfony/mime": "7.2.*", "symfony/mime": "7.3.*",
"symfony/monolog-bundle": "^3.0", "symfony/monolog-bundle": "^3.0",
"symfony/notifier": "7.2.*", "symfony/notifier": "7.3.*",
"symfony/process": "7.2.*", "symfony/process": "7.3.*",
"symfony/property-access": "7.2.*", "symfony/property-access": "7.3.*",
"symfony/property-info": "7.2.*", "symfony/property-info": "7.3.*",
"symfony/runtime": "7.2.*", "symfony/runtime": "7.3.*",
"symfony/security-bundle": "7.2.*", "symfony/security-bundle": "7.3.*",
"symfony/serializer": "7.2.*", "symfony/serializer": "7.3.*",
"symfony/stimulus-bundle": "^2.17", "symfony/stimulus-bundle": "^2.17",
"symfony/string": "7.2.*", "symfony/string": "7.3.*",
"symfony/translation": "7.2.*", "symfony/translation": "7.3.*",
"symfony/twig-bundle": "7.2.*", "symfony/twig-bundle": "7.3.*",
"symfony/uid": "7.2.*", "symfony/uid": "7.3.*",
"symfony/ux-turbo": "^2.17", "symfony/ux-turbo": "^2.17",
"symfony/validator": "7.2.*", "symfony/validator": "7.3.*",
"symfony/web-link": "7.2.*", "symfony/web-link": "7.3.*",
"symfony/yaml": "7.2.*", "symfony/yaml": "7.3.*",
"twig/extra-bundle": "^2.12|^3.0", "twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0" "twig/twig": "^2.12|^3.0"
}, },
@@ -96,16 +96,16 @@
"extra": { "extra": {
"symfony": { "symfony": {
"allow-contrib": false, "allow-contrib": false,
"require": "7.2.*" "require": "7.3.*"
} }
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.5",
"symfony/browser-kit": "7.2.*", "symfony/browser-kit": "7.3.*",
"symfony/css-selector": "7.2.*", "symfony/css-selector": "7.3.*",
"symfony/maker-bundle": "^1.0", "symfony/maker-bundle": "^1.0",
"symfony/phpunit-bridge": "^7.2", "symfony/phpunit-bridge": "^7.2",
"symfony/stopwatch": "7.2.*", "symfony/stopwatch": "7.3.*",
"symfony/web-profiler-bundle": "7.2.*" "symfony/web-profiler-bundle": "7.3.*"
} }
} }

View File

@@ -1,10 +1,10 @@
services: services:
sermon-notes: sermon-notes:
container_name: sermon-notes container_name: sermon-notes
image: ryanprather/sermon-notes:latest image: gitea.rkprather.com/ryan/sermon-notes:1.1
ports: ports:
- 80:80 - 80:80
volumes: volumes:
- $PWD/data:/data - ${PWD}/data:/data
- $PWD/.env:/var/www/html/.env - ${PWD}/.env:/var/www/html/.env
restart: unless-stopped restart: unless-stopped

View File

@@ -4,7 +4,6 @@ if [ ! -f /var/www/html/.env ]; then
exit 0 exit 0
fi fi
echo "APP_ENV=prod" > .env echo "APP_ENV=prod" > .env
echo "APP_DEBUG=0" >> .env echo "APP_DEBUG=0" >> .env
@@ -13,10 +12,11 @@ LENGTH=32
SECRET_KEY=$(openssl rand -base64 $LENGTH | tr -d '=' | tr -d '+' | tr -d '/' | tr -d ' ') SECRET_KEY=$(openssl rand -base64 $LENGTH | tr -d '=' | tr -d '+' | tr -d '/' | tr -d ' ')
TRIMMED_KEY=$(cut -c1-32 <<< $SECRET_KEY) TRIMMED_KEY=$(cut -c1-32 <<< $SECRET_KEY)
echo "APP_SECRET=$TRIMMED_KEY" >> .env echo "APP_SECRET=$TRIMMED_KEY" >> .env
echo "DATABASE_URL=\"sqlite:///data/data.db\"" >> .env echo "DATABASE_URL=\"sqlite:////data/data.db\"" >> .env
echo "MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0" >> .env echo "MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0" >> .env
COMPOSER_ALLOW_SUPERUSER=1 composer update COMPOSER_ALLOW_SUPERUSER=1 composer update
symfony console asset-map:compile
symfony console doctrine:migrations:migrate --no-interaction symfony console doctrine:migrations:migrate --no-interaction
chown -R www-data:www-data /data chown -R www-data:www-data /data

View File

@@ -3,6 +3,35 @@
use App\Kernel; use App\Kernel;
if (file_exists(__DIR__.$_SERVER['REQUEST_URI']) && is_readable(__DIR__.$_SERVER['REQUEST_URI']) && is_file(__DIR__.$_SERVER['REQUEST_URI'])) { if (file_exists(__DIR__.$_SERVER['REQUEST_URI']) && is_readable(__DIR__.$_SERVER['REQUEST_URI']) && is_file(__DIR__.$_SERVER['REQUEST_URI'])) {
$header = 'text/html';
if (substr($_SERVER['REQUEST_URI'], -4) == '.css')
$header = 'text/css';
elseif (substr($_SERVER['REQUEST_URI'], -3) == '.js')
$header = 'text/javascript';
elseif (substr($_SERVER['REQUEST_URI'], -4) == '.ico')
$header = 'image/x-icon';
elseif (substr($_SERVER['REQUEST_URI'], -4) == '.png')
$header = 'image/png';
elseif (substr($_SERVER['REQUEST_URI'], -4) == '.jpg')
$header = 'image/jpeg';
elseif (substr($_SERVER['REQUEST_URI'], -4) == '.gif')
$header = 'image/gif';
elseif (substr($_SERVER['REQUEST_URI'], -3) == '.wo' || substr($_SERVER['REQUEST_URI'], -6) == '.woff2')
$header = 'font/woff2';
elseif (substr($_SERVER['REQUEST_URI'], -4) == '.ttf')
$header = 'font/truetype';
elseif (substr($_SERVER['REQUEST_URI'], -4) == '.eot' || substr($_SERVER['REQUEST_URI'], -5) == '.ttf-')
$header = 'application/vnd.ms-fontobject';
elseif (substr($_SERVER['REQUEST_URI'], -4) == '.svg')
$header = 'image/svg+xml';
elseif (substr($_SERVER['REQUEST_URI'], -5) == '.json')
$header = 'application/json';
elseif (substr($_SERVER['REQUEST_URI'], -3) == '.otf')
$header = 'font/opentype';
elseif (substr($_SERVER['REQUEST_URI'], -5) == '.woff')
$header = 'font/woff';
print header("Content-Type: $header; charset=UTF-8");
print file_get_contents(__DIR__.$_SERVER['REQUEST_URI']); print file_get_contents(__DIR__.$_SERVER['REQUEST_URI']);
exit; exit;
} }

View File

@@ -4,6 +4,8 @@ namespace App\Controller;
use App\Entity\Note; use App\Entity\Note;
use App\Entity\User; use App\Entity\User;
use App\Entity\Speaker;
use App\Entity\Series;
use App\Entity\SharedNote; use App\Entity\SharedNote;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -30,6 +32,8 @@ class DefaultController extends AbstractController
$last4Notes = $emi->getRepository(Note::class)->getLast4Notes($user); $last4Notes = $emi->getRepository(Note::class)->getLast4Notes($user);
$openNotes = $emi->getRepository(Note::class)->reverseNoteSort($user); $openNotes = $emi->getRepository(Note::class)->reverseNoteSort($user);
$meta = $user->getMetaData(); $meta = $user->getMetaData();
$speakers = $emi->getRepository(Speaker::class)->findBy(['user' => $user], ['name' => 'ASC']);
$series = $emi->getRepository(Series::class)->findBy(['user' => $user], ['name' => 'ASC']);
return $this->render('default/home.html.twig', [ return $this->render('default/home.html.twig', [
//'onLoad' => 'initHome()', //'onLoad' => 'initHome()',
@@ -37,6 +41,8 @@ class DefaultController extends AbstractController
'reverseNoteSort' => $openNotes, 'reverseNoteSort' => $openNotes,
'isAdmin' => $this->isGranted('ROLE_ADMIN'), 'isAdmin' => $this->isGranted('ROLE_ADMIN'),
'meta' => $meta, 'meta' => $meta,
'speakers' => $speakers,
'series' => $series,
]); ]);
} }
@@ -72,7 +78,7 @@ class DefaultController extends AbstractController
$sharedWithMe = $emi->getRepository(SharedNote::class)->getNotesSharedWithMe($user); $sharedWithMe = $emi->getRepository(SharedNote::class)->getNotesSharedWithMe($user);
$shared = $emi->getRepository(SharedNote::class)->getNotesSharedByMe($user); $shared = $emi->getRepository(SharedNote::class)->getNotesSharedByMe($user);
dump($shared); //dump($shared);
return $this->render('default/profile.html.twig', [ return $this->render('default/profile.html.twig', [
'onLoad' => 'rollUp("user");rollUp("settings")', 'onLoad' => 'rollUp("user");rollUp("settings")',

View File

@@ -50,6 +50,9 @@ class RegistrationController extends AbstractController
return $this->redirectToRoute('app_home'); return $this->redirectToRoute('app_home');
} }
else {
dd($form);
}
return $this->render('registration/register.html.twig', [ return $this->render('registration/register.html.twig', [
'registrationForm' => $form, 'registrationForm' => $form,

View File

@@ -43,6 +43,9 @@ class RegistrationFormType extends AbstractType
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'data_class' => User::class, 'data_class' => User::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'registration',
]); ]);
} }
} }

View File

@@ -16,6 +16,9 @@
button.button i { button.button i {
font-size: 1.5em; font-size: 1.5em;
} }
.input-error {
border: solid 2px red !important;
}
</style> </style>
{% endblock %} {% endblock %}
@@ -112,7 +115,7 @@ let saveFailureCount = {{ meta.saveFailureCount }};
<select id="speaker" onchange='newSpeaker()'> <select id="speaker" onchange='newSpeaker()'>
<option value=0>-- Speaker --</option> <option value=0>-- Speaker --</option>
<option value='new'>New Speaker</option> <option value='new'>New Speaker</option>
{% for s in app.user.speakers %} {% for s in speakers %}
<option value='{{ s.id }}'>{{ s.name }}</option> <option value='{{ s.id }}'>{{ s.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
@@ -120,7 +123,7 @@ let saveFailureCount = {{ meta.saveFailureCount }};
<select id="series" onchange='newSeries()'> <select id="series" onchange='newSeries()'>
<option value=0>-- Series --</option> <option value=0>-- Series --</option>
<option value='new'>New Series</option> <option value='new'>New Series</option>
{% for s in app.user.series %} {% for s in series %}
<option value='{{ s.id }}'>{{ s.name }}</option> <option value='{{ s.id }}'>{{ s.name }}</option>
{% endfor %} {% endfor %}
</select> </select>

View File

@@ -24,15 +24,9 @@
<div id='main'> <div id='main'>
<div class='inner'> <div class='inner'>
<header id="header"> <header id="header">
{% if app.user %} <a href='/{% if app.user %}home{% endif %}' class='logo'>
<a href='/home' class='logo'>
<img src='{{ asset('images/vecteezy_notes-icon-in-trendy-flat-style-isolated-on-white_29722382.jpg') }}' style='width:60px;'/> <img src='{{ asset('images/vecteezy_notes-icon-in-trendy-flat-style-isolated-on-white_29722382.jpg') }}' style='width:60px;'/>
</a> </a>
{% else %}
<a href="/" class="logo">
<img src=' {{ asset('images/vecteezy_notes-icon-in-trendy-flat-style-isolated-on-white_29722382.jpg') }}' style='width:60px;'/>
</a>
{% endif %}
</header> </header>
<!-- Banner --> <!-- Banner -->

View File

@@ -27,41 +27,19 @@
{{ form_errors(registrationForm) }} {{ form_errors(registrationForm) }}
{{ form_start(registrationForm) }} {{ form_start(registrationForm) }}
{{ form_row(registrationForm.name) }} <label for='registration_form_name' class='form-label'>Name</label>
{{ form_row(registrationForm.email) }} <input type='text' name='{{ field_name(registrationForm.name) }}' id='registration_form_name' class='form-control' required='required'/>
<label for='registration_form_email' class='form-label'>Email</label>
<input type='text' name='{{ field_name(registrationForm.email) }}' id='registration_form_email' class='form-control' required='required'/>
{{ form_row(registrationForm.plainPassword, { {{ form_row(registrationForm.plainPassword, {
label: 'Password' label: 'Password'
}) }} }) }}
<button type='submit' class='btn' id='register-btn'>Register</button> <button type='submit' class='btn' id='register-btn'>Register</button>
{{ form_end(registrationForm) }} {{ form_end(registrationForm) }}
<!--
<form action="/register" method="post" id='registration-form'>
<input type="hidden" name="_csrf_token" id="csrfToken"
value="{{ csrf_token('authenticate') }}"
>
<fieldset>
<legend>Login Information</legend>
<div class="input-container">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
</div>
<div class="input-container">
<label for="emailAddress">Email Address:</label>
<input type="email" id="emailAddress" name="email" required>
</div>
<div class="input-container">
<label for="password">Password:</label>
<input type="password" id="password" name="plainPassword" required>
</div>
<div class="input-container">
<label for="confirmPassword">Confirm Password:</label>
<input type="password" id="confirmPassword" name="confirmPassword" required>
</div>
</fieldset>
<button id='register-btn'>Register</button>
<input type="submit" value="Register" />
</form>-->
</div> </div>
{% endblock %} {% endblock %}