You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sessioncommunities.online/php/servers/tags.php

368 lines
8.6 KiB
PHP

<?php
/**
* \file
* Implement maintainer-given and derived tags for Community rooms.
*/
require_once 'utils/utils.php';
/**
* Class enumerating types of Community tags.
*/
class TagType {
private function __construct() {}
/**
* Specifies custom tag added by Community maintainer or Community source.
*/
const USER_TAG = 'user';
/**
* Specifies basic type of tag reserved for assignment by our aggregator.
*/
const RESERVED_TAG = 'reserved';
/**
* Specifies warning tag reserved for assignment by our aggregator.
*/
const WARNING_TAG = 'warning';
}
/**
* Represents a manual or derived Community tag.
*/
class CommunityTag implements JsonSerializable {
/**
* Create a new CommunityTag instance.
* @param string $text Text the tag should read.
* @param string $tag_type {@link TagType} enumeration value.
* @param string|null $description [optional] Brief explanation of tag.
*/
public function __construct(
string $text,
string $tag_type = TagType::USER_TAG,
?string $description = ""
) {
$this->text = CommunityTag::preprocess_tag($text);
$this->type = $tag_type;
$this->description = $description;
}
/**
* @var string $type
* Tag type as given by a {@link TagType} value.
*/
public readonly string $type;
/**
* @var string $text
* The string tag itself.
*/
public readonly string $text;
/**
* @var string $description
* A text description of the tag.
*/
public readonly string $description;
/**
* Return a lowercase representation of the tag for purposes of de-duping.
*/
public function __toString(): string {
return strtolower($this->text);
}
/**
* Return a lowercase representation of the tag for use in display.
*/
public function get_text(): string {
return strtolower($this->text);
}
/**
* Return a lowercase text representation of the tag for use in HTML.
*/
public function get_text_sanitized(): string {
return html_sanitize($this->get_text());
}
/**
* Return tag description.
*/
public function get_description_sanitized(): string {
return html_sanitize($this->description ?? "Tag: $this->text");
}
/**
* Produce data used to serialize the tag.
*/
public function jsonSerialize(): mixed {
$details = [];
$details['text'] = $this->get_text();
$details['type'] = $this->get_tag_type();
if (!empty($this->description)) {
$details['description'] = $this->description;
}
return $details;
}
private static function preprocess_tag(?string $tag) {
$tag = trim($tag);
if (strlen($tag) == 0) {
return $tag;
}
$tag = html_entity_decode($tag);
if ($tag[0] == '#') {
return substr($tag, 1);
}
return strtolower($tag);
}
/**
* @param string[] $tag_array
* @return CommunityTag[]
*/
private static function from_string_tags(array $tag_array) {
$tags = array_filter(
$tag_array, function(?string $tag) {
return strlen($tag) != 0;
}
);
$tags = CommunityTag::dedupe_tags($tags);
return array_map(function(string $tag) {
return new CommunityTag($tag);
}, $tags);
}
/**
* Constructs the tags given, removing any reserved tags.
* @param string[] $tags
* @param bool $remove_redundant Removes meaningless tags.
* @return CommunityTag[]
*/
public static function from_user_tags(
array $tags,
bool $remove_redundant = true
): array {
$tags_user = array_values(array_filter(
$tags,
function($tag) {
return !CommunityTag::is_reserved_tag($tag);
}
));
$tags_built = CommunityTag::from_string_tags($tags_user);
if ($remove_redundant) {
$tags_built = array_values(
array_filter($tags_built, function(CommunityTag $tag) {
return !in_array($tag->get_text(), CommunityTag::REDUNDANT_TAGS);
})
);
}
return $tags_built;
}
/**
* @param array $details Deserialized tag info.
* @return CommunityTag
*/
public static function from_details(array $details): CommunityTag {
return new CommunityTag(
$details['text'],
$details['type'],
$details['description'] ?? ''
);
}
/**
* @param array[] $details_array Array of deserialized tag info.
* @return CommunityTag[]
*/
public static function from_details_array(array $details_array): array {
return array_map(function($details) {
return CommunityTag::from_details($details);
}, $details_array);
}
/**
* @param CommunityTag[] $tags
* @return CommunityTag[]
*/
public static function dedupe_tags(array $tags) {
return array_values(array_unique($tags));
}
/**
* Return a HTML classname corresponding to the tag.
*/
public function get_tag_classname(): string {
$tag_type = $this->get_tag_type();
$classname = "room-label-$tag_type";
if (CommunityTag::is_showcased_tag($this->text)) {
$classname .= " room-label-showcased";
}
if (CommunityTag::is_highlighted_tag($this->text)) {
$classname .= " room-label-highlighted";
}
return $classname;
}
/**
* Return a string representation of the tag's {@link TagType}.
* @return "user", "reserved", or "warning", as appropriate.
*/
public function get_tag_type(): string {
return match($this->type) {
TagType::USER_TAG => 'user',
TagType::RESERVED_TAG => 'reserved',
TagType::WARNING_TAG => 'warning'
};
}
/**
* @var string[] RESERVED_TAGS
* Array of derived tags unavailable for manual tagging.
*/
private const RESERVED_TAGS = [
"official",
"nsfw",
"new",
"modded",
"moderated",
"not modded",
"not moderated",
"read-only",
"uploads off",
"we're here",
"test",
"pinned"
];
private const SHOWCASED_TAGS = ["official", "new", "we're here", "nsfw", "read-only", "pinned"];
private const HIGHLIGHTED_TAGS = ["new", "we're here", "pinned"];
private const REDUNDANT_TAGS = [];
public const NSFW_KEYWORDS = ["nsfw", "porn", "erotic", "18+", "sex"];
/**
* Check whether the given user tag is reserved by our aggregator.
*/
public static function is_reserved_tag(string $tag): bool {
return in_array(strtolower($tag), CommunityTag::RESERVED_TAGS);
}
/**
* Return true if the tag should be given a chance to appear in more crowded views.
*/
public static function is_showcased_tag(string $tag): bool {
return in_array(strtolower($tag), CommunityTag::SHOWCASED_TAGS);
}
/**
* Return true if the tag should be given visibility in more crowded views.
*/
public static function is_highlighted_tag(string $tag): bool {
return in_array(strtolower($tag), CommunityTag::HIGHLIGHTED_TAGS);
}
}
class ReservedTags {
public static function official() {
$CHECK_MARK = "";
return new CommunityTag(
"official",
TagType::RESERVED_TAG,
"This Community is maintained by the Session team. $CHECK_MARK"
);
}
public static function nsfw() {
$WARNING_ICON = "⚠️";
return new CommunityTag(
"nsfw",
TagType::WARNING_TAG,
"This Community may contain adult material. $WARNING_ICON"
);
}
public static function moderated(int $users_per_staff = 0) {
$CHECK_MARK = "";
return new CommunityTag(
"moderated",
TagType::RESERVED_TAG,
"This Community has at least 1 staff per $users_per_staff active users. $CHECK_MARK"
);
}
public static function not_modded(int $users_per_staff = 0) {
$WARNING_ICON = "⚠️";
return new CommunityTag(
"not modded",
TagType::WARNING_TAG,
"This Community has less than 1 staff per $users_per_staff active users. $WARNING_ICON"
);
}
public static function read_only() {
return new CommunityTag(
"read-only",
TagType::RESERVED_TAG,
"This Community is read-only."
);
}
public static function no_upload_permission() {
return new CommunityTag(
"uploads off",
TagType::RESERVED_TAG,
"This Community does not support uploading files or link previews."
);
}
public static function recently_created() {
return new CommunityTag(
"new",
TagType::RESERVED_TAG,
"This Community was created recently."
);
}
public static function used_by_project() {
return new CommunityTag(
"we're here",
TagType::RESERVED_TAG,
"The sessioncommunities.online maintainer(s) can post updates "
. "or respond to feedback in this Community."
);
}
public static function testing() {
return new CommunityTag(
"test",
TagType::RESERVED_TAG,
"This Community is intended for testing only."
);
}
public static function stickied() {
return new CommunityTag(
"pinned",
TagType::RESERVED_TAG,
"This Community has been pinned for greater visibility. 📌"
);
}
}
?>