feat: render about page from readme
parent
8dec9a29c2
commit
3514e9c020
@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
require_once 'logging.php';
|
||||
require_once 'utils.php';
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Compile Markdown text.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Emits HTML code for Markdown file.
|
||||
* @param string $file Path to Markdown file.
|
||||
* @return void
|
||||
*/
|
||||
function include_markdown(string $file) {
|
||||
$builder = new MarkdownDocumentParser();
|
||||
|
||||
foreach (file($file) as $line) {
|
||||
$line = trim($line, "\n\r");
|
||||
$builder->readLine($line);
|
||||
}
|
||||
|
||||
$builder->emit();
|
||||
}
|
||||
|
||||
class MarkdownDocumentParser {
|
||||
/**
|
||||
* @var MarkdownEntity[] $stack
|
||||
*/
|
||||
private array $stack;
|
||||
private string $document;
|
||||
|
||||
public function __construct() {
|
||||
$this->document = "";
|
||||
$this->stack = [];
|
||||
}
|
||||
|
||||
public function emit() {
|
||||
if ($this->topTypeIs('p')) {
|
||||
$this->pop();
|
||||
}
|
||||
|
||||
$stack_size = count($this->stack);
|
||||
|
||||
if ($stack_size > 0) {
|
||||
log_warning("Stack has $stack_size markdown entities");
|
||||
}
|
||||
|
||||
while (count($this->stack) > 0) {
|
||||
$this->pop();
|
||||
}
|
||||
|
||||
echo $this->document;
|
||||
}
|
||||
|
||||
public function readLine(string $line) {
|
||||
$this->readStartOfLineEntity($line);
|
||||
|
||||
$this->readEntitiesFrom($line);
|
||||
|
||||
$this->popOneLiners();
|
||||
}
|
||||
|
||||
private function readEntitiesFrom(string &$line) {
|
||||
$count = 0;
|
||||
while (strlen($line) > 0) {
|
||||
$this->readEntityFrom($line);
|
||||
$count += 1;
|
||||
if ($count > 5000) {
|
||||
throw new LogicException("Too many entities on line");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function peek(): ?MarkdownEntity {
|
||||
return $this->stack[count($this->stack) - 1] ?? null;
|
||||
}
|
||||
|
||||
private function push(string $type, array $props = []) {
|
||||
$node = new MarkdownEntity($type);
|
||||
foreach ($props as $key => $val) {
|
||||
$node->setProp($key, $val);
|
||||
}
|
||||
$this->document .= $node->renderStart();
|
||||
array_push($this->stack, $node);
|
||||
}
|
||||
|
||||
private function pop() {
|
||||
/**
|
||||
* @var MarkdownEntity $node
|
||||
*/
|
||||
$node = array_pop($this->stack);
|
||||
$this->document .= $node->renderRest();
|
||||
}
|
||||
|
||||
private function topType(): ?string {
|
||||
$top = $this->peek();
|
||||
if ($top !== null) {
|
||||
return $top->type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function topTypeIs(string... $matches): bool {
|
||||
return in_array($this->topType(), $matches);
|
||||
}
|
||||
|
||||
private function countOf(string $type): int {
|
||||
$count = 0;
|
||||
foreach ($this->stack as $node) {
|
||||
if ($node->type == $type) {
|
||||
$count += 1;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function readStartOfLineEntity(string &$line) {
|
||||
if (strlen(trim($line)) == 0) {
|
||||
if ($this->topTypeIs('p')) {
|
||||
$this->pop();
|
||||
}
|
||||
} else if (remove_prefix($line, "#### ")) {
|
||||
$this->push('h4', [ 'docpos' => strlen($this->document) ]);
|
||||
} else if (remove_prefix($line, "### ")) {
|
||||
$this->push('h3', [ 'docpos' => strlen($this->document) ]);
|
||||
} else if (remove_prefix($line, "## ")) {
|
||||
$this->push('h2', [ 'docpos' => strlen($this->document) ]);
|
||||
} else if (remove_prefix($line, "# ")) {
|
||||
$this->push('h1', [ 'docpos' => strlen($this->document) ]);
|
||||
} else if ($this->topTypeIs('li', 'ul', 'p') && $this->countOf('li') > 0) {
|
||||
do {
|
||||
$indent = str_repeat(" ", 2 * $this->countOf("li"));
|
||||
|
||||
if (remove_prefix($line, "{$indent}- ")) {
|
||||
if ($this->topTypeIs('ul')) {
|
||||
$this->push('li');
|
||||
break;
|
||||
} else if ($this->topTypeIs('li')) {
|
||||
// one more indent satisfied in this case
|
||||
$this->push('ul');
|
||||
$this->push('li');
|
||||
break;
|
||||
}
|
||||
} else if (!$this->topTypeIs('p') && remove_prefix($line, "{$indent}")) {
|
||||
if ($this->topTypeIs('li')) {
|
||||
$this->push('p');
|
||||
break;
|
||||
} else if ($this->topTypeIs('ul')) {
|
||||
$this->pop();
|
||||
$this->push('p');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pop();
|
||||
} while ($this->topTypeIs('li', 'ul', 'p'));
|
||||
} else if (remove_prefix($line, "- ")) {
|
||||
$this->push('ul');
|
||||
$this->push('li');
|
||||
} else if (!$this->topTypeIs('p')) {
|
||||
$this->push('p');
|
||||
}
|
||||
}
|
||||
|
||||
public function popOneLiners() {
|
||||
while ($this->topTypeIs('h1', 'h2', 'h3', 'h4', 'h5', 'h6')) {
|
||||
// Need to retroactively add heading id for anchors
|
||||
$start = (int) $this->peek()->getProp("docpos");
|
||||
$taglen = strlen($this->peek()->renderStart());
|
||||
$contents = substr($this->document, $start + $taglen);
|
||||
$contents = strip_tags($contents);
|
||||
$slug = trim(preg_replace("/[^a-z]/", '-', trim(strtolower($contents))), '-');
|
||||
$this->document = substr_replace($this->document, " id=\"$slug\"", $start + $taglen - 1, 0);
|
||||
$this->pop();
|
||||
}
|
||||
}
|
||||
|
||||
public function readEntityFrom(string &$line) {
|
||||
if (str_starts_with($line, "[")) {
|
||||
$matches = [];
|
||||
if (preg_match('/^\[(.*?[^\\\\])\]\((.+?)\)/', $line, $matches)) {
|
||||
$this->push('a', [ 'href' => $matches[2] ]);
|
||||
$contents = str_replace("\\]", "]", $matches[1]);
|
||||
$this->readEntitiesFrom($contents);
|
||||
$this->pop();
|
||||
remove_prefix($line, $matches[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (str_starts_with($line, "<")) {
|
||||
$matches = [];
|
||||
if (preg_match('/^\<(.*?[^\\\\])\>/', $line, $matches)) {
|
||||
$contents = str_replace("\\>", ">", $matches[1]);
|
||||
$this->push('a', [ 'href' => $contents ]);
|
||||
$this->readEntitiesFrom($contents);
|
||||
$this->pop();
|
||||
remove_prefix($line, $matches[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (str_starts_with($line, "**")) {
|
||||
$matches = [];
|
||||
if (preg_match('/\*\*(.*?[^\\\\])\*\*/', $line, $matches)) {
|
||||
$this->push('strong');
|
||||
$contents = str_replace("\\**", "**", $matches[1]);
|
||||
$this->readEntitiesFrom($contents);
|
||||
$this->pop();
|
||||
remove_prefix($line, $matches[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (str_starts_with($line, "*")) {
|
||||
$matches = [];
|
||||
if (preg_match('/\*(.*?[^\\\\])\*/', $line, $matches)) {
|
||||
$this->push('em');
|
||||
$contents = str_replace("\\*", "*", $matches[1]);
|
||||
$this->readEntitiesFrom($contents);
|
||||
$this->pop();
|
||||
remove_prefix($line, $matches[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (str_starts_with($line, "`")) {
|
||||
$matches = [];
|
||||
if (preg_match('/\`(.*?[^\\\\])\`/', $line, $matches)) {
|
||||
$this->push('code');
|
||||
$contents = str_replace("\\`", "`", $matches[1]);
|
||||
$this->readEntitiesFrom($contents);
|
||||
$this->pop();
|
||||
remove_prefix($line, $matches[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// consume word
|
||||
$matches = [];
|
||||
if (preg_match('/^\s*\S+\s*/', $line, $matches)) {
|
||||
if (!str_ends_with($this->document, " ")
|
||||
&& !str_ends_with($this->document, ">")
|
||||
&& !str_starts_with($matches[0], " ")) {
|
||||
$this->document .= " ";
|
||||
}
|
||||
$this->document .= $matches[0];
|
||||
remove_prefix($line, $matches[0]);
|
||||
return;
|
||||
}
|
||||
log_value($this, LoggingVerbosity::Warning);
|
||||
log_value($line, LoggingVerbosity::Warning);
|
||||
throw new LogicException("Could not parse any entity");
|
||||
}
|
||||
}
|
||||
|
||||
class MarkdownEntity {
|
||||
public function __construct(string $type) {
|
||||
$this->type = $type;
|
||||
$this->props = [];
|
||||
}
|
||||
|
||||
public function setProp(string $prop, string $value) {
|
||||
$this->props[$prop] = $value;
|
||||
}
|
||||
|
||||
public function appendProp(string $prop, string $value) {
|
||||
$this->props[$prop] ??= '';
|
||||
$this->props[$prop] .= $value;
|
||||
}
|
||||
|
||||
public function getProp(string $prop): string {
|
||||
return $this->props[$prop];
|
||||
}
|
||||
|
||||
public string $type;
|
||||
private array $props;
|
||||
|
||||
public function renderStart(): string {
|
||||
global $REPOSITORY_CANONICAL_URL_FILES;
|
||||
|
||||
$type = $this->type;
|
||||
switch ($type) {
|
||||
case 'a':
|
||||
$href = $this->getProp('href');
|
||||
if (!str_starts_with($href, "#") && !str_contains($href, "://")) {
|
||||
$href = "$REPOSITORY_CANONICAL_URL_FILES/$href";
|
||||
}
|
||||
return "\n<a href=\"{$href}\">";
|
||||
default: return "\n<$type>";
|
||||
}
|
||||
}
|
||||
|
||||
public function renderRest(): string {
|
||||
$type = $this->type;
|
||||
switch ($type) {
|
||||
default: return "</$type>";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,63 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* \file
|
||||
* Generate about page.
|
||||
*/
|
||||
// prerequisite include for sites
|
||||
require_once '+getenv.php';
|
||||
/**
|
||||
* \file
|
||||
* Generate about page.
|
||||
*/
|
||||
// prerequisite include for sites
|
||||
require_once '+getenv.php';
|
||||
require_once 'utils/markdown.php';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<?php include('+components/page-head.php'); ?>
|
||||
<?php include '+components/page-head.php'; ?>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/common-dark.css">
|
||||
<meta name="description" content="Learn more about sessioncommunities.online, the up-to-date list of public Session groups; learn how to get your Session Community listed or how to contact us.">
|
||||
<meta name="description"
|
||||
content="Learn more about sessioncommunities.online, the up-to-date list of public Session groups; learn how to get your Session Community listed or how to contact us.">
|
||||
<title>About — sessioncommunities.online</title>
|
||||
<meta property="og:title" content="About — sessioncommunities.online">
|
||||
<meta property="og:description" content="Session Communities are public chatrooms accessible from within Session Messenger. <?php
|
||||
?>This web project crawls known sources of Session Communities, and <?php
|
||||
?>displays information about them as a static HTML page.">
|
||||
?>This web project crawls known sources of Session Communities, and <?php
|
||||
?>displays information about them as a static HTML page.">
|
||||
<meta property="og:type" content="article">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 id="self-updating-list-of-session-communities">About our list of <a href="/" title="Visit sessioncommunities.online">Session Communities</a></h1>
|
||||
<h2 id="what-is-session-">What is Session?</h2>
|
||||
<p><a href="https://getsession.org/">Session</a> is a private messaging app that protects your meta-data,
|
||||
encrypts your communications, and makes sure your messaging activities
|
||||
leave no digital trail behind.</p>
|
||||
<h2>What are Session Communities? What does this site do?</h2>
|
||||
<p>Session Communities are public chatrooms accessible from within Session Messenger.
|
||||
This web project crawls known sources of Session Communities, and
|
||||
displays information about them as a static HTML page.</p>
|
||||
<h2 id="how-do-i-get-my-community-listed-">How do I get my Community listed?</h2>
|
||||
<p><strong>Answer</strong>: Submit your Community to one of our <a href="#which-sources-do-you-crawl">upstream sources</a>, or better yet, create another page with links to your favorite Communities for more redundancy.</p>
|
||||
<p>Then, read our <a href="<?=$REPOSITORY_CANONICAL_URL_FILES?>/for-sogops.md">recommendations for Community operators</a>.</p>
|
||||
<h2 id="which-sources-do-you-crawl-">Which sources do you crawl?</h2>
|
||||
<p>Communities displayed come from a variety of sources:</p>
|
||||
<ul>
|
||||
<li><a href="https://session.directory/">https://session.directory/</a>,</li>
|
||||
<li><a href="https://lokilocker.com/Mods/Session-Groups/wiki/Session-Open-Groups">https://lokilocker.com/Mods/Session-Groups/wiki/Session-Open-Groups</a>,</li>
|
||||
<li><a href="https://github.com/GNU-Linux-libre/Awesome-Session-Group-List">https://github.com/GNU-Linux-libre/Awesome-Session-Group-List</a>,</li>
|
||||
<li><a href="https://freearhkam.cc">https://freearhkam.cc</a>,</li>
|
||||
<li><a href="https://simplifiedprivacy.com/techgroups">https://simplifiedprivacy.com/techgroups</a>,</li>
|
||||
<li><a href="https://nsfwlisting.carrd.co/">https://nsfwlisting.carrd.co/</a>,</li>
|
||||
<li><a href="<?=$REPOSITORY_CANONICAL_URL_FILES?>/php/servers/known-servers.php">a few hardcoded servers found on the internet</a>.</li>
|
||||
</ul>
|
||||
Have a list we don't know about? <a href="#contact-us">Shoot us a message</a>.
|
||||
<h2 id="contributing">Contributing</h2>
|
||||
<p>See <a href="<?=$REPOSITORY_CANONICAL_URL_FILES?>/CONTRIBUTING.md">CONTRIBUTING.md</a> for information about running the project and contributing code, or <a href="<?=$REPOSITORY_CANONICAL_URL_FILES?>/languages/README.md">languages/README.md</a> to help add language labels for Communities.</p>
|
||||
<h2 id="policy">Policy</h2>
|
||||
<p>We require that content posted in Communities adheres to the Content Policy laid out in the <a href="https://getsession.org/terms-of-service">Session Terms of Service</a>. Additionally, we may consider Communities unsuitable for display if their content is in breach of copyright and/or encourages or instructs drug use.</p>
|
||||
<p>If Communities explicitly accept such content or fail to moderate such content, we may display these Communities only to users who have disabled JavaScript in their browser, or to none at all. Therefore, <strong>it is crucial your Community has adequate coverage by moderators</strong>.</p>
|
||||
<p>For safety reasons, we may also hide Communities intended for testing. We appreciate contact with and the continued vigilance of server operators to prevent any issues from arising that would lead to us de-listing your Community.</p>
|
||||
<h2 id="contact-us">Contact us</h2>
|
||||
<p>To report issues or make suggestions, <a href="https://codeberg.org/gravel/sessioncommunities.online/issues/new">file an issue on our repository</a> or <a href="<?=$SITE_CANONICAL_URL?>/#webdev+118d">visit the Web Development Community</a> on caliban.org. Be sure to include proper reasoning with your suggestion.</p>
|
||||
<p>In case your issue cannot be resolved publicly, contact <a href="https://codeberg.org/gravel/gravel">gravel</a> on Session as <code>gravel</code> (Registered Session ID).</p>
|
||||
|
||||
<?php include "+components/footer.php"; ?>
|
||||
|
||||
<?php include_markdown("$PROJECT_ROOT/README.md"); ?>
|
||||
|
||||
<?php include "+components/footer.php"; ?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue