docs: document symbols where reasonable

Doxygen is still producing lots of warnings due to bad parsing.

Includes one or two error checks for function constraints.
dev
gravel 1 year ago
parent 91818858f5
commit f9aa117e6b
Signed by: gravel
GPG Key ID: C0538F3C906B308F

@ -4,6 +4,13 @@
* Provide language flags for hardcoded Communities. * Provide language flags for hardcoded Communities.
*/ */
/**
* @var array<string, string> $server_languages
*
* Dictionary of language flags for hardcoded Communities.
*
* The array key a Community ID (long or legacy short).
*/
$server_languages = []; $server_languages = [];
// https://reccacon.com/Ukraine // https://reccacon.com/Ukraine

@ -8,10 +8,14 @@
require_once 'utils/getopt.php'; require_once 'utils/getopt.php';
/** /**
* Recursively match the last segment of the given path pattern. * Return file names matching the glob pattern in all subdirectories.
* @source https://stackoverflow.com/a/17161106 * @param string $pattern Glob pattern.
* @param int $flags Glob flags.
* @author Tony Chen
* @see https://stackoverflow.com/a/17161106
* @return string[] Array of file names.
*/ */
function rglob($pattern, $flags = 0) { function rglob($pattern, $flags = 0): array {
$files = glob($pattern, $flags); $files = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
$files = array_merge( $files = array_merge(
@ -22,7 +26,12 @@
return $files; return $files;
} }
function serialize_shell_environment(array $env_vars) { /**
* Produce shell code to set the environment variables given.
*
* @param string[] $env_vars Dictionary of environment variables.
*/
function serialize_shell_environment(array $env_vars): string {
$env_assignments = array_map(function(string $key, string $value) { $env_assignments = array_map(function(string $key, string $value) {
return "$key=".escapeshellarg($value); return "$key=".escapeshellarg($value);
}, array_keys($env_vars), array_values($env_vars)); }, array_keys($env_vars), array_values($env_vars));
@ -30,7 +39,10 @@
} }
/** /**
* Generate files from PHP templates in the templates directory. * Generate files from PHP templates.
*
* Templates used are in the {@link $TEMPLATES_ROOT} directory.
* Generated files are places in the {@link $DOCUMENT_ROOT} directory.
*/ */
function generate_files() { function generate_files() {
global $LOGGING_VERBOSITY, $TEMPLATES_ROOT, $DOCUMENT_ROOT; global $LOGGING_VERBOSITY, $TEMPLATES_ROOT, $DOCUMENT_ROOT;

@ -10,6 +10,10 @@
*/ */
$PROJECT_ROOT = dirname(__FILE__); $PROJECT_ROOT = dirname(__FILE__);
/**
* @var string $PHPENV_FILENAME
* File comtaining PHP environment variables. Marks root folder.
*/
$PHPENV_FILENAME = ".phpenv.php"; $PHPENV_FILENAME = ".phpenv.php";
(function(){ (function(){

@ -11,25 +11,50 @@
} }
/** /**
* Stored Community servers..
* @var CommunityServer[] $servers * @var CommunityServer[] $servers
*/ */
public array $servers; public array $servers;
/** /**
* Stored Communities.
* @var CommunityRoom[] $rooms * @var CommunityRoom[] $rooms
*/ */
public array $rooms; public array $rooms;
/**
* Read a database of Session Open Group servers and Communities.
*
* @param string $rooms_file JSON file containing fetched server data.
* Typically equal to {@link $ROOMS_FILE}.
*
* @return CommunityDatabase
*/
public static function read_from_file(string $rooms_file): CommunityDatabase { public static function read_from_file(string $rooms_file): CommunityDatabase {
$servers = CommunityServer::read_servers_from_file($rooms_file); $servers = CommunityServer::read_servers_from_file($rooms_file);
return new CommunityDatabase($servers); return new CommunityDatabase($servers);
} }
/**
* Re-fetch outdated assets for Communities stored by the database.
*
* @return CommunityDatabase self
*/
public function fetch_assets(): CommunityDatabase { public function fetch_assets(): CommunityDatabase {
CommunityRoom::fetch_assets($this->rooms); CommunityRoom::fetch_assets($this->rooms);
return $this; return $this;
} }
/**
* Return the Communities and servers stored by the database.
*
* Usage:
* ```php
* list($rooms, $servers) = communityDatabase->unpack();
* ```
*
* @return array
*/
public function unpack() { public function unpack() {
return [$this->rooms, $this->servers]; return [$this->rooms, $this->servers];
} }

@ -12,6 +12,11 @@
/** /**
* Construct Community listings from listing configuration and cached Communities. * Construct Community listings from listing configuration and cached Communities.
*
* @param string $listings_ini File containing listing configuration.
* @param CommunityServer[] $servers Array of Community servers used to resolve the listing. Leave empty to fetch from cache.
*
* @return CommunityListingDatabase
*/ */
public static function resolve_listings_from_ini(string $listings_ini, array $servers = null): CommunityListingDatabase { public static function resolve_listings_from_ini(string $listings_ini, array $servers = null): CommunityListingDatabase {
global $ROOMS_FILE; global $ROOMS_FILE;
@ -59,7 +64,17 @@
return array_values($this->listings); return array_values($this->listings);
} }
public function get_listing(string $id): CommunityListing { /**
* Get the Community listing with the given ID.
*
* @param string $id Configured listing identifier.
*
* @return CommunityListing
*/
public function get_listing(string $id): CommunityListing|null {
if (!isset($this->listings[$id])) {
throw new Error("No such listing: '$id'");
}
return $this->listings[$id]; return $this->listings[$id];
} }
} }

@ -173,6 +173,12 @@
/** /**
* Create incomplete CommunityRoom instance from intermediate data. * Create incomplete CommunityRoom instance from intermediate data.
*
* Use when room data has been fetched, but the server's public key is still unknown.
*
* @param CommunityServer $server Open Group Server hosting given Community.
* @param array $details Associative data describing given Community.
* @return CommunityRoom Incomplete CommunityRoom instance. Expect errors.
*/ */
public static function _from_intermediate_data( public static function _from_intermediate_data(
CommunityServer $server, CommunityServer $server,
@ -273,7 +279,9 @@
/** /**
* Create a CommunityRoom instance from associative data. * Create a CommunityRoom instance from associative data.
* @param CommunityServer $server * @param CommunityServer $server Open Group Server hosting given Community.
* @param array $details Associative data describing Community.
* @return CommunityRoom
*/ */
public static function from_details($server, array $details) { public static function from_details($server, array $details) {
return new CommunityRoom($server, $details); return new CommunityRoom($server, $details);
@ -281,7 +289,8 @@
/** /**
* Create an array of CommunityRoom instances from associative data. * Create an array of CommunityRoom instances from associative data.
* @param array[] $details * @param CommunityServer $server Open Group server hosting the given rooms.
* @param array[] $details_array Array of associative arrays holding room data.
* @return CommunityRoom[] * @return CommunityRoom[]
*/ */
public static function from_details_array($server, array $details_array) { public static function from_details_array($server, array $details_array) {
@ -294,9 +303,11 @@
* Sort Community rooms in-place by the given string property. * Sort Community rooms in-place by the given string property.
* @param CommunityRoom[] $rooms Rooms to sort by given key. * @param CommunityRoom[] $rooms Rooms to sort by given key.
* @param string $key String property of CommunityRoom to sort by. * @param string $key String property of CommunityRoom to sort by.
* @param bool $descending If true, sort in descending order.
* @return void
*/ */
public static function sort_rooms_str(array &$rooms, string $key, bool $reverse = false) { public static function sort_rooms_str(array &$rooms, string $key, bool $descending = false) {
usort($rooms, $reverse ? function(CommunityRoom $a, CommunityRoom $b) use ($key) { usort($rooms, $descending ? function(CommunityRoom $a, CommunityRoom $b) use ($key) {
return strcmp( return strcmp(
$b->$key, $b->$key,
$a->$key $a->$key
@ -313,9 +324,11 @@
* Sort Community rooms in-place by the given numeric property. * Sort Community rooms in-place by the given numeric property.
* @param CommunityRoom[] $rooms Rooms to sort by given key. * @param CommunityRoom[] $rooms Rooms to sort by given key.
* @param string $key Numeric property of CommunityRoom to sort by. * @param string $key Numeric property of CommunityRoom to sort by.
* @param bool $descending If true, sort in descending order.
* @return void
*/ */
public static function sort_rooms_num(array &$rooms, string $key, bool $reverse = false) { public static function sort_rooms_num(array &$rooms, string $key, bool $descending = false) {
usort($rooms, $reverse ? function(CommunityRoom $a, CommunityRoom $b) use ($key) { usort($rooms, $descending ? function(CommunityRoom $a, CommunityRoom $b) use ($key) {
return $b->$key - $a->$key; return $b->$key - $a->$key;
} : function(CommunityRoom $a, CommunityRoom $b) use ($key) { } : function(CommunityRoom $a, CommunityRoom $b) use ($key) {
return $a->$key - $b->$key; return $a->$key - $b->$key;
@ -325,6 +338,8 @@
/** /**
* Sort Community rooms in-place by their server. * Sort Community rooms in-place by their server.
* @param CommunityRoom[] $rooms Rooms to sort by server. * @param CommunityRoom[] $rooms Rooms to sort by server.
* @param bool $random If true, use random server order to sort rooms.
* @return void
*/ */
public static function sort_rooms_by_server(array &$rooms, bool $random = false) { public static function sort_rooms_by_server(array &$rooms, bool $random = false) {
if ($random) { if ($random) {
@ -345,7 +360,9 @@
} }
/** /**
* Re-fetch assets for the given Communities as necessary.
* @param CommunityRoom[] $rooms * @param CommunityRoom[] $rooms
* @return void
*/ */
public static function fetch_assets(array $rooms) { public static function fetch_assets(array $rooms) {
// Sequential in each server, see note in fetch_room_hints_coroutine() // Sequential in each server, see note in fetch_room_hints_coroutine()
@ -362,9 +379,12 @@
} }
/** /**
* @param CommunityRoom[] $rooms * Keep only pinned Communities from the input list.
* @param CommunityRoom[] $rooms Input list of Communities.
* @param CommunityRoom[] $rest (output) Remainder of Communities from the list. (optional)
* @return CommunityRoom[] Pinned Communities.
*/ */
public static function get_stickied_rooms(array &$rooms, array &$rest = null) { public static function get_stickied_rooms(array $rooms, array &$rest = null) {
global $STICKIED_ROOMS; global $STICKIED_ROOMS;
return CommunityRoom::select_rooms($rooms, $STICKIED_ROOMS, unmatched: $rest); return CommunityRoom::select_rooms($rooms, $STICKIED_ROOMS, unmatched: $rest);
} }
@ -538,7 +558,7 @@
* Check whether the given list matches the current Community or its parent server. * Check whether the given list matches the current Community or its parent server.
* @param string[] $filter * @param string[] $filter
* Array of unique room identifiers, server pubkeys and/or server hostnames. * Array of unique room identifiers, server pubkeys and/or server hostnames.
* @param string $matchee String matching room. Output parameter. * @param string $matchee (output) String matching current room.
* @return bool True if the array matches the Community, false otherwise. * @return bool True if the array matches the Community, false otherwise.
*/ */
public function matched_by_list(array $filter, string &$matchee = null): bool { public function matched_by_list(array $filter, string &$matchee = null): bool {
@ -553,9 +573,11 @@
} }
/** /**
* Select Communities matching the given filter list.
* @param CommunityRoom[] $rooms * @param CommunityRoom[] $rooms
* @param string[] $filter * @param string[] $filter List of room identifiers, server pubkeys and/or hostnames.
* @param string[] $matchees output parameter * @param string[] $matchees (output) Filter list items used to select at least one Community. (optional)
* @param CommunityRoom[] $unmatched (output) Communities not matched by any filter items. (optional)
* @return CommunityRoom[] * @return CommunityRoom[]
*/ */
public static function select_rooms(array $rooms, array|string $filter, array &$matchees = null, array &$unmatched = null): array { public static function select_rooms(array $rooms, array|string $filter, array &$matchees = null, array &$unmatched = null): array {
@ -615,11 +637,21 @@
return $this->has_nsfw_keywords() || $this->matched_by_list($NSFW_INCLUDE); return $this->has_nsfw_keywords() || $this->matched_by_list($NSFW_INCLUDE);
} }
/**
* Return true if the Community is marked for testing.
*
* @return bool
*/
public function is_testing_room(): bool { public function is_testing_room(): bool {
global $TESTING_INCLUDE; global $TESTING_INCLUDE;
return in_array("test", $this->string_tags) || $this->matched_by_list($TESTING_INCLUDE); return in_array("test", $this->string_tags) || $this->matched_by_list($TESTING_INCLUDE);
} }
/**
* Return true if the Community is manually stickied.
*
* @return bool
*/
public function is_stickied_room(): bool { public function is_stickied_room(): bool {
global $STICKIED_ROOMS; global $STICKIED_ROOMS;
return $this->matched_by_list($STICKIED_ROOMS); return $this->matched_by_list($STICKIED_ROOMS);
@ -642,7 +674,12 @@
return 0; return 0;
} }
public function get_recommended_staff_count() { /**
* Compute the recommended threshold for Community staff count.
*
* @return float
*/
public function get_recommended_staff_count(): float {
if ($this->active_users == null || $this->active_users == 0) return INF; if ($this->active_users == null || $this->active_users == 0) return INF;
$recommended_staff_count = ceil($this->active_users ** 0.25); $recommended_staff_count = ceil($this->active_users ** 0.25);
return max(2, $recommended_staff_count); return max(2, $recommended_staff_count);
@ -650,18 +687,30 @@
/** /**
* Estimate whether the Community has enough staff. * Estimate whether the Community has enough staff.
*
* @return bool
*/ */
public function has_good_staff_rating(): bool { public function has_good_staff_rating(): bool {
$staff_count = count($this->get_staff()); $staff_count = count($this->get_staff());
return $staff_count >= $this->get_recommended_staff_count(); return $staff_count >= $this->get_recommended_staff_count();
} }
public function get_numeric_staff_rating() { /**
* Return a rating for the Community's staff count relative to active users.
*
* @return float
*/
public function get_numeric_staff_rating(): float {
if (!$this->write || !$this->read) return 2; if (!$this->write || !$this->read) return 2;
return min(2, count($this->get_staff()) / $this->get_recommended_staff_count()); return min(2, count($this->get_staff()) / $this->get_recommended_staff_count());
} }
public function get_minimal_staff_count() { /**
* Compute the lower threshold for Community staff count.
*
* @return float
*/
public function get_minimal_staff_count(): float {
if ($this->active_users == null || $this->active_users == 0) return INF; if ($this->active_users == null || $this->active_users == 0) return INF;
$minimal_staff_count = 1 + round((0.38 * log($this->active_users)) ** 1.15); $minimal_staff_count = 1 + round((0.38 * log($this->active_users)) ** 1.15);
return max(2, $minimal_staff_count); return max(2, $minimal_staff_count);
@ -858,6 +907,7 @@
/** /**
* Sort an array of servers in place based on URL. * Sort an array of servers in place based on URL.
* @param CommunityServer[] &$servers * @param CommunityServer[] &$servers
* @return void
*/ */
static function sort_by_url(array &$servers) { static function sort_by_url(array &$servers) {
usort($servers, 'CommunityServer::compare_by_url'); usort($servers, 'CommunityServer::compare_by_url');
@ -878,6 +928,7 @@
/** /**
* Sorts an array of servers in place by public key. * Sorts an array of servers in place by public key.
* @param CommunityServer[] $servers * @param CommunityServer[] $servers
* @return void
*/ */
public static function sort_by_pubkey(&$servers) { public static function sort_by_pubkey(&$servers) {
foreach ($servers as $server) { foreach ($servers as $server) {
@ -893,6 +944,9 @@
/** /**
* Return true whether the two servers given share a room. * Return true whether the two servers given share a room.
* @param CommunityServer $a First server to compare.
* @param CommunityServer $b Second server to compare.
* @return bool
*/ */
public static function rooms_in_common(CommunityServer $a, CommunityServer $b): bool { public static function rooms_in_common(CommunityServer $a, CommunityServer $b): bool {
// Rely on at least token or creation date differing. // Rely on at least token or creation date differing.
@ -1122,7 +1176,7 @@
/** /**
* Create Community server instance from array loaded server data. * Create Community server instance from array loaded server data.
* @param array $details Decoded JSON associative arrays about server. * @param array $details_array Array of associative arrays holding server data.
* @return CommunityServer[] Servers represented by given data. * @return CommunityServer[] Servers represented by given data.
*/ */
static function from_details_array(array $details_array) { static function from_details_array(array $details_array) {
@ -1139,6 +1193,7 @@
* Add to the given servers additional data extracted from our sources. * Add to the given servers additional data extracted from our sources.
* @param CommunityServer[] $servers * @param CommunityServer[] $servers
* @param CommunitySources $source * @param CommunitySources $source
* @return void
*/ */
static function source_additional_info(array $servers, CommunitySources $source): void { static function source_additional_info(array $servers, CommunitySources $source): void {
foreach ($servers as $server) { foreach ($servers as $server) {
@ -1214,13 +1269,17 @@
/** /**
* Returns the hostname for this server. * Returns the hostname for this server.
* @param bool $include_scheme [optional] * @param bool $include_port
* Includes the port. `true` by default. * Include port in output, if provided. Default: `true`.
* @return string URL with hostname and port, if applicable. * @return string URL with hostname and port, if applicable.
* Scheme not included. * Scheme not included.
*/ */
function get_hostname(bool $include_port = true) { function get_hostname(bool $include_port = true) {
return url_get_base($this->base_url, include_scheme: false, include_port: $include_port); return url_get_base(
$this->base_url,
include_scheme: false,
include_port: $include_port
);
} }
/** /**
@ -1232,7 +1291,9 @@
} }
/** /**
* Returns the URL to the endpoint listing this server's rooms. * Returns the URL to the endpoint describing this server's rooms.
*
* @return string
*/ */
function get_rooms_api_url(): string { function get_rooms_api_url(): string {
$base_url = $this->base_url; $base_url = $this->base_url;
@ -1240,7 +1301,11 @@
} }
/** /**
* Returns the URL for the endpoint of the particular room. * Returns the URL for the endpoint describing a particular room.
*
* @param string $token Token of Community to construct URL.
*
* @return string
*/ */
function get_room_api_url(string $token): string { function get_room_api_url(string $token): string {
$base_url = $this->base_url; $base_url = $this->base_url;
@ -1318,12 +1383,19 @@
return $pubkey_prefix . $hostname_hash_prefix; return $pubkey_prefix . $hostname_hash_prefix;
} }
/**
* Return string value used to sort Communities by host.
*
* @return string
*/
public function get_server_sort_key(): string { public function get_server_sort_key(): string {
return $this->get_pubkey() . $this->get_hostname(); return $this->get_pubkey() . $this->get_hostname();
} }
/** /**
* Returns the room of the given token, or null if one does not exist. * Returns the room of the given token, or null if one does not exist.
* @param string $token The string token of a room on the server.
* @return CommunityRoom|null
*/ */
function get_room_by_token(string $token): CommunityRoom | null { function get_room_by_token(string $token): CommunityRoom | null {
$candidates = array_filter($this->rooms, function(CommunityRoom $room) use ($token) { $candidates = array_filter($this->rooms, function(CommunityRoom $room) use ($token) {
@ -1577,7 +1649,14 @@
return true; return true;
} }
/**
* Construct full-fledged Community objects if the public key is available.
* @return void
*/
public function construct_rooms() { public function construct_rooms() {
if (!$this->has_pubkey()) {
throw new Error("Cannot construct rooms before pubkey is fetched");
}
$this->rooms = CommunityRoom::from_details_array( $this->rooms = CommunityRoom::from_details_array(
$this, $this,
$this->_intermediate_room_data $this->_intermediate_room_data
@ -1585,7 +1664,9 @@
} }
/** /**
* @return CommunityServer[] * Deserialize Community servers from the given JSON file.
* @param string $file Path to JSON file containing fetched server data.
* @return CommunityServer[] Array of Session Open Group servers.
*/ */
public static function read_servers_from_file(string $file): array { public static function read_servers_from_file(string $file): array {
// Read the server data from disk. // Read the server data from disk.

@ -22,8 +22,12 @@
} }
/** /**
* Create new instance of SDIRCommunitySource on the given source text. * Create new instance of SDIRCommunitySource.
* Returns false if processing the source fails. * Returns false if processing the source fails.
*
* @param string $contents Text from Session.directory to process.
*
* @return SDIRCommunitySource|false
*/ */
public static function from_contents(string $contents): SDIRCommunitySource | false { public static function from_contents(string $contents): SDIRCommunitySource | false {
$source = new SDIRCommunitySource($contents); $source = new SDIRCommunitySource($contents);
@ -142,8 +146,13 @@
private array $tags = []; private array $tags = [];
/** /**
* Attempt to create an ASGLCommunitySource instance on the given source text. * Attempt to create an ASGLCommunitySource instance.
*
* Returns false if processing the source fails. * Returns false if processing the source fails.
*
* @param string $contents Text from ASGL to process.
*
* @return ASGLCommunitySource|false
*/ */
public static function from_contents(string $contents): ASGLCommunitySource | false { public static function from_contents(string $contents): ASGLCommunitySource | false {
$source = new ASGLCommunitySource($contents); $source = new ASGLCommunitySource($contents);

@ -75,10 +75,16 @@
return html_sanitize($this->get_text()); return html_sanitize($this->get_text());
} }
public static $descriptions = []; /**
* @var string[] $descriptions
* Dictionary of reserved tag descriptions.
*/
public static array $descriptions = [];
/** /**
* Return tag description. * Return the tag's description.
*
* @return string
*/ */
public function get_description_sanitized(): string { public function get_description_sanitized(): string {
// Feels out-of-place anywhere else. // Feels out-of-place anywhere else.
@ -89,15 +95,34 @@
return html_sanitize(CommunityTag::$descriptions[$this->text] ?? "Tag: $this->text"); return html_sanitize(CommunityTag::$descriptions[$this->text] ?? "Tag: $this->text");
} }
/**
* Associate the current tag's text with the given description.
*
* @param string $description New description for the current tag text.
*
* @return CommunityTag Return self.
*/
public function set_description_globally(string $description): self { public function set_description_globally(string $description): self {
CommunityTag::$descriptions[$this->text] = $description; CommunityTag::$descriptions[$this->text] = $description;
return $this; return $this;
} }
public static function serializeClassData() { /**
* Serialize tag data into a string.
*
* @return string JSON data
*/
public static function serializeClassData(): string {
return json_encode(CommunityTag::$descriptions); return json_encode(CommunityTag::$descriptions);
} }
/**
* Load tag data from the given serialized string.
*
* @param string $data JSON string of tag descriptions.
*
* @return void
*/
public static function loadSerializedClassData(string $data) { public static function loadSerializedClassData(string $data) {
CommunityTag::$descriptions = json_decode($data, associative: true); CommunityTag::$descriptions = json_decode($data, associative: true);
} }
@ -258,9 +283,18 @@
private const REDUNDANT_TAGS = []; private const REDUNDANT_TAGS = [];
/**
* @var string[] NSFW_KEYWORDS
* Keywords indicating a not-safe-for-work Community.
*/
public const NSFW_KEYWORDS = ["nsfw", "porn", "erotic", "18+", "sex"]; public const NSFW_KEYWORDS = ["nsfw", "porn", "erotic", "18+", "sex"];
/** /**
* Check whether the given user tag is reserved by our aggregator. * Check whether the given user tag is reserved by our aggregator.
*
* @param string $tag String tag to check.
*
* @return bool
*/ */
public static function is_reserved_tag(string $tag): bool { public static function is_reserved_tag(string $tag): bool {
return in_array(strtolower($tag), CommunityTag::RESERVED_TAGS); return in_array(strtolower($tag), CommunityTag::RESERVED_TAGS);
@ -268,6 +302,10 @@
/** /**
* Return true if the tag should be given a chance to appear in more crowded views. * Return true if the tag should be given a chance to appear in more crowded views.
*
* @param string $tag String tag to check.
*
* @return bool
*/ */
public static function is_showcased_tag(string $tag): bool { public static function is_showcased_tag(string $tag): bool {
return in_array(strtolower($tag), CommunityTag::SHOWCASED_TAGS); return in_array(strtolower($tag), CommunityTag::SHOWCASED_TAGS);
@ -275,6 +313,10 @@
/** /**
* Return true if the tag should be given visibility in more crowded views. * Return true if the tag should be given visibility in more crowded views.
*
* @param string $tag String tag to check.
*
* @return bool
*/ */
public static function is_highlighted_tag(string $tag): bool { public static function is_highlighted_tag(string $tag): bool {
return in_array(strtolower($tag), CommunityTag::HIGHLIGHTED_TAGS); return in_array(strtolower($tag), CommunityTag::HIGHLIGHTED_TAGS);
@ -285,7 +327,12 @@
* Constructs Community tags reserved by the aggregator. * Constructs Community tags reserved by the aggregator.
*/ */
class ReservedTags { class ReservedTags {
public static function official() { /**
* Return "official" reserved Community tag.
*
* @return CommunityTag
*/
public static function official(): CommunityTag {
$CHECK_MARK = "✅"; $CHECK_MARK = "✅";
return (new CommunityTag( return (new CommunityTag(
@ -294,7 +341,12 @@
))->set_description_globally("This Community is maintained by the Session team. $CHECK_MARK"); ))->set_description_globally("This Community is maintained by the Session team. $CHECK_MARK");
} }
public static function nsfw() { /**
* Return "nsfw" reserved Community tag.
*
* @return CommunityTag
*/
public static function nsfw(): CommunityTag {
$WARNING_ICON = "⚠️"; $WARNING_ICON = "⚠️";
return (new CommunityTag( return (new CommunityTag(
@ -303,7 +355,12 @@
))->set_description_globally("This Community may contain adult material. $WARNING_ICON"); ))->set_description_globally("This Community may contain adult material. $WARNING_ICON");
} }
public static function moderated() { /**
* Return "moderated" reserved Community tag.
*
* @return CommunityTag
*/
public static function moderated(): CommunityTag {
$CHECK_MARK = "✅"; $CHECK_MARK = "✅";
return (new CommunityTag( return (new CommunityTag(
@ -312,7 +369,12 @@
))->set_description_globally("This Community seems to have enough moderators. $CHECK_MARK"); ))->set_description_globally("This Community seems to have enough moderators. $CHECK_MARK");
} }
public static function not_modded() { /**
* Return "not_modded" reserved Community tag.
*
* @return CommunityTag
*/
public static function not_modded(): CommunityTag {
$WARNING_ICON = "⚠️"; $WARNING_ICON = "⚠️";
return (new CommunityTag( return (new CommunityTag(
@ -321,28 +383,48 @@
))->set_description_globally("This Community does not seem to have enough moderators. $WARNING_ICON"); ))->set_description_globally("This Community does not seem to have enough moderators. $WARNING_ICON");
} }
public static function read_only() { /**
* Return "read_only" reserved Community tag.
*
* @return CommunityTag
*/
public static function read_only(): CommunityTag {
return (new CommunityTag( return (new CommunityTag(
"read-only", "read-only",
TagType::RESERVED_TAG, TagType::RESERVED_TAG,
))->set_description_globally("This Community is read-only."); ))->set_description_globally("This Community is read-only.");
} }
public static function no_upload_permission() { /**
* Return "no_upload_permission" reserved Community tag.
*
* @return CommunityTag
*/
public static function no_upload_permission(): CommunityTag {
return (new CommunityTag( return (new CommunityTag(
"uploads off", "uploads off",
TagType::RESERVED_TAG, TagType::RESERVED_TAG,
))->set_description_globally("This Community does not support uploading files or link previews."); ))->set_description_globally("This Community does not support uploading files or link previews.");
} }
public static function recently_created() { /**
* Return "recently_created" reserved Community tag.
*
* @return CommunityTag
*/
public static function recently_created(): CommunityTag {
return (new CommunityTag( return (new CommunityTag(
"new", "new",
TagType::RESERVED_TAG, TagType::RESERVED_TAG,
))->set_description_globally("This Community was created recently."); ))->set_description_globally("This Community was created recently.");
} }
public static function used_by_project() { /**
* Return "used_by_project" reserved Community tag.
*
* @return CommunityTag
*/
public static function used_by_project(): CommunityTag {
return (new CommunityTag( return (new CommunityTag(
"we're here", "we're here",
TagType::RESERVED_TAG, TagType::RESERVED_TAG,
@ -350,14 +432,24 @@
. "or respond to feedback in this Community."); . "or respond to feedback in this Community.");
} }
public static function testing() { /**
* Return "testing" reserved Community tag.
*
* @return CommunityTag
*/
public static function testing(): CommunityTag {
return (new CommunityTag( return (new CommunityTag(
"test", "test",
TagType::RESERVED_TAG, TagType::RESERVED_TAG,
))->set_description_globally("This Community is intended for testing only."); ))->set_description_globally("This Community is intended for testing only.");
} }
public static function stickied() { /**
* Return "stickied" reserved Community tag.
*
* @return CommunityTag
*/
public static function stickied(): CommunityTag {
return (new CommunityTag( return (new CommunityTag(
"pinned", "pinned",
TagType::RESERVED_TAG, TagType::RESERVED_TAG,

@ -42,7 +42,8 @@
* Create a new FetchingCoroutine to fetch the contents of a URL. * Create a new FetchingCoroutine to fetch the contents of a URL.
* @param string $url URL to fetch. * @param string $url URL to fetch.
* @param array $curlopts Addition cURL options. * @param array $curlopts Addition cURL options.
* @return FetchingCoroutine<CurlHandle|false> Coroutine returning * @return FetchingCoroutine<CurlHandle|false>
* Coroutine returning 1) fulfilled cURL handle, or 2) false in case of failure.
*/ */
public static function from_url(string $url, array $curlopts = []): FetchingCoroutine { public static function from_url(string $url, array $curlopts = []): FetchingCoroutine {
/** /**
@ -234,7 +235,7 @@
/** /**
* Step coroutine with network result until next yield point. * Step coroutine with network result until next yield point.
* Coroutine must not have been consumed by any transformations. * Coroutine must not have been consumed by any transformations.
* @param CurlHandle|false $response * @param CurlHandle|false $response_handle
* cURL handle containing fetch result or false in case of failure. * cURL handle containing fetch result or false in case of failure.
* @return bool True if response was accepted by coroutine, false otherwise. * @return bool True if response was accepted by coroutine, false otherwise.
*/ */

@ -6,32 +6,61 @@
include_once 'utils/logging.php'; include_once 'utils/logging.php';
// Read the -v|--verbose option increasing logging verbosity to debug. // Read the -v|--verbose option increasing logging verbosity to debug.
/**
* @var array $options
* List of options parsed from the command-line.
*/
$options = getopt("vn", ["verbose", "fast", "no-color", "dry-run", "archive"]); $options = getopt("vn", ["verbose", "fast", "no-color", "dry-run", "archive"]);
if (isset($options["v"]) or isset($options["verbose"])) { if (isset($options["v"]) or isset($options["verbose"])) {
/**
* @var int $LOGGING_VERBOSITY
* Highest verbosity to display in logs.
*/
$LOGGING_VERBOSITY = LoggingVerbosity::Debug; $LOGGING_VERBOSITY = LoggingVerbosity::Debug;
} }
/**
* @var bool $FAST_FETCH_MODE
* If true, be less patient when polling servers.
*/
$FAST_FETCH_MODE = (isset($options["fast"])); $FAST_FETCH_MODE = (isset($options["fast"]));
/**
* @var bool $DO_DRY_RUN
* If true, do not overwrite fetched server data.
*/
$DO_DRY_RUN = (isset($options["n"]) || isset($options["dry-run"])); $DO_DRY_RUN = (isset($options["n"]) || isset($options["dry-run"]));
if (isset($options["no-color"])) { if (isset($options["no-color"])) {
LoggingVerbosity::$showColor = false; LoggingVerbosity::$showColor = false;
} }
/**
* @var bool $DO_ARCHIVE_FILES
* If true, archive fetched server data.
*/
$DO_ARCHIVE_FILES = isset($options["archive"]); $DO_ARCHIVE_FILES = isset($options["archive"]);
// set timeout for file_get_contents() // set timeout for file_get_contents()
ini_set('default_socket_timeout', 6); // in seconds, default is 60 ini_set('default_socket_timeout', 6); // in seconds, default is 60
// curl timeout in milliseconds /**
* @var int $CURL_CONNECT_TIMEOUT_MS
// max time for initiation of the connection * Maximum time to initiate connection.
*/
$CURL_CONNECT_TIMEOUT_MS = 2000; $CURL_CONNECT_TIMEOUT_MS = 2000;
// max time for each connection (incl. transfer) /**
* @var int $CURL_TIMEOUT_MS
* Maximum time for each connection, including transfer.
*/
$CURL_TIMEOUT_MS = $FAST_FETCH_MODE ? 3000 : 9000; $CURL_TIMEOUT_MS = $FAST_FETCH_MODE ? 3000 : 9000;
// delay between retries in milliseconds /**
* @var int $CURL_RETRY_SLEEP
* Delay between fetch retries in milliseconds.
*/
$CURL_RETRY_SLEEP = 2000; $CURL_RETRY_SLEEP = 2000;
?> ?>

@ -23,9 +23,21 @@
// Prevent class instantiation // Prevent class instantiation
private function __construct() {} private function __construct() {}
/**
* Error log verbosity constant.
*/
const Error = 10; const Error = 10;
/**
* Warning log verbosity constant.
*/
const Warning = 20; const Warning = 20;
/**
* Info log verbosity constant.
*/
const Info = 30; const Info = 30;
/**
* Debug log verbosity constant.
*/
const Debug = 40; const Debug = 40;
/** /**
@ -118,6 +130,9 @@
); );
} }
/**
* @private
*/
function _log_message(?string $msg, int $message_verbosity) { function _log_message(?string $msg, int $message_verbosity) {
global $LOGGING_VERBOSITY; global $LOGGING_VERBOSITY;
if ($message_verbosity > $LOGGING_VERBOSITY) return; if ($message_verbosity > $LOGGING_VERBOSITY) return;
@ -169,7 +184,8 @@
/** /**
* Logs the given value in a debug message to stderr. * Logs the given value in a debug message to stderr.
* Only logs when `$LOGGING_VERBOSITY` is debug and below. * Only logs when `$LOGGING_VERBOSITY` is debug and below.
* @param string $msg String message to log. * @param mixed $value Value to log.
* @param int $message_verbosity Verbosity to use when logging value. Default: Debug.
*/ */
function log_value(mixed $value, int $message_verbosity = LoggingVerbosity::Debug) { function log_value(mixed $value, int $message_verbosity = LoggingVerbosity::Debug) {
_log_message(var_export($value, true), $message_verbosity); _log_message(var_export($value, true), $message_verbosity);

@ -4,26 +4,68 @@
* Provides site generation context variables. * Provides site generation context variables.
*/ */
class SiteGeneration { class SiteGeneration {
public static function getCanonicalPageURL() { /**
* Get the absolute web path to the current document, omitting the final 'index.html'.
*
* @return string
*/
public static function getCanonicalPageURL(): string {
global $SITE_CANONICAL_URL; global $SITE_CANONICAL_URL;
return dirname($SITE_CANONICAL_URL.getenv('SSG_TARGET')) . '/'; return dirname($SITE_CANONICAL_URL.getenv('SSG_TARGET')) . '/';
} }
public static function getAbsoluteSourceDocumentPath() {
/**
* Get the absolute source path of the current document.
*
* @return string
*/
public static function getAbsoluteSourceDocumentPath(): string {
return $_SERVER['SCRIPT_NAME']; return $_SERVER['SCRIPT_NAME'];
} }
public static function getTargetDocumentPath() { /**
* Get the relative web path of the current document.
*
* @return string
*/
public static function getTargetDocumentPath(): string {
return getenv('SSG_TARGET'); return getenv('SSG_TARGET');
} }
public static function getTargetDocumentRoute() { /**
* Get the directory above the current document's web location.
*
* Returns the path to the directory in which the current document
* will be served, relative to the webroot.
*
* Usage:
* ```php
* // Generating /index.php
* SiteGeneration::getTargetDocumentRoute() // -> '/'
*
* // Generating /privacy/index.php
* SiteGeneration::getTargetDocumentRoute() // -> '/privacy'
* ```
*
* @return string Path to the directory serving the current document.
*/
public static function getTargetDocumentRoute(): string {
return dirname(SiteGeneration::getTargetDocumentPath()); return dirname(SiteGeneration::getTargetDocumentPath());
} }
public static function getOwnSubDocumentPath(string $identifier) { /**
* Return the path of a subdocument of the current document.
*
* When generating "index.php", this function will return
* "+index.head.php" when given a subdocument identifier of "head".;
*
* @param string $subdocument Subdocument identifier.
* @return string Absolute source path of subdocument "+current.subdocument.php"
*/
public static function getOwnSubDocumentPath(string $subdocument) {
$page = SiteGeneration::getAbsoluteSourceDocumentPath(); $page = SiteGeneration::getAbsoluteSourceDocumentPath();
$sub_document = dirname($page) . '/+' . preg_replace('/[.]php$/', ".$identifier.php", basename($page)); $sub_document = dirname($page) . '/+' . preg_replace('/[.]php$/', ".$subdocument.php", basename($page));
return $sub_document; return $sub_document;
} }
} }

@ -4,6 +4,10 @@
* Implement basic utility functions. * Implement basic utility functions.
*/ */
/**
* @var string $REGEX_JOIN_LINK
* Regular expression matching Session Community join links.
*/
$REGEX_JOIN_LINK = (function(){ $REGEX_JOIN_LINK = (function(){
// See https://github.com/oxen-io/session-pysogs/blob/dev/administration.md // See https://github.com/oxen-io/session-pysogs/blob/dev/administration.md
$protocol = 'https?:'; $protocol = 'https?:';
@ -47,7 +51,14 @@
return $truncated; return $truncated;
} }
function make_curl_handle(string $url, $curlopts = []) { /**
* Constructs a cURL handle for performing network requests.
*
* @param string $url Target resource to fetch.
* @param array $curlopts Associative array of cURL options. (optional)
* @return CurlHandle
*/
function make_curl_handle(string $url, $curlopts = []): CurlHandle {
global $CURL_CONNECT_TIMEOUT_MS, $CURL_TIMEOUT_MS; global $CURL_CONNECT_TIMEOUT_MS, $CURL_TIMEOUT_MS;
$curl = curl_init($url); $curl = curl_init($url);
@ -70,7 +81,8 @@
/** /**
* Downgrades a HTTPS-facing cURL handle to HTTP. * Downgrades a HTTPS-facing cURL handle to HTTP.
* @return CurlHandle|null Handle copy if can downgrade, or null if not applicable. * @param CurlHandle $handle cURL handle.
* @return CurlHandle|null Handle copy, or null if not applicable.
*/ */
function curl_handle_downgrade(CurlHandle $handle): CurlHandle|null { function curl_handle_downgrade(CurlHandle $handle): CurlHandle|null {
$url = curl_getinfo($handle, CURLINFO_EFFECTIVE_URL); $url = curl_getinfo($handle, CURLINFO_EFFECTIVE_URL);
@ -87,7 +99,7 @@
* @param string $url The URL to slice the path from. * @param string $url The URL to slice the path from.
* @param bool $include_scheme [optional] * @param bool $include_scheme [optional]
* Includes the scheme. `true` by default. * Includes the scheme. `true` by default.
* @param bool $include_scheme [optional] * @param bool $include_port [optional]
* Includes the port. `true` by default. * Includes the port. `true` by default.
* @return string A URL composed of the original scheme (unless specified), * @return string A URL composed of the original scheme (unless specified),
* hostname, and port (if present). * hostname, and port (if present).
@ -142,10 +154,10 @@
/** /**
* Extracts join links that match $REGEX_JOIN_LINK. * Extracts join links that match $REGEX_JOIN_LINK.
* @param ?string $html Text to find join URLs in. * @param string $html Text to find join URLs in.
* @return string[] Sorted array of unique server join links. * @return string[] Sorted array of unique server join links.
*/ */
function parse_join_links(?string $html){ function parse_join_links(?string $html): array {
global $REGEX_JOIN_LINK; global $REGEX_JOIN_LINK;
preg_match_all($REGEX_JOIN_LINK, $html, $match_result); preg_match_all($REGEX_JOIN_LINK, $html, $match_result);
$links = $match_result[0]; $links = $match_result[0];
@ -159,8 +171,9 @@
* @param string $str String to sanitize * @param string $str String to sanitize
* @param int $flags [optional] * @param int $flags [optional]
* A bitmask of one or more of the following flags, * A bitmask of one or more of the following flags,
* which specify how to handle quotes, invalid code unit sequences * which specify how to handle quotes, invalid code unit sequences
* and the used document type. The default is ENT_COMPAT | ENT_HTML401. * and the used document type. The default is ENT_COMPAT | ENT_HTML401.
* @param string $encoding Character encoding used. [optional]
* @param bool $double_encode [optional] * @param bool $double_encode [optional]
* When double_encode is turned off, PHP will not encode * When double_encode is turned off, PHP will not encode
* existing html entities, the default is to convert everything. * existing html entities, the default is to convert everything.
@ -176,6 +189,13 @@
return htmlspecialchars($str, $flags, $encoding, $double_encode); return htmlspecialchars($str, $flags, $encoding, $double_encode);
} }
/**
* Return the sign of the given number.
*
* @param float $num Floating-point number.
*
* @return int -1 if negative, 1 if positive, 0 otherwise
*/
function sign(float $num): int { function sign(float $num): int {
return ($num > 0) - ($num < 0); return ($num > 0) - ($num < 0);
} }

@ -11,6 +11,10 @@
* @var CommunityRoom[] $rooms * @var CommunityRoom[] $rooms
*/ */
/**
* @var array $json_ld_data
* Associative data about the site in JSON-LD format.
*/
$json_ld_data = array( $json_ld_data = array(
'@context' => 'https://schema.org/', '@context' => 'https://schema.org/',
'@id' => $SITE_CANONICAL_URL, '@id' => $SITE_CANONICAL_URL,

@ -11,7 +11,17 @@
// Set the last-updated timestamp // Set the last-updated timestamp
// to the time the server data file was last modified. // to the time the server data file was last modified.
/**
* @var int $time_modified
* Timestamp of last Community data fetch.
*/
$time_modified = filemtime($ROOMS_FILE); $time_modified = filemtime($ROOMS_FILE);
/**
* @var string $time_modified_str
* Timestamp of last Community data fetch.
*/
$time_modified_str = date("Y-m-d H:i:s", $time_modified); $time_modified_str = date("Y-m-d H:i:s", $time_modified);
?> ?>

@ -10,14 +10,23 @@
/** /**
* Filters Session Communities. * Filters Session Communities.
*
* RoomSieve methods return copies when mutating the object.
* The stickied room list is preserved after filtering.
*/ */
class RoomSieve { class RoomSieve {
/** /**
* Communities stored.
*
* @var CommunityRoom[] $rooms; * @var CommunityRoom[] $rooms;
*/ */
private array $rooms; private array $rooms;
/** /**
* Pinned Communities, set aside.
*
* Not affected by filters.
*
* @var CommunityRoom[] $stickied * @var CommunityRoom[] $stickied
*/ */
private array $stickies; private array $stickies;
@ -31,53 +40,104 @@
$this->stickies = $stickied; $this->stickies = $stickied;
} }
/**
* Default limit for number of Communities.
*
* Applies only to certain functions.
*/
public const TOP_DEFAULT = 35; public const TOP_DEFAULT = 35;
/** /**
* Create new RoomSieve from the given Communities.
*
* @param CommunityRoom[] $rooms * @param CommunityRoom[] $rooms
*
* @return RoomSieve
*/ */
public static function takeRooms(array $rooms) { public static function takeRooms(array $rooms): RoomSieve {
return new RoomSieve($rooms); return new RoomSieve($rooms);
} }
public function saveStickies() { /**
* Set aside pinned Communities from the main list.
*
* @return RoomSieve
*/
public function saveStickies(): self {
$stickied = CommunityRoom::get_stickied_rooms($this->rooms, $rest); $stickied = CommunityRoom::get_stickied_rooms($this->rooms, $rest);
$rooms = $rest; $rooms = $rest;
return $this->cloneWith($rooms, $stickied); return $this->cloneWith($rooms, $stickied);
} }
/** /**
* Add the given Communities to a new RoomSieve.
*
* @param CommunityRoom[] $rooms * @param CommunityRoom[] $rooms
*
* @return RoomSieve
*/ */
public function addRooms(array $rooms) { public function addRooms(array $rooms): RoomSieve {
return $this->cloneWith(array_merge($this->rooms, $rooms)); return $this->cloneWith(array_merge($this->rooms, $rooms));
} }
public function apply(Closure $filter) { /**
* Use a custom filter for Communities.
*
* Creates a new RoomSieve with all Communities that passed the filter.
*
* @param Closure $filter Function which takes an array of Communities and returns an array of Communities.
*
* @return RoomSieve
*/
public function apply(Closure $filter): RoomSieve {
return $this->cloneWith($filter($this->rooms)); return $this->cloneWith($filter($this->rooms));
} }
public function getWithStickies() { /**
* Return all stored Communities, including pinned Communities.
*
* @return CommunityRoom[]
*/
public function getWithStickies(): array {
return [...$this->stickies, ...$this->rooms]; return [...$this->stickies, ...$this->rooms];
} }
public function getWithoutStickies() { /**
return $this->saveStickies()->getRooms(); * Return stored Communities without pinned Communities.
} *
* @return CommunityRoom[]
public function getRooms() { */
return $this->rooms; public function getWithoutStickies(): array {
return $this->saveStickies()->rooms;
} }
public function onlyTop(int $count = RoomSieve::TOP_DEFAULT) { /**
* Only keep the top N active Communities.
*
* Does not affect stickied Communities.
*
* @param int $count Number of top Communities to keep. (optional)
*
* @return RoomSieve
*/
public function onlyTop(int $count = RoomSieve::TOP_DEFAULT): RoomSieve {
$rooms = $this->rooms; $rooms = $this->rooms;
CommunityRoom::sort_rooms_num($rooms, 'active_users', reverse: true); CommunityRoom::sort_rooms_num($rooms, 'active_users', descending: true);
return $this->cloneWith(array_slice($rooms, 0, $count)); return $this->cloneWith(array_slice($rooms, 0, $count));
} }
public function exceptTop(int $count = RoomSieve::TOP_DEFAULT) { /**
* Remove the top N active Communities.
*
* Does not affect stickied Communities.
*
* @param int $count Number of top Communities to remove. (optional)
*
* @return RoomSieve
*/
public function exceptTop(int $count = RoomSieve::TOP_DEFAULT): RoomSieve {
$rooms = $this->rooms; $rooms = $this->rooms;
CommunityRoom::sort_rooms_num($rooms, 'active_users', reverse: true); CommunityRoom::sort_rooms_num($rooms, 'active_users', descending: true);
return $this->cloneWith(array_slice($rooms, $count)); return $this->cloneWith(array_slice($rooms, $count));
} }
@ -90,14 +150,26 @@
); );
} }
public function applyStandardSort() { /**
* Sort Communities by name and server.
*
* @return RoomSieve
*/
public function applyStandardSort(): RoomSieve {
$rooms = $this->rooms; $rooms = $this->rooms;
CommunityRoom::sort_rooms_str($rooms, 'name'); CommunityRoom::sort_rooms_str($rooms, 'name');
CommunityRoom::sort_rooms_by_server($rooms); CommunityRoom::sort_rooms_by_server($rooms);
return new RoomSieve($rooms, $this->stickies); return new RoomSieve($rooms, $this->stickies);
} }
public function applyPreferentialSort() { /**
* Sort Communities by staff rating.
*
* Communities with a description are also preferred.
*
* @return RoomSieve
*/
public function applyPreferentialSort(): RoomSieve {
$rooms = $this->rooms; $rooms = $this->rooms;
CommunityRoom::sort_rooms_num($rooms,'created'); CommunityRoom::sort_rooms_num($rooms,'created');
usort($rooms, function($a, $b) { usort($rooms, function($a, $b) {
@ -109,14 +181,24 @@
return new RoomSieve(array_reverse($rooms), $this->stickies); return new RoomSieve(array_reverse($rooms), $this->stickies);
} }
public function indexApproved() { /**
* Keep only Communities heuristically deemed to be appropriate.
*
* @return RoomSieve
*/
public function indexApproved(): RoomSieve {
$rooms = array_values(array_filter($this->rooms, function($room) { $rooms = array_values(array_filter($this->rooms, function($room) {
return RoomSieve::isIndexApproved($room); return RoomSieve::isIndexApproved($room);
})); }));
return new RoomSieve($rooms, $this->stickies); return new RoomSieve($rooms, $this->stickies);
} }
public function indexNonApproved() { /**
* Remove Communities heuristically deemed to be appropriate.
*
* @return RoomSieve
*/
public function indexNonApproved(): RoomSieve {
$rooms = array_values(array_filter($this->rooms, function($room) { $rooms = array_values(array_filter($this->rooms, function($room) {
return !RoomSieve::isIndexApproved($room); return !RoomSieve::isIndexApproved($room);
})); }));

@ -9,7 +9,16 @@
require_once 'php/servers/room-database.php'; require_once 'php/servers/room-database.php';
require_once 'sites/_fragment/+room-sieve.php'; require_once 'sites/_fragment/+room-sieve.php';
/**
* @var CommunityDatabase $room_database
* Database of fetched servers and Communities.
*/
$room_database = CommunityDatabase::read_from_file($ROOMS_FILE)->fetch_assets(); $room_database = CommunityDatabase::read_from_file($ROOMS_FILE)->fetch_assets();
/**
* @var CommunityRoom[] $rooms
* Communities shown on page by default.
*/
$rooms = $rooms =
RoomSieve::takeRooms($room_database->rooms) RoomSieve::takeRooms($room_database->rooms)
->saveStickies() ->saveStickies()

@ -6,18 +6,47 @@
require_once '+getenv.php'; require_once '+getenv.php';
/**
* @var string[] $instruction_files
* List of all files containing localized site instructions.
*/
$instruction_files = glob("+instructions/*.txt"); $instruction_files = glob("+instructions/*.txt");
/**
* Get the language name of the given localized file.
*
* @param string $file File containing site instructions.
*
* @return string
*/
function file_language($file) { function file_language($file) {
$filename = pathinfo($file)['filename']; $filename = pathinfo($file)['filename'];
return explode(" ", $filename)[0]; return explode(" ", $filename)[0];
} }
/**
* Get the language code of the given localized file.
*
* @param string $file File containing site instructions.
*
* @return string
*/
function file_language_code($file) { function file_language_code($file) {
$filename = pathinfo($file)['filename']; $filename = pathinfo($file)['filename'];
$code_in_brackets = explode(" ", $filename)[1]; $code_in_brackets = explode(" ", $filename)[1];
return mb_substr($code_in_brackets, 1, mb_strlen($code_in_brackets) - 2); return mb_substr($code_in_brackets, 1, mb_strlen($code_in_brackets) - 2);
} }
/**
* @var string $languages
* List of languages in which instructions are available.
*/
$languages = array_map('file_language', array_slice($instruction_files, 0, 10)); $languages = array_map('file_language', array_slice($instruction_files, 0, 10));
/**
* @var string $language_enumeration
* Enumerated list of languages with available instructions.
*/
$language_enumeration = join(", ", $languages); $language_enumeration = join(", ", $languages);
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@ -71,6 +100,10 @@
// Sanitization as second layer of protection // Sanitization as second layer of protection
// for user-submitted instruction files. // for user-submitted instruction files.
// Should not ever have to be used. // Should not ever have to be used.
/**
* @var string $content
* Instructions file contents.
*/
$content = trim(file_get_contents($file)); $content = trim(file_get_contents($file));
$content = htmlentities($content); $content = htmlentities($content);
// Minimal formatting so that contributions are easier // Minimal formatting so that contributions are easier

@ -8,6 +8,10 @@
require_once '+getenv.php'; require_once '+getenv.php';
/**
* @var string[] $HIGHLIGHTED_FIELDS
* List of interactive server log entries.
*/
$HIGHLIGHTED_FIELDS = ["ip", "datetime", "resource", "status", "bytes", "referer", "user-agent"]; $HIGHLIGHTED_FIELDS = ["ip", "datetime", "resource", "status", "bytes", "referer", "user-agent"];
?> ?>
<!DOCTYPE html> <!DOCTYPE html>

@ -6,8 +6,20 @@
*/ */
require_once '+getenv.php'; require_once '+getenv.php';
/**
* Generate sitemap fragment containing page location and last modified time.
*
* Only works for pages named "index.php".
*
* @param string $rel_loc Canonical webpage location relative to webroot.
* @param string $changes_under_root The directory to check (source or output) to infer file modification time.
* Typically {@link $DOCUMENT_ROOT} to detect new versions of files with updating content
* and {@link $TEMPLATES_ROOT} for articles which only substantially change on source change.
*
* @return void
*/
function loc_lastmod(string $rel_loc, ?string $changes_under_root = null) { function loc_lastmod(string $rel_loc, ?string $changes_under_root = null) {
global $SITE_CANONICAL_URL, $DOCUMENT_ROOT, $TEMPLATES_ROOT; global $SITE_CANONICAL_URL, $TEMPLATES_ROOT;
$root = $changes_under_root ?? $TEMPLATES_ROOT; $root = $changes_under_root ?? $TEMPLATES_ROOT;
$ext = ($root == $TEMPLATES_ROOT) ? "php" : "html"; $ext = ($root == $TEMPLATES_ROOT) ? "php" : "html";
?> ?>

@ -6,11 +6,31 @@
require_once '+getenv.php'; require_once '+getenv.php';
/**
* Number of background particles.
* @var int $NUM_PARTICLES
*/
$NUM_PARTICLES = 20; $NUM_PARTICLES = 20;
/**
* @var int[] $DELAYS
* Time delays for background particles to appear.
*/
$DELAYS = range(0, 240 - 1, 240 / $NUM_PARTICLES); $DELAYS = range(0, 240 - 1, 240 / $NUM_PARTICLES);
shuffle($DELAYS); shuffle($DELAYS);
/**
* @var string[] $PARTICLES
* Array of available background particles.
*/
$PARTICLES = explode(" ", "$ 💵 💰 💸 💯"); $PARTICLES = explode(" ", "$ 💵 💰 💸 💯");
function random_particle() {
/**
* Pick random particle.
*
* @return string
*/
function random_particle(): string {
global $PARTICLES; global $PARTICLES;
$r = rand(0, count($PARTICLES) - 1); $r = rand(0, count($PARTICLES) - 1);
return $PARTICLES[$r]; return $PARTICLES[$r];

Loading…
Cancel
Save