openvibers/openvibe.help Commits

A ticket opened from a site's 'Report a problem' link starts with that site and page (#81)

OP OpenVibers committed 21 days ago · 12a6aa5c3e · 2 files, +29 −2

Changes

ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex af6ad1f..167e085 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -328,10 +328,20 @@ ${problem ? notice(problem, 'warn') : ''}
<p><button class="sc-btn sc-primary" type="submit">Open the ticket</button> <a class="sc-btn" href="/tickets">Your tickets</a></p>
</form>`;
++ // "Report a problem" in every site's footer (openvibe-shared footer.js) opens /tickets/new?site=<domain>&page=<url>:
++ // the form starts with that site and page. A site Help does not know, or a page that is not an http(s) address of
++ // at most 500 characters, is left blank; the page is stored as text and never fetched, as when typed.
++ const prefillOf = (q) => {
++ const site = String(q.site || '').toLowerCase().replace(/^www\./, '');
++ const pageUrl = typeof q.page === 'string' && q.page.length <= tickets.LIMITS.pageUrl ? tickets.pageLink(q.page) : null;
++ return { site: catalog.byId.has(site) ? site : '', pageUrl: pageUrl || '' };
++ };
r.get('/tickets/new', (req, res) => {
++ const pre = prefillOf(req.query);
++ const back = `/tickets/new${pre.site || pre.pageUrl ? `?${new URLSearchParams({ ...(pre.site ? { site: pre.site } : {}), ...(pre.pageUrl ? { page: pre.pageUrl } : {}) })}` : ''}`;
if (!signedIn(req)) return page(req, res, {
title: 'Open a ticket', crumbs: [{ label: 'Home', href: '/' }, { label: 'Your tickets', href: '/tickets' }, { label: 'New' }],
-- body: html`<h1>Open a ticket</h1>${signInPrompt('A ticket belongs to your OpenVibe account, so you can see and answer it later.', '/tickets/new')}`,
++ body: html`<h1>Open a ticket</h1>${signInPrompt('A ticket belongs to your OpenVibe account, so you can see and answer it later.', back)}`,
}, 401);
page(req, res, {
title: 'Open a ticket',
@@@ -339,7 +349,7 @@ ${problem ? notice(problem, 'warn') : ''}
crumbs: [{ label: 'Home', href: '/' }, { label: 'Your tickets', href: '/tickets' }, { label: 'New' }],
body: html`<h1>Open a ticket</h1>
<p class="sc-lede">One conversation with the people who run OpenVibe. You will see it under <a href="/tickets">your tickets</a> and can reply there.</p>
--${ticketForm()}`,
++${ticketForm({ values: pre })}`,
});
});
ddiff --git a/test/tickets.test.js b/test/tickets.test.js
iindex 3442b08..a1873dc 100644
---- a/test/tickets.test.js
++++ b/test/tickets.test.js
@@@ -186,6 +186,23 @@ const SUBJECT = 'Nothing happens when I press Go Live';
assert.ok(bad.text.includes('<select id="site" name="site"'), 'the form is not shown again');
});
++ await check('"Report a problem" from a site\'s footer starts the form with that site and page; anything else is left blank', async () => {
++ const page = 'https://openvibe.live/@kim?tab=vods';
++ const form = await t.get(`/tickets/new?${new URLSearchParams({ site: 'www.openvibe.live', page })}`, { as: kim });
++ assert.strictEqual(form.status, 200);
++ assert.ok(form.text.includes('<option value="openvibe.live" selected>'), 'the site is not chosen');
++ assert.ok(form.text.includes(`value="${page.replace('&', '&amp;')}"`) || form.text.includes(`value="${page}"`), 'the page is not filled in');
++ const odd = await t.get(`/tickets/new?${new URLSearchParams({ site: 'evil.example', page: 'javascript:alert(1)' })}`, { as: kim });
++ assert.ok(!odd.text.includes(' selected>') || !odd.text.includes('evil.example'), 'an unknown site is chosen');
++ assert.ok(!odd.text.includes('javascript:alert'), 'a non-http page is filled in');
++ const long = await t.get(`/tickets/new?${new URLSearchParams({ page: `https://openvibe.live/${'x'.repeat(600)}` })}`, { as: kim });
++ assert.ok(long.text.includes('name="page_url" type="text" maxlength="500" value=""'), 'a page over 500 characters is filled in');
++ // Signed out, the sign-in link comes back to the same prefilled form.
++ const out = await t.get(`/tickets/new?${new URLSearchParams({ site: 'openvibe.live', page })}`);
++ assert.strictEqual(out.status, 401);
++ assert.ok(out.text.includes(encodeURIComponent(`/tickets/new?${new URLSearchParams({ site: 'openvibe.live', page })}`)), 'sign-in loses the site and page');
++ });
++
await check('a reply from the page goes back to the ticket, and only from this site', async () => {
const r = await t.get(`/tickets/${id}`, { as: kim, form: { message: 'Still happening today.' }, headers: SAME });
assert.strictEqual(r.status, 303);