openvibers/openvibe.chat Commits
SEO-19: a server page canonicals to the URL served and listed in the sitemap (#243)
Changes
@@@ -136,8 +136,12 @@ ${create}`,can.member && can.role !== 'owner' ? `<form class="oc-inline" method="post" action="/s/${esc(server.slug)}/leave"><button type="submit" class="oc-link">Leave server</button></form>` : '',].filter(Boolean).join(' · ');const canInvite = can.manage || (can.moderate && server.visibility === 'public');++ // The canonical URL is the one served and listed in the sitemap: /s/<server> without a channel in the++ // request, and the channel's own URL when the request named it. `back` stays the channel URL the composer++ // and the sign-in link post to (POST /s/:slug/:channel).++ const canonicalPath = req.params.channel ? back : `/s/${server.slug}`;await page(req, res, 200, {-- actor, wide: true, footer: 'compact', title: `${isVoice ? '' : '#'}${room.name} · ${server.name}`, path: back, active: 'servers',++ actor, wide: true, footer: 'compact', title: `${isVoice ? '' : '#'}${room.name} · ${server.name}`, path: canonicalPath, active: 'servers',robots: server.visibility === 'public' ? 'index, follow' : 'noindex, nofollow', script: true, callScript: callReady,description: server.description || `${server.name}, a server on OpenVibe.Chat with ${server.member_count} member${Number(server.member_count) === 1 ? '' : 's'}.`,page: {@@@ -231,7 +231,11 @@ t('the official server: only chat staff take /s/openvibe; it is marked officialassert.ok(list.slice(1).every((x) => x.official === false), 'no other server is official');assert.strictEqual((await api('POST', '/', staff, { name: 'Explore', slug: 'explore' })).status, 422, 'the other reserved words stay reserved, even for staff');assert.match((await page('/servers', null)).text, /<strong>OpenVibe<\/strong><span class="oc-badge oc-badge-official">official<\/span>/);-- assert.match((await page('/s/openvibe', null)).text, /oc-srv-name">OpenVibe<\/h1><span class="oc-badge oc-badge-official">official<\/span>/);++ const official = (await page('/s/openvibe', null)).text;++ assert.match(official, /oc-srv-name">OpenVibe<\/h1><span class="oc-badge oc-badge-official">official<\/span>/);++ const canonical = (/<link rel="canonical" href="([^"]*)"/.exec(official) || [])[1] || '';++ assert.match(canonical, /\/s\/openvibe$/, 'canonical is the URL served and listed in the sitemap, not the default channel');++ assert.ok(!canonical.includes('/s/openvibe/general'), 'no canonical to the channel the server happens to default to');});t.run(async () => { if (h && h.close) await h.close(); });
@@ -136,8 +136,12 @@ ${create}`,
@@ -136,8 +136,12 @@ ${create}`,
can.member && can.role !== 'owner' ? `<form class="oc-inline" method="post" action="/s/${esc(server.slug)}/leave"><button type="submit" class="oc-link">Leave server</button></form>` : '',
can.member && can.role !== 'owner' ? `<form class="oc-inline" method="post" action="/s/${esc(server.slug)}/leave"><button type="submit" class="oc-link">Leave server</button></form>` : '',
].filter(Boolean).join(' · ');
].filter(Boolean).join(' · ');
const canInvite = can.manage || (can.moderate && server.visibility === 'public');
const canInvite = can.manage || (can.moderate && server.visibility === 'public');
+ // The canonical URL is the one served and listed in the sitemap: /s/<server> without a channel in the
+ // request, and the channel's own URL when the request named it. `back` stays the channel URL the composer
+ // and the sign-in link post to (POST /s/:slug/:channel).
+ const canonicalPath = req.params.channel ? back : `/s/${server.slug}`;
await page(req, res, 200, {
await page(req, res, 200, {
- actor, wide: true, footer: 'compact', title: `${isVoice ? '' : '#'}${room.name} · ${server.name}`, path: back, active: 'servers',
+ actor, wide: true, footer: 'compact', title: `${isVoice ? '' : '#'}${room.name} · ${server.name}`, path: canonicalPath, active: 'servers',
robots: server.visibility === 'public' ? 'index, follow' : 'noindex, nofollow', script: true, callScript: callReady,
robots: server.visibility === 'public' ? 'index, follow' : 'noindex, nofollow', script: true, callScript: callReady,
description: server.description || `${server.name}, a server on OpenVibe.Chat with ${server.member_count} member${Number(server.member_count) === 1 ? '' : 's'}.`,
description: server.description || `${server.name}, a server on OpenVibe.Chat with ${server.member_count} member${Number(server.member_count) === 1 ? '' : 's'}.`,
page: {
page: {
@@ -231,7 +231,11 @@ t('the official server: only chat staff take /s/openvibe; it is marked official
@@ -231,7 +231,11 @@ t('the official server: only chat staff take /s/openvibe; it is marked official
assert.ok(list.slice(1).every((x) => x.official === false), 'no other server is official');
assert.ok(list.slice(1).every((x) => x.official === false), 'no other server is official');
assert.strictEqual((await api('POST', '/', staff, { name: 'Explore', slug: 'explore' })).status, 422, 'the other reserved words stay reserved, even for staff');
assert.strictEqual((await api('POST', '/', staff, { name: 'Explore', slug: 'explore' })).status, 422, 'the other reserved words stay reserved, even for staff');
assert.match((await page('/servers', null)).text, /<strong>OpenVibe<\/strong><span class="oc-badge oc-badge-official">official<\/span>/);
assert.match((await page('/servers', null)).text, /<strong>OpenVibe<\/strong><span class="oc-badge oc-badge-official">official<\/span>/);
- assert.match((await page('/s/openvibe', null)).text, /oc-srv-name">OpenVibe<\/h1><span class="oc-badge oc-badge-official">official<\/span>/);
+ const official = (await page('/s/openvibe', null)).text;
+ assert.match(official, /oc-srv-name">OpenVibe<\/h1><span class="oc-badge oc-badge-official">official<\/span>/);
+ const canonical = (/<link rel="canonical" href="([^"]*)"/.exec(official) || [])[1] || '';
+ assert.match(canonical, /\/s\/openvibe$/, 'canonical is the URL served and listed in the sitemap, not the default channel');
+ assert.ok(!canonical.includes('/s/openvibe/general'), 'no canonical to the channel the server happens to default to');
});
});
t.run(async () => { if (h && h.close) await h.close(); });
t.run(async () => { if (h && h.close) await h.close(); });