grav-plugin-form
grav-plugin-form copied to clipboard
Slug based URLs, forms and multi-language
So I'm actually not sure how to explain the issue very clearly, but I'll try
Grav 1.7.3 + Form 4.3.0 plugin. All freshly installed
Basically, trying to implement one feature on website (building personal page from scratch) encountered an issue, which uncovered a bunch of problems with Gravs core and how in handles URLs, routes, redirects and pages.
First: the only way to get slug based URL, is page.route() (at least that's what I figured), but that's not always an option, because sometimes you want to add a link just from some saved pages field, which saves slug value without language code. That wouldn't be a problem in a normal page, because we can just pages.find([SLUG]).route or pages.find([FOLDER]).route, but there's a problem - pages === null in modules of modular pages.
OK, we can't get to page.route, then lets try redirects. Yes, even though they are one of the worst enemies of SEO, but still - we don't have much of a choice. When in system.yaml you have pages.redirect_default_route: true, when you get to folder based URL, this setting enabled, redirects to correct slug based URL. OK it seems we have a really dirty workaround, BUT...
After redirect, forms() will return null no matter how you try to get the form - by name or by route. And only cache clearing helps. But it helps only if you have a single language page and won't navigate to folder based URLs (so that redirect wouldn't happen). If you have at least two languages and want to switch it on page with a form, again there's no easy way to get slug URL for same page in different language and you have to use whatever URL you can get (most likely folder based URL). After clicking on new language redirect happens and form is gone.
Same applies if you want to implement AJAX form and have different slugs than folder name. You can't use common frontmatter.yaml for all languages, because you need to have separate actions for form in each language depending on slug, because if you use folder based action URL, after submit it redirects to correct slug based URL and whole payload is gone - form never gets submitted.
It's kind of a closed loop
You need to have correct (slug based) URLs to be somewhat good to SEO. There's no way to get slug URLs, because you can't access page.route in some situations (especially for different language). So you just use folder based URLs with redirects, but you can't use them either not only because of SEO, but because of redirects after which you lose form payload or lose forms all together.
I was already liking Grav and even considering a donation, but no no matter how easy it is to extend, I'm starting to find it pretty much unusable if you want to have beautiful multilingual SEO friendly slugs and at least a contact form.
BTW, missing forms issue dates 4 years back
Here's my setup to reproduce the issue (copy from discourse, where I initially raised a question)
In Quark templates add two files:
fom.html.twig
{% if pageForm %}
{% include "forms/form.html.twig" with {form: pageForm} %}
{% endif %}
contact-us.html.twig
{% extends 'partials/base.html.twig' %}
{% block content %}
{% include "form.html.twig" with {pageForm: forms({"route": '/contact-us/form'})} %}
{% endblock %}
System.yaml
Uncomment redirect and navigate to folder based URL to reproduce the missing form issue
languages:
supported:
- lt
- en
default_lang: lt
include_default_lang: false
content_fallback:
en: en
#pages:
# redirect_default_route: true
Folder user/pages/03.contact-us
contact-us.en.md
---
title: 'Contact me'
slug: contact-me
cache_enable: false
---
##### Text
contact-us.lt.md
---
title: Susisiekime
slug: susisiekime
cache_enable: false
---
##### Tekstas
Folder user/pages/03.contact-us/form
form.en.md
---
title: Form
visible: false
form:
name: contact-form
action: /contact-me/form
debugger: false
cache_enable: false
---
form.lt.md
---
title: Form
visible: false
form:
name: contact-form
action: /susisiekime/form
debugger: false
cache_enable: false
---
frontmatter.yaml
form:
name: contact-form
action: '/contact-us/form'
client_side_validation: false
inline_errors: true
keep_alive: true
fields:
-
id: contact-name
name: name
label: CONTACT_FORM.NAME
placeholder: CONTACT_FORM.NAME_PLACEHOLDER
autocomplete: true
type: text
validate:
required: true
buttons:
-
type: submit
value: CONTACT_FORM.BUTTON.SUBMIT
classes: "btn-primary"
-
type: reset
value: CONTACT_FORM.BUTTON.RESET
classes: "btn-secondary"
process:
-
message: CONTACT_FORM.THANK_YOU
-
reset: true
Hope this can be fixed before I need to publish my page :(
@Karmalakas, Trying to replay your steps and it seems it contains a design flaw.
Note: I removed the cache_enable: false setting everywhere.
- Both your '03.contact-us' pages contain a slug: 'contact-me' and 'susisiekime'.
- In template 'templates/contact-us.html.twig' you load the form as follows:
{% include "form.html.twig" with {pageForm: forms({"route": '/contact-us/form'})} %} - However, route '/contact-us/form' does not exist. Only routes '/contact-me/form' and '/susisiekime/form' exist depending on the current language.
- To circumvent the different route issues, you could:
- use :
{% include "form.html.twig" with { pageForm: forms({'route': page.route ~ '/form' }) } %} - or include the form using
forms('contact-form') - or move '03.contact-us/form' to '/form'. Now there is a single route for both languages: '/form'
- use :
- NB, also the route of the action should be slug related ('/contact-me/form' vs. '/susisiekime/form') to prevent 404 issues.
I might have multiple different forms in the future (most likely I will), so I want contact form to stay under /contact-us folder.
forms({"route": '/contact-us/form'}) is just an example and, as mentioned, it works perfectly fine after cache is cleared or you go directly to slug URL without any redirect. I also tried:
forms({"route": '/contact-me/form'})forms({"route": pages.find('/contact-us/form').route})forms('contact-form')
All variations work perfectly fine when there's no redirect, but none of them work after redirect. So moving to /form isn't solving the core issue anyway.
Actions also have to be separate for each language and slug related, so there wouldn't be a redirect, because, as mentioned, after redirect, payload is gone. There was no 404 error, because Grav handles folder based URLs fine - it's just that redirect ruins everything.
I'm thinking now, being able to get correct routes of any pages anywhere in the templates or PHP classes for any language would eliminate redirects (unless of course you'll link some place not by route). Eliminating redirects by linking to the correct URLs from the beginning, would solve all of the above described missing forms and missing submit payloads. Also language switcher could be updated to get correct route and wouldn't need all the concatenation with base_url, which also leads to redirect now.
Updated previous post: Added alternative to including 'form.html.twig'
{% include "form.html.twig" with { pageForm: forms({"route": page.route ~ '/form' }) } %}
Still doesn't work. No matter how you try to get a form, forms() can't find any after redirect
Created a draft PR
Not very familiar with Grav core, so it might be completely wrong, and it's not really working yet, because of the $this->parent, but I think someone might take a look at the approach and say something
Now I'm thinking, if it's easier to fix, maybe just making forms() actually find forms after redirect would do for now, but getting different language slug routes is a must. Will see later, maybe I'll check how forms work :/
Just encountered, if I have maintenance plugin and maintenance mode active, and try to get to a page with a form, I'm asked to login. After login, redirect happens and form isn't there. So it's not just multi-language issue, but any redirect (or maybe just from folder to slug - not sure how maintenance plugin redirects).
Anyway, I believe getting slug URL from any place for any language for any page is a must still
Created a PR, that partially fixes missing form issue, but it still won't work if form is searched by name, because $this->forms is empty there.
Also slug URLs still needs to be implemented
Can you please provide a sample user folder (zipped) so I don’t have to spend time setting this up manually?
Sure - Grav_missing_forms_setup.zip
Just add this structure to fresh Grav install and
- Best case scenario
- Navigate to https://localhost/susisiekime - form is there
- Change address bar to https://localhost/en/contact-me - form is there
- Then no matter how you navigate, form is always there, even when going to https://localhost/contact-us - you get redirected to https://localhost/susisiekime, but form was already cached (probably) and it's there
- Missing forms scenario No 1 (after clearing cache)
- Navigate to https://localhost/susisiekime - form is there
- Change address bar to https://localhost/en/contact-us - you get redirected to /en/contact-me and form is missing
- No matter how you navigate, LT page has form and EN doesn't
- Missing forms scenario No 2 (after clearing cache)
- Navigate to https://localhost/en/contact-me - form is there
- Change address bar to https://localhost/contact-us - you get redirected to /susisiekime and form is missing
- No matter how you navigate, EN page has form and LT doesn't
- Worst case missing form scenario No 3 (after clearing cache)
- Navigate to https://localhost/contact-us - form missing (after redirect to /susisiekime)
- Change address bar to https://localhost/en/contact-us - form missing (after redirect to /en/contact-me)
- No matter how you navigate, both LT and EN pages are missing forms
And there's really no way currently to get correct slug based URL for current page, but different language, hence redirects from folder based URLs
I am not 100% sure, but is this issue related? https://github.com/getgrav/grav-plugin-form/issues/484
I am not 100% sure, but is this issue related? #484
I doubt.. You have both forms visible, but submission is messed up, and here - when forms are not visible at all
Will take a look at this after the next release.