From a5543e0099fa20f3ed917d353da3a43dee7c18c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patricio=20Garc=C3=ADa?= Date: Fri, 15 Aug 2025 13:55:02 +0100 Subject: [PATCH] Nikola auto commit. Nikola version: 8.3.3 --- .doit.db.db | Bin 258048 -> 270336 bytes pages/inscription.md | 14 +++ plugins/nextcloud_forms/README.md | 40 +++++++ .../nextcloud_forms/nextcloud_forms.plugin | 12 ++ plugins/nextcloud_forms/nextcloud_forms.py | 109 ++++++++++++++++++ .../nextcloud_forms/requirements-nonpy.txt | 2 + plugins/nextcloud_forms/submit_form.js | 69 +++++++++++ .../templates/jinja/nextcloud_forms.tmpl | 79 +++++++++++++ .../templates/mako/nextcloud_forms.tmpl | 103 +++++++++++++++++ 9 files changed, 428 insertions(+) create mode 100644 pages/inscription.md create mode 100644 plugins/nextcloud_forms/README.md create mode 100644 plugins/nextcloud_forms/nextcloud_forms.plugin create mode 100644 plugins/nextcloud_forms/nextcloud_forms.py create mode 100644 plugins/nextcloud_forms/requirements-nonpy.txt create mode 100644 plugins/nextcloud_forms/submit_form.js create mode 100644 plugins/nextcloud_forms/templates/jinja/nextcloud_forms.tmpl create mode 100644 plugins/nextcloud_forms/templates/mako/nextcloud_forms.tmpl diff --git a/.doit.db.db b/.doit.db.db index 9523f6434c3c04c291c282e6ab1227305407aee0..ff040e2e3a0f3d670506dbab1f3a86e522387c10 100644 GIT binary patch delta 2637 zcmb_eTWl3Y7(TOedO7rzwkQG>;q)Rwfju*`v$MN3#$t?#7jE7Wwd`fKwzj87da4u^ zONh}IN$V;T6=Hc3Br15sns@;ph>6Ao%?V1>fbo{-gC!-tn5h32spXP*jBc_gJ3Dj! z|KIQX{%>aJMBUJ#`r!;(KGI~NXZEih&Z5J`pRyY>Xrkz^-oNmM=7dZnYLC_3RM%2> z>CBV0->3JcH`iQOGiSy`>c`Z(seP%ZQjezArdFidQ&p+&s&`dxs=OjuliZ0n;-|Gv zZJlOlYmtehTJa&;P^^4#Lc2`*hDANY7mn-LB(z0faFYUyG(k`Yn~wOAQsp zRfQcDmDeN^i63S~CkN-H`uz=E`9fD;uK3lUeaW>85+xK@S3C3D+N0?`>7D6^(%tFy zG*8b;A4?rbeOx_Sy{BqR)o^8JWuanCMR)T0WLxs0WE)39yciD6SBSV*{MQ^&R~hjN&fhBTv- zvu9Uz<5I#bmx(yoqvon2r2=J}_!>g52}(PSbKt!ZqW zzP>1T?BAN4n31T88t&p_D>LYnrgE)lo?6w47EYz12sf%%T2SM2-FUvrwxW!>^C+%Y zIgDp)*I;fK=G2ge=cpqkJWGA8;hN`u!I!BeU%*I*RjRgx7pO-*!!_!U&tQ146)jLt zd;u|5w4k(FR>IBdw-!`CcID-$qoM(D=o={H2MXCUUt0e;o^urw%p^=Fj^L)vmu9HJ zxdUsd@O&-};RQDJMCke!wN1msNWe_SX8Yf4|J;-A>tm2Ar6A{r*C4&)>>AjZ>^zn=Q>))Gz=h&L|f?fEX}a zm|+OL#(dkLh9jsTiWq1}9jw4NuQ@;Ze^jj+8NiRL%+K0ERsSYF66HVj)ZPfsh}OUL zq1tDl18O{pN@{cf-KswS5^q(*$F*51y9}qNBtc3XpAX|>1l`i3`H=;i7GX+8Q-RU--#-xbUpr_woTca(V*(M7QVebf~V1T zyhx3{h91^HG#?aq#|auPK*YN8hycWePKiMb1G*cC0NFN6YPp`{dBhNeao3iv1uh^w zGZYRFg;dKc@JIi|0yiyPQ0{<{#NHtwuHcMuLkhznK(FmkDJ&YmFArRYFx6xMwCmr- z@07(t-x$E!fYj<7AUs}!My9ZtQ2AF8pTatJmopcNeWogM3olJ)4wSy~hX2h2V>OA# zWia5og)Uz&Z0y&e8S*^?o!}PPoZMLOd-?`~j<9b-?|EZAKRDGU03Btz?br^9?ZmL0 zxJ`&ng>VRGe!zX|ny?jd#|=2O;H^VR^S!Vrs}|pmvt<`ikiGdHw*Y1Kx)1c7Tccip z*{Q04RPN|tVYbbn9>cP1NN9V~vJ4@ekQja-92Rhw*Kkk|GeT)G8GS0uvg_#|@Orxn zlL+bE{ZpyW0fbROAvO2}=a5o5Jn#+ICepLOgF{>THupH61SF98{x&uLQEZBGnK`?N zXyk5wQ@MXVhvb-4DD*7fQqbL3tt>ZSIRt?r&;$3B+4z$OhFD6(0un81#v2ugD3CmK zLpVivrenAuEtgwi=y@(Bl211N>a#v$(%h%nuW8075fCn+z)e|ahJWq8(A^iu7Q6Md fv4R6mY~Kkx^o0%i#1_<~MqfYMS2w8>FKG6k6@5yM delta 910 zcmZ9JUuY9a9LF;|oAx%{nBK#+nCfL4({bbF=+Tje+v4;8KzDsDErNow4t z>DE))Tnklv(=?$Sq*d;*at~S-&7*P%z6gp2v2ThTCl(axv*^ZJiw?suKR(~z{Jvji z=3e8>i$IYAy>qQGaAjqv*ac>tH(lo_kaP6kS3>(K&yvUEUkw}#>p&f0mlsVC^+V9kSn z+bjPw@&jNUl!o_p=i ztYM|>bRTn!vAI0U9bgQWYvtJK+A_JIvJbj#~X`%}N(|yZgawCOIuL~T$7Wa z+^Q?+2nEP;Q_20yZdHz{ol#L#B~jogQ4%DM3MXY=({w&58{JVOA;&~M8k1b4CY5f1 zH+AP$X~tWgj*uuoV{vEk>NU)GnoX=BVo?DPB?u2y=O+JAQ=;IT(dwvA&nEx9F z(c&%;L?0p`IR6&zM8C9wM&xe;z6H!>4FFPT_$dsbAO^Q1e--|W{&)sA6o|*&vM&Of zQF|3e(Bmq+TzSw2Ug1QcwuTGFxrICMCow9@iYjuHDo9Z`iY7~@qzbBTiZZV&$%G+C z6N=s))6IkuRfO9fwSg8i3ZV~O NiGZDzmpBZqeFCv;8>IjM diff --git a/pages/inscription.md b/pages/inscription.md new file mode 100644 index 0000000..cb05590 --- /dev/null +++ b/pages/inscription.md @@ -0,0 +1,14 @@ + + +{{% nextcloud_forms link="https://nube.txs.es/apps/forms/s/BPYqzqF44bFCXb3H7eFe8888" %}} +¡Muchas gracias! En breve nos pondremos en contacto contigo +{{% /nextcloud_forms %}} diff --git a/plugins/nextcloud_forms/README.md b/plugins/nextcloud_forms/README.md new file mode 100644 index 0000000..1f02df3 --- /dev/null +++ b/plugins/nextcloud_forms/README.md @@ -0,0 +1,40 @@ +This plugin embeds a Nextcloud Forms formular in a static site. +It provides a mechanism for the static HTML site to embed a form and to collect +and store the user data. + +The form specification is loaded at build time from the Nextcloud using the +public form link. The HTML form is then build according to the extracted JSON +form specification. + +When the submit button is pressed a simple JS function collects the form data +and sends it to the Nextcloud server using the public API. + +## Requirements and setup + +For this plugin to work a Nextcloud with the Forms App is needed. + +Create a form and make it public. Copy the public link and pass it as the +`link` parameter to the nextcloud_forms shortcut: + +``` +{{% nextcloud_forms link="https://nextcloud_url/index.php/apps/forms/..." %}} +Success Text +{{% /nextcloud_forms %}} +``` + +The `Success Text` is shown after the form is successfully sent to the server. + +## Heads up + +Nextcloud Forms public API is not handling CORS requests correctly, especially +the preflight checks. This leads to the XmlHTTPRequest failing with a CORS +error and the browser not submitting the data. + +At the moment there are two open feature requests at Nextcloud Server and +Nextcloud Forms that should fix these issues: + +- https://github.com/nextcloud/server/pull/31698 +- https://github.com/nextcloud/forms/pull/1139 + +Until these are merged into upstream, at least the changes to Nextcloud Forms +have to be applied manually to the Nextcloud installation. diff --git a/plugins/nextcloud_forms/nextcloud_forms.plugin b/plugins/nextcloud_forms/nextcloud_forms.plugin new file mode 100644 index 0000000..aa51c36 --- /dev/null +++ b/plugins/nextcloud_forms/nextcloud_forms.plugin @@ -0,0 +1,12 @@ +[Core] +name = nextcloud_forms +module = nextcloud_forms + +[Nikola] +PluginCategory = Shortcode + +[Documentation] +author = Andreas Brinner +version = 0.1 +website = https://getnikola.com/ +description = Embed Nextcloud Forms diff --git a/plugins/nextcloud_forms/nextcloud_forms.py b/plugins/nextcloud_forms/nextcloud_forms.py new file mode 100644 index 0000000..fea63b6 --- /dev/null +++ b/plugins/nextcloud_forms/nextcloud_forms.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2022, Andreas Brinner. + +# Permission is hereby granted, free of charge, to any +# person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the +# Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the +# Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of +# the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +import os +import json +import base64 +import requests + +from html.parser import HTMLParser +from urllib.parse import urlparse + +from nikola.plugin_categories import ShortcodePlugin + + +SUBMIT_FORM_JS_PATH = os.path.join(os.path.dirname(__file__), 'submit_form.js') + + +class FormDataParser(HTMLParser): + def __init__(self, *args, **argv): + self.data = {} + HTMLParser.__init__(self, *args, **argv) + + def handle_starttag(self, tag, attrs): + if tag == "input": + data = dict(attrs) + if "id" in data and "value" in data: + self.data[data["id"]] = json.loads(base64.b64decode(data["value"])) + + +class Plugin(ShortcodePlugin): + """Plugin for nextcloud_forms directive.""" + + name = "nextcloud_forms" + + def get_public_form_data(self, link): + try: + data = requests.get(link).text + except requests.exceptions.RequestException: + self.logger.error('Cannot get form data from url={0}', link) + + parser = FormDataParser() + parser.feed(data) + + return parser.data + + def generate_ocs_url(self, link): + """Generate the OCS API base url from the given link. + + see also: + https://github.com/nextcloud/nextcloud-router/blob/master/lib/index.ts#L29" + """ + url = urlparse(link) + return "{}://{}/ocs/v2.php/apps/forms".format(url.scheme, url.netloc) + + def set_site(self, site): + super(type(self), self).set_site(site) + with open(SUBMIT_FORM_JS_PATH, "r") as fd: + submit_form_js = "".format(fd.read()) + site.template_hooks['body_end'].append(submit_form_js) + + def handler(self, link, template="nextcloud_forms.tmpl", + site=None, data=None, lang=None, post=None, **argv): + """Create HTML for Nextcloud Forms formular.""" + + form_data = self.get_public_form_data(link) + + endpoint = self.generate_ocs_url(link) + endpoint += "/api/v1.1/submission/insert" + + form = form_data.get("initial-state-forms-form") + if not form: + self.logger.error('No form defintion fond in url={}'.format(link)) + + template_deps = site.template_system.template_deps(template, site.GLOBAL_CONTEXT) + template_data = site.GLOBAL_CONTEXT.copy() + template_data.update({ + 'lang': lang, + 'form': form, + 'endpoint': endpoint, + 'initial_state': form_data, + 'success_data': data, + }) + output = site.template_system.render_template( + template, None, template_data) + return output, template_deps + [__file__, SUBMIT_FORM_JS_PATH] diff --git a/plugins/nextcloud_forms/requirements-nonpy.txt b/plugins/nextcloud_forms/requirements-nonpy.txt new file mode 100644 index 0000000..dbd65e6 --- /dev/null +++ b/plugins/nextcloud_forms/requirements-nonpy.txt @@ -0,0 +1,2 @@ +Nextcloud::https://nextcloud.com/install +Nextcloud Forms App::https://apps.nextcloud.com/apps/forms diff --git a/plugins/nextcloud_forms/submit_form.js b/plugins/nextcloud_forms/submit_form.js new file mode 100644 index 0000000..488de9f --- /dev/null +++ b/plugins/nextcloud_forms/submit_form.js @@ -0,0 +1,69 @@ +(function () { + function submit(ev) { + const form = ev.target; + + ev.preventDefault(); + + const answers = {}; + const [,formId] = form.id.split("-"); + + for (let i = 0; i < form.length; i++) { + var input = form[i]; + + // extract question id (qid) and answer id (aid) + var [,,qid,,aid] = input.id.split("-"); + + if ( qid && !(qid in answers) ) + answers[qid] = []; + + switch (input.type) { + case "checkbox": + case "radio": + if (input.checked) + answers[qid].push(aid); + console.log(input.type, input.id, qid, aid, input.checked); + break; + + case "select-one": + case "text": + case "textarea": + case "date": + case "datetime-local": + if (input.value) + answers[qid].push(input.value); + console.log(input.type, input.id, qid, input.value); + break; + + default: + console.log("unknown form element type:", input.type); + } + } + + const request = new XMLHttpRequest(); + request.open("POST", form.action, true); + request.setRequestHeader("OCS-APIRequest", "true"); + request.setRequestHeader("Accept", "application/json"); + request.setRequestHeader("Content-Type", "application/json"); + request.onload = function () { + const message = document.getElementById("form-" + formId + "-messages"); + const form = document.getElementById("form-" + formId); + const success = document.getElementById("form-" + formId + "-success"); + const response = JSON.parse(this.response); + + if (this.status == 200) { + // success + form.style.display = "none"; + success.style.display = "block"; + } else { + message.innerHTML = ''; + } + }; + request.send(JSON.stringify({'formId': formId, 'answers': answers})); + } + + for (let i = 0; i < document.forms.length; i++) + document.forms[i].onsubmit = submit; +})(); diff --git a/plugins/nextcloud_forms/templates/jinja/nextcloud_forms.tmpl b/plugins/nextcloud_forms/templates/jinja/nextcloud_forms.tmpl new file mode 100644 index 0000000..0a9ab06 --- /dev/null +++ b/plugins/nextcloud_forms/templates/jinja/nextcloud_forms.tmpl @@ -0,0 +1,79 @@ +
+
+ +{% for question in form.questions %} +
+ + {% if question.type == "short" %} + + {% elif question.type == "long" %} + + {% elif question.type == "multiple" %} + {% for option in question.options %} +
+ + +
+ {% endfor %} + {% elif question.type == "multiple_unique" %} + {% for option in question.options %} +
+ + +
+ {% endfor %} + {% elif question.type == "dropdown" %} + + {% elif question.type == "date" %} + + {% elif question.type == "datetime" %} + + {% else %} + + {% endif %} +
+{% endfor %} + + +
+ diff --git a/plugins/nextcloud_forms/templates/mako/nextcloud_forms.tmpl b/plugins/nextcloud_forms/templates/mako/nextcloud_forms.tmpl new file mode 100644 index 0000000..f17c9b6 --- /dev/null +++ b/plugins/nextcloud_forms/templates/mako/nextcloud_forms.tmpl @@ -0,0 +1,103 @@ +
+
+ +% for question in form['questions']: +
+ + % if question['type'] == "short": + + + % elif question['type'] == "multiple": + % for option in question['options']: +
+ + +
+ % endfor + % elif question['type'] == "multiple_unique": + % for option in question['options']: +
+ + +
+ % endfor + % elif question['type'] == "dropdown": + + % elif question['type'] == "date": + + % elif question['type'] == "datetime": + + % else: + + % endif +
+% endfor + + +
+