1
0
Fork 1

Merge pull request '#29 + #30: SEO Improvements + CSP & Sanitization' (#31) from gravel/sessioncommunities.online:pr-bundle-001 into main

Reviewed-on: #31
main
SomeGuy 1 year ago
commit f6ee9ab158

@ -3,6 +3,7 @@
// change in the foreseeable future.
export const dom = {
/** @return {HTMLTableElement | null} */
tbl_communities: () => document.getElementById("tbl_communities"),
last_checked: () => document.getElementById("last_checked_value"),
qr_modal: (communityID) => document.getElementById(`modal_${communityID}`),

@ -61,12 +61,26 @@ const transformJoinURL = (join_link) => {
});
}
function onLoad(timestamp) {
setLastChecked(timestamp);
function getTimestamp() {
const timestampRaw =
document.querySelector('meta[name=timestamp]')
?.getAttribute('content');
if (!timestampRaw) return null;
const timestamp = parseInt(timestampRaw);
if (Number.isNaN(timestamp)) return null;
return timestamp;
}
function onLoad() {
const timestamp = getTimestamp();
if (timestamp !== null) {
setLastChecked(timestamp);
}
hideBadCommunities();
sortTable(COLUMN.NAME);
createJoinLinkButtons();
markSortableColumns();
addQRModalHandlers();
}
function displayQRModal(communityID) {
@ -77,6 +91,25 @@ function hideQRModal(communityID) {
dom.qr_modal(communityID).style.display = "none";
}
function addQRModalHandlers() {
const rows = dom.tbl_communities()?.rows;
if (!rows) throw new Error("Rows not found");
for (const row of rows) {
if (row.querySelector('th')) continue;
const communityID = row.getAttribute('--data-identifier');
row.querySelector('.td_qr_code').addEventListener(
'click',
() => displayQRModal(communityID)
);
const closeButton =
dom.qr_modal(communityID).querySelector('.qr-code-modal-close');
closeButton.addEventListener(
'click',
() => hideQRModal(communityID)
);
}
}
function createJoinLinkButtons() {
const join_URLs = dom.join_urls();
Array.from(join_URLs).forEach((td_url) => {
@ -253,9 +286,13 @@ function markSortableColumns() {
const table = dom.tbl_communities();
const header_cells = table.querySelectorAll('th');
for (let colno = 0; colno < header_cells.length; colno++) {
if (!columnIsSortable(colno)) continue;
if (!columnIsSortable(colno)) continue;
header_cells[colno].classList.add('sortable');
}
header_cells[colno].addEventListener(
'click',
() => sortTable(colno)
)
};
}
/**
@ -279,13 +316,7 @@ function sortTable(column) {
setSortState(table, { ascending, column });
}
// html.js for styling purposes
window.document.documentElement.classList.add("js");
// Crude way to export from module script due to inline event handlers.
// Ideally, all handlers would be attached from JS via addEventListener.
Object.assign(window, {
onLoad, sortTable, displayQRModal,
hideQRModal, copyToClipboard
});
// `html.js` selector for styling purposes
document.documentElement.classList.add("js");
document.addEventListener('DOMContentLoaded', () => onLoad());

@ -1,3 +1,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/svg+xml" href="favicon.svg" sizes="any">
<link rel="icon" type="image/svg+xml" href="favicon.svg" sizes="any">
<meta name="keywords" content="Session, Community, Anonymous, Chat">
<meta
http-equiv="Content-Security-Policy"
content="
script-src 'self'; img-src 'self' data:; connect-src 'self'; font-src 'none';
object-src 'none'; media-src 'none'; form-action 'none'; base-uri 'self';
"
>

@ -34,7 +34,7 @@
<?php foreach ($rooms as $id => $room): ?>
<div id="modal_<?=$id?>" class="qr-code-modal">
<div class="qr-code-modal-content">
<span class="qr-code-modal-close" onclick='hideQRModal("<?=$id?>")'>
<span class="qr-code-modal-close">
&times;
</span>
<img

@ -6,14 +6,15 @@
// Join URL contents are not guaranteed to have visible text.
return $id != "qr" && $id != "preview" && $id != "join_url";
}
function sort_onclick($colno) {
global $TABLE_COLUMNS;
$column = $TABLE_COLUMNS[$colno];
if (!column_sortable($column['id'])) return "";
return " onclick='sortTable($colno)' title='Click to sort this column'";
$name = $column['name'];
return " title='Click to sort by $name'";
}
$TABLE_COLUMNS = [
['id' => "identifier", 'name' => "Identifier"],
['id' => "language", 'name' => "L"],
@ -51,17 +52,29 @@
$hostname = explode("//", $room->join_link)[1];
$hostname = explode("/", $hostname)[0];
// Escape external input.
// Ternaries prevent passing null-equal strings, which produce warnings.
$id = htmlspecialchars($id);
$language = $room->language ? htmlspecialchars($room->language) : "";
$name = htmlspecialchars($room->name);
$desc = $room->description ? htmlspecialchars($room->description) : "";
$users = htmlspecialchars($room->active_users);
$preview_link = htmlspecialchars($room->preview_link);
$join_link = htmlspecialchars($room->join_link);
// TODO: Do not forget to rename this escape when merging!
$token = htmlspecialchars($token);
$hostname = htmlspecialchars($hostname);
?>
<tr id="<?=$id?>">
<td class="td_identifier"><?=$id?></td>
<td class="td_language"><?=$room->language?></td>
<td class="td_name"><?=$room->name?></td>
<td class="td_description"
><?=$room->description?></td>
<td class="td_users"><?=$room->active_users?></td>
<td class="td_preview">
<a href="<?=$room->preview_link?>" target="_blank" rel="noopener noreferrer">
<tr id="<?=$id?>" itemscope itemtype="https://schema.org/EntryPoint" --data-identifier="<?=$id?>">
<td class="td_identifier" itemprop="identifier"><?=$id?></td>
<td class="td_language"><?=$language?></td>
<td class="td_name" itemprop="name"><?=$name?></td>
<td class="td_description" itemprop="description"><?=$desc?></td>
<td class="td_users"><?=$users?></td>
<td class="td_preview" itemprop="url">
<a href="<?=$preview_link?>" target="_blank" rel="noopener noreferrer nofollow">
<?php if (str_starts_with($room->preview_link, 'http://')): ?>
<span class="protocol-indicator protocol-http">HTTP</span>
<?php endif; ?>
@ -71,26 +84,26 @@
</a>
</td>
<td class="td_qr_code">
<img
class="qr-code-icon"
<img
class="qr-code-icon"
src="qrcode-solid.svg"
onclick='displayQRModal("<?=$id?>")'
alt="Pictogram of a QR code"
>
</td>
<td class="td_server_icon"
<td class="td_server_icon"
data-token="<?=$token?>"
title="<?=$hostname?> (<?=$token?>)"
item="image"
>
<div class="td_server_icon-circle" style="background-color: <?=$icon_color?>">
<span><?=strtoupper($token[0] . $token[1])?></span>
</div>
</td>
<td class="td_join_url">
<div class="join_url_container" data-url="<?=$room->join_link?>">
<a class="join_url show-from-w5" title="<?=$room->join_link?>"
><?=truncate($room->join_link, 32)?></a>
<a class="noscript" href="<?=$room->join_link?>"
<div class="join_url_container" data-url="<?=$join_link?>">
<a class="join_url show-from-w5" title="<?=$join_link?>"
><?=truncate($join_link, 32)?></a>
<a class="noscript" href="<?=$join_link?>" rel="external nofollow"
>Copy link</a>
</div>
</td>

@ -13,17 +13,35 @@
<head>
<?php include "+components/page-head.php" ?>
<link rel="canonical" href="https://sessioncommunities.online/">
<link rel="stylesheet" href="styles2.css">
<script type="module" src="main.js"></script>
<script type="module" src="./main.js"></script>
<title>Self-updating list of active Session communities</title>
<meta name="description" content="
Directory of Session Open Groups — public chatrooms within Session Messenger.
Copy these Communities into the Session app
and talk anonymously about Privacy, Security, or Cryptocurrency.
">
<meta name="modified" content="<?=date("Y-m-d H:i:s", $timestamp)?>">
<meta property="og:title" content="Click here for Session Communities">
<meta
property="og:description"
content="<?=count($rooms_assoc)?> Communities and counting — updated every day!"
>
<meta property="og:url" content="https://sessioncommunities.online/">
<meta property="og:type" content="website">
<meta property="og:locale" content="en_US"/>
<meta name="timestamp" content="<?=$timestamp?>">
</head>
<body onload="onLoad(<?php echo $timestamp ?>)">
<body>
<header>
<div id="header-start"></div>
<div id="header-end">
<a
id="link-instructions"
target="_blank"
rel="help"
title="Mutli-language guide to joining communities using the site."
href="instructions.html"
>Instructions</a>
</div>
@ -50,10 +68,16 @@
</p>
<p id="disclaimer">
This site is not affiliated with
<a href="https://optf.ngo">Oxen Privacy Tech Foundation</a>.
<a
href="https://optf.ngo"
target="_blank"
>Oxen Privacy Tech Foundation</a>.
<br>
Communities shown are fetched automatically from
various sources.
<a
href="https://github.com/mdPlusPlus/sessioncommunities.online#which-sources-are-crawled"
target="_blank"
>various sources</a>.
<br>
We make an attempt to hide communities containing
objectionable or illegal content, but
@ -65,8 +89,13 @@
only available with JS enabled.
</p>
<p>
<label for="toggle-show-room-ids" class="clickable anchorstyle">
Toggle room identifier display
<label
for="toggle-show-room-ids"
class="clickable anchorstyle"
tabindex="0"
title="Shows a column with community identifiers
for developers or language data contributors.">
Toggle room identifier display
</label>
</p>
<nav>
@ -83,17 +112,30 @@
<a
href="https://github.com/oxen-io/session-pysogs"
target="_blank"
title="Information about running a community server"
title="Information about running a community server."
>Host Your Own Community</a>
<a
href="https://getsession.org/terms-of-service"
target="_blank"
>Session Terms Of Service</a>
</nav>
<nav>
<a
href="https://github.com/mdPlusPlus/sessioncommunities.online"
target="_blank"
title="sessioncommunities.online repository on GitHub."
>Source Code & Contact</a>
>Source Code</a>
<a
href="https://lokilocker.com/someguy/sessioncommunities.online"
target="_blank"
title="sessioncommunities.online repository on Lokinet Gitea."
>Source Code (Mirror)</a>
<a
href="https://github.com/mdPlusPlus/sessioncommunities.online#contact"
target="_blank"
rel="author"
title="Information on how to contact the maintainer of sessioncommunities.online"
>Contact</a>
</nav>
</footer>
<div id="copy-snackbar">

@ -1,46 +1,66 @@
<?php
include_once "+getenv.php";
$instruction_files = glob("+instructions/*.txt");
function file_language($file) { return pathinfo($file)['filename']; }
?>
<!DOCTYPE html>
<html>
<head>
<?php include "+components/page-head.php" ?>
<link rel="canonical" href="https://sessioncommunities.online/instructions.php">
<link rel="stylesheet" href="css/instructions.css">
<style type="text/css">
<?php foreach ($instruction_files as $i => $file): ?>
#language-selection-<?=$i?>:checked ~
#language-selection-<?=$i?>:checked ~
#instructions #instructions-<?=$i?> {
display: block;
}
<?php endforeach; ?>
</style>
<meta name="description" content="
Discover how to use the sessioncommunities.online website
to help you join Session Open Groups; available in <?php
// Print languages instructions are available in.
$languages = array_map('file_language', array_slice($instruction_files, 0, 10));
echo(join(", ", $languages));
?>. In short: Use the Copy button or scan the QR code!
">
<meta property="og:title" content="How to join Session Communities">
<meta property="og:description" content="Learn how to use sessioncommunities.online to join">
<meta property="og:url" content="https://sessioncommunities.online/instructions.php">
<meta property="og:type" content="article">
</head>
<body>
<header>
<h1>Instructions for joining Session Communities</h1>
</header>
<main>
<p>
<a href="/" title="Return to the Session Community listing">
Go back to Community list
</a>
</p>
Choose your language:
<?php foreach ($instruction_files as $i => $file): ?>
<br>
<input
<input
id="language-selection-<?=$i?>"
class="language-selection"
name="language"
type="radio"
<?=file_language($file) == 'English' ? 'checked="checked"' : ''?>
>
<label for="language-selection-<?=$i?>">
<?=
// Name of the language
// Can be later parsed from i.e. first line of file
pathinfo($file)['filename']
file_language($file);
?>
</label>
<?php endforeach; ?>
<article id="instructions">
<?php foreach ($instruction_files as $i => $file): ?>
<section id="instructions-<?=$i?>" class="instructions"><?php