From dccb83f2821828a3dd580f43450410fbeedbf928 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sat, 24 Sep 2022 14:40:54 +0200
Subject: [PATCH] backoffice: keep extra query parameters in backoffice
 submission (#50821)

---
 tests/backoffice_pages/test_submission.py | 59 +++++++++++++++++++++++
 wcs/backoffice/submission.py              | 17 ++++++-
 wcs/forms/common.py                       |  8 ++-
 3 files changed, 81 insertions(+), 3 deletions(-)

diff --git a/tests/backoffice_pages/test_submission.py b/tests/backoffice_pages/test_submission.py
index a8b954be6..4aebcdcee 100644
--- a/tests/backoffice_pages/test_submission.py
+++ b/tests/backoffice_pages/test_submission.py
@@ -1315,6 +1315,65 @@ def test_backoffice_submission_with_nameid_and_channel(pub, local_user):
     assert 'The target user was not found, this form is anonymous.' in resp.text
 
 
+def test_backoffice_submission_with_nameid_and_extra_query_string(pub, local_user):
+    user = create_user(pub)
+
+    FormDef.wipe()
+    formdef = FormDef()
+    formdef.name = 'form title'
+    formdef.fields = [
+        fields.ComputedField(
+            id='1',
+            label='computed',
+            varname='computed',
+            value_template='{{ request.GET.param }}',
+            freeze_on_initial_value=True,
+        ),
+        fields.StringField(
+            id='2',
+            label='field',
+            type='string',
+            prefill={'type': 'string', 'value': 'x{{ form_var_computed }}y'},
+        ),
+        fields.StringField(
+            id='3',
+            label='field',
+            type='string',
+            prefill={'type': 'string', 'value': 'x{{ request.GET.param }}y'},
+        ),
+    ]
+    formdef.backoffice_submission_roles = user.roles[:]
+    formdef.workflow_roles = {'_receiver': 1}
+    formdef.store()
+
+    app = login(get_app(pub))
+    resp = app.get('/backoffice/submission/form-title/?NameID=%s&param=test' % local_user.name_identifiers[0])
+    assert resp.location.startswith('http://example.net/backoffice/submission/form-title/')
+
+    formdata_no = resp.location.split('/')[-2]
+    formdata = formdef.data_class().get(formdata_no)
+    assert formdata.user_id == str(local_user.id)
+    resp = resp.follow()  # redirect to created draft
+    resp = resp.follow()  # redirect to ?mt=
+
+    # check user is mentioned in sidebar
+    assert '<h3>Associated User</h3>' in resp
+    assert '<p>%s</p>' % local_user.get_display_name() in resp
+
+    assert resp.form['f2'].value == 'xtesty'  # prefill with computed string (= request.GET)
+    assert resp.form['f3'].value == 'xtesty'  # prefill with request.GET
+    resp = resp.form.submit('submit')
+    assert 'Check values then click submit.' in resp.text
+    # final submit
+    resp = resp.form.submit('submit')
+
+    formdata_no = resp.location.split('/')[-2]
+    data_class = formdef.data_class()
+    formdata = data_class.get(formdata_no)
+    assert formdata.user_id == str(local_user.id)
+    assert formdata.data == {'1': 'test', '2': 'xtesty', '3': 'xtesty'}
+
+
 def test_backoffice_submission_only_one_check(pub, local_user):
     user = create_user(pub)
 
diff --git a/wcs/backoffice/submission.py b/wcs/backoffice/submission.py
index 287f73062..1846317d5 100644
--- a/wcs/backoffice/submission.py
+++ b/wcs/backoffice/submission.py
@@ -15,6 +15,7 @@
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
 import time
+import urllib.parse
 
 from django.utils.safestring import mark_safe
 from quixote import get_publisher, get_request, get_response, get_session, redirect
@@ -84,6 +85,12 @@ class SubmissionFormStatusPage(FormStatusPage):
             return redirect(get_publisher().get_backoffice_url() + '/submission/')
         return super()._q_index()
 
+    def restore_draft(self):
+        # redirect to draft and keep extra query parameters so {{request.GET}} can be used in form.
+        params = {'mt': self.get_restore_draft_magictoken()}
+        params.update(get_request().form or {})
+        return redirect('../?' + urllib.parse.urlencode(params))
+
 
 class FormFillPage(PublicFormFillPage):
     _q_exports = [
@@ -145,7 +152,15 @@ class FormFillPage(PublicFormFillPage):
                 formdata.submission_context['caller'] = caller
             formdata.store()
             self.set_tracking_code(formdata)
-            return redirect('%s/' % formdata.id)
+            redirect_url = '%s/' % formdata.id
+            extra_query_params = {
+                x: y
+                for x, y in get_request().form.items()
+                if x not in ('channel', 'NameID', 'ReturnURL', 'caller')
+            }
+            if extra_query_params:
+                redirect_url += '?' + urllib.parse.urlencode(extra_query_params)
+            return redirect(redirect_url)
 
         self.selected_submission_channel = get_request().form.get('channel') or get_request().form.get(
             'submission_channel'
diff --git a/wcs/forms/common.py b/wcs/forms/common.py
index 17b5f865b..b0770a1e9 100644
--- a/wcs/forms/common.py
+++ b/wcs/forms/common.py
@@ -312,8 +312,8 @@ class FormStatusPage(Directory, FormTemplateMixin):
             templates=list(self.get_formdef_template_variants(self.status_templates)), context=context
         )
 
-    def restore_draft(self):
-        # restore and redirect to draft
+    def get_restore_draft_magictoken(self):
+        # restore draft into session
         session = get_session()
         filled = self.filled
         if not (get_request().is_in_backoffice() and filled.backoffice_submission):
@@ -355,7 +355,11 @@ class FormStatusPage(Directory, FormTemplateMixin):
                 computed_data[field.id] = form_data[field.id]
         if computed_data:
             session.add_magictoken('%s-computed' % magictoken, computed_data)
+        return magictoken
 
+    def restore_draft(self):
+        # redirect to draft
+        magictoken = self.get_restore_draft_magictoken()
         return redirect('../?mt=%s' % magictoken)
 
     def get_workflow_form(self, user):
-- 
2.37.2

